The wrox.com
code downloads for this chapter are found at www.wrox.com/go/beginningvisualc#2015programming
on the Download Code tab. The code is in the Chapter 22 download and individually named according to the names throughout the chapter.
In recent years, as use of the Internet has become more ubiquitous, there has been a rapid increase in web services. A web service is like a website that is used by a computer instead of a person. For example, instead of browsing to a website about your favorite TV program, you might instead use a desktop application that pulled in the same information via a web service. The advantage here is that the same web service might be used by all sorts of applications, and, indeed, by websites. Also, you can write your own application or website that uses third-party web services. Perhaps you might combine information about your favorite TV program with a mapping service to show filming locations.
The .NET Framework has supported web services for some time now. However, in the more recent versions of the framework, web services have been combined with another technology, called remoting, to create Windows Communication Foundation (WCF), which is a generic infrastructure for communication between applications.
Remoting makes it possible to create instances of objects in one process and use them from another process — even if the object is created on a computer other than the one that is using it. However, remoting on its own is limited, and isn't the easiest thing for a beginner programmer to learn.
WCF takes concepts such as services and platform-independent SOAP messaging from web services, and combines these with concepts such as host server applications and advanced binding capabilities from remoting. The result is a technology you can think of as a superset that includes both web services and remoting, but that is much more powerful than web services and much easier to use than remoting. Using WCF, you can move from simple applications to applications that use a service-oriented architecture (SOA). SOA means that you decentralize processing and make use of distributed processing by connecting to services and data as you need them across local networks and the Internet.
This chapter walks you through how to create and consume WCF services from your application code. But just as importantly, it also covers the principles behind WCF, so you understand why things work the way they do.
WCF is a technology that enables you to create services that you can access from other applications across process, machine, and network boundaries. You can use these services to share functionality across multiple applications, to expose data sources, or to abstract complicated processes.
The functionality that WCF services offer is encapsulated as individual methods that are exposed by the service. Each method — or, in WCF terminology, each operation — has an endpoint that you exchange data with in order to use it. This data exchange can be defined by one or more protocols, depending on the network that you use to connect to the service and your specific requirements.
In WCF, an endpoint can have multiple bindings, each of which specifies a means of communication. Bindings can also specify additional information, such as which security requirements must be met to communicate with the endpoint. A binding might require username and password authentication or a Windows user account token, for example. When you connect to an endpoint, the protocol that the binding uses affects the address that you use, as you will see shortly.
Once you have connected to an endpoint, you can communicate with it by using Simple Object Access Protocol (SOAP) messages. The form of the messages that you use depends on the operation you are using and the data structures that are required to send messages to (and receive messages from) that operation. WCF uses contracts to specify all of this. You can discover contracts through metadata exchange with a service. One commonly used format for service discovery is the Web Service Description Language (WSDL), which was originally used for web services, although WCF services can also be described in other ways.
When you have identified a service and endpoint that you want to use, and after you know which binding you use and which contracts to adhere to, you can communicate with a WCF service as easily as with an object that you have defined locally. Communications with WCF services can be simple, one-way transactions, request/response messages, or full-duplex communications that can be initiated from either end of the communication channel. You can also use message payload optimization techniques, such as Message Transmission Optimization Mechanism (MTOM), to package data if required.
The WCF service itself might be running in one of a number of different processes on the computer where it is hosted. Unlike web services, which always run in IIS, you can choose a host process that is appropriate to your situation. You can use IIS to host WCF services, but you can also use Windows services or executables. If you are using TCP to communicate with a WCF service over a local network, there is no need even to have IIS installed on the PC that is hosting the service.
The WCF framework has been designed to enable you to customize nearly everything you have read about in this section. However, this is an advanced subject and you will only be using the techniques provided by default in .NET 4.5 in this chapter.
Now that you have covered the basics about WCF services, you will look in more detail at these concepts in the following sections.
This section describes the following aspects of WCF:
As described earlier, you can communicate with WCF services through a variety of transport protocols. In fact, five are defined in the .NET 4.5 Framework:
These protocols often enable you to establish secure connections. For example, you can use the HTTPS protocol to establish an SSL connection across the Internet. TCP offers extensive possibilities for security in a local network by using the Windows security framework. UDP doesn't support security.
In order to connect to a WCF service, you must know where it is. In practice, this means knowing the address of an endpoint.
The type of address you use for a service depends on the protocol that you are using. Service addresses are formatted for the three protocols described in this chapter (MSMQ is not covered) as follows:
<service>
will be a file with a .svc
extension. IIS addresses will probably include more subdirectories than this example — that is, more sections separated by /
characters before the .svc
file.net.tcp://<server>:<port>/<service>
.soap.udp://<server>:<port>/<service>
. Certain <server>
values are required for multicast communications, but this is beyond the scope of this chapter.net.pipe://<server>/<service>
.The address for a service is a base address that you can use to create addresses for endpoints representing operations. For example, you might have an operation at net.tcp://<server>:<port>/<service>/operation1
.
For example, imagine you create a WCF service with a single operation that has bindings for all three of the protocols listed here. You might use the following base addresses:
http://www.mydomain.com/services/amazingservices/mygreatservice.svc
net.tcp://myhugeserver:8080/mygreatservice
net.pipe://localhost/mygreatservice
You could then use the following addresses for operations:
http://www.mydomain.com/services/amazingservices/mygreatservice.svc/greatop
net.tcp://myhugeserver:8080/mygreatservice/greatop
net.pipe://localhost/mygreatservice/greatop
Since .NET 4, it has been possible to use default endpoints for operations, without having to explicitly configure them. This simplifies configuration, especially in situations where you want to use standard endpoint addresses, as in the preceding examples.
Bindings, as mentioned earlier, specify more than just the transport protocol that will be used by an operation. You can also use them to specify the security requirements for communication over the transport protocol, transactional capabilities of the endpoint, message encoding, and much more.
Because bindings offer such a great degree of flexibility, the .NET Framework provides some predefined bindings that you can use. You can also use these bindings as starting points, tweaking them to obtain exactly the type of binding you want — up to a point. The predefined bindings have certain capabilities to which you must adhere. Each binding type is represented by a class in the
namespace. Table 22.1 lists the most commonly used bindings along with some basic information about them.System.ServiceModel
Table 22.1 Binding Types
Binding | Description |
|
The simplest HTTP binding, and the default binding used by web services. It has limited security capabilities and no transactional support. |
|
A more advanced form of HTTP binding that is capable of using all the additional functionality that was introduced in WSE. |
|
Extends capabilities to include duplex communication capabilities. With duplex communication, the server can initiate communications with the client in addition to ordinary message exchange. |
|
Extends capabilities to include federation capabilities. Federation enables third parties to implement single sign-on and other proprietary security measures. This is an advanced topic not covered in this chapter. |
|
Used for TCP communications, and enables you to configure security, transactions, and so on. |
|
Used for named pipe communications, and enables you to configure security, transactions, and so on. |
|
Used with MSMQ, which is not covered in this chapter. |
|
Used for peer-to-peer binding, which is not covered in this chapter. |
|
Used for web services that use HTTP requests instead of SOAP messages. |
|
Allows binding to the UDP protocol. |
Many of the binding classes have similar properties that you can use for additional configuration. For example, they have properties that you can use to configure timeout values. You'll learn more about this when you look at code later in this chapter.
Since .NET 4, endpoints have default bindings that vary according to the protocol used. These defaults are shown in Table 22.2.
Table 22.2 NET Default Bindings
Protocol | Default Binding |
HTTP |
|
TCP |
|
UDP |
|
Named pipe |
|
MSMQ |
|
Contracts define how WCF services can be used. Several types of contract can be defined:
You typically add contracts to service classes and methods by using attributes, as you will see later in this chapter.
In the previous section, you saw that an operation contract can define whether an operation returns a value. You've also read about duplex communications that are made possible by the
binding. These are both forms of message patterns, of which there are three types:WSDualHttpBinding
You'll see how these message patterns are used in practice later in this chapter.
Behaviors are a way to apply additional configuration that is not directly exposed to a client to services and operations. By adding a behavior to a service, you can control how it is instantiated and used by its hosting process, how it participates in transactions, how multithreading issues are dealt with in the service, and so on. Operation behaviors can control whether impersonation is used in the operation execution, how the individual operation affects transactions, and more.
Since .NET 4, you can specify default behaviors at various levels, so that you don't have to specify every aspect of every behavior for every service and operation. Instead, you can provide defaults and override settings where necessary, which reduces the amount of configuration required.
In the introduction to this chapter, you learned that WCF services can be hosted in several different processes. These possibilities are as follows:
Two of the options in the preceding list — IIS and WAS — provide useful features for WCF services such as activation, process recycling, and object pooling. If you use either of the other two hosting options, the WCF service is said to be self-hosted. You will occasionally self-host services for testing purposes, but there can be very good reasons for creating self-hosted production-grade services. For example, you could be in a situation where you're not allowed to install a web server on the computer on which your service should run. This might be the case if the service runs on a domain controller or if the local policy of your organization simply prohibits running IIS. In this case you can host the service in a Windows service and it will work every bit as well as it would otherwise.
Now that you have covered all the basics, it is time to get started with some code. In this section you'll start by looking as a simple web server–hosted WCF service and a console application client. After looking at the structure of the code created, you'll learn about the basic structure of WCF services and client applications. Then you will look at some key topics in a bit more detail:
In the previous Try It Out, you created both a service and a client in order to look at how the basic WCF architecture works and how configuration of WCF services is achieved. In practice, though, the client application you want to use might be complex, and it can be tricky to test services properly.
To ease the development of WCF services, Visual Studio provides a test tool you can use to ensure that your WCF operations work correctly. This tool is automatically configured to work with your WCF service projects, so if you run your project the tool will appear. All you need to do is ensure that the service you want to test (that is, the
file) is set to be the startup page for the WCF service project. Alternatively, you can run the test client as a standalone application. You can find the test client on 64-bit operating systems at .svc
C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\WcfTestClient.exe
.
If you are using a 32-bit operating system, the path is the same except the root folder is
.Program
Files
The tool enables you to invoke service operations and inspect the service in some other ways. The following Try It Out illustrates this.
Service1.svc
service in Solution Explorer and click Set As Start Page.Web.config
, ensure that metadata is enabled:<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
GetDataUsingDataContract()
operation.BoolValue
to True
and StringValue
to Test
String
, and then click Invoke.In this example you used the WCF test client to inspect and invoke an operation on the service you created in the previous Try It Out. The first thing you probably noticed is a slight delay while the service is loaded. This is because the test client has to inspect the service to determine its capabilities. This discovery uses the same metadata as the Add Service Reference tool, which is why you must ensure that metadata is available (it's possible you experimented with disabling it in the previous Try It Out). Once discovery is complete, you can view the service and its operations in the left pane of the tool.
Next, you looked at the configuration used to access the service. As with the client application from the previous Try It Out, this is generated automatically from the service metadata, and contains exactly the same code. You can edit this configuration file through the tool if you need to, by right-clicking on the Config File item and clicking Edit WCF Configuration. An example of this configuration is shown in Figure 22.3, which includes the binding configuration options mentioned earlier in this chapter.
Finally, you invoked an operation. The test client allows you to enter the parameters to use and invoke the method, then displays the result, all without you writing any client code. You also saw how to view the actual XML that is sent and received to obtain the result. This information is quite technical, but it can be absolutely critical when debugging more complex services.
The previous examples showed how the WCF infrastructure makes it easy for you to define contracts for WCF services with a combination of classes, interfaces, and attributes. This section takes a deeper look at this technique.
To define a data contract for a service, you apply the
attribute to a class definition. This attribute is found in the DataContractAttribute
namespace. You can configure this attribute with the properties shown in Table 22.3.System.Runtime.Serialization
Table 22.3 DataContractAttribute Properties
Property | Description |
|
Names the data contract with a different name than the one you use for the class definition. This name will be used in SOAP messages and client-side data objects that are defined from service metadata. |
|
Defines the namespace that the data contract uses in SOAP messages. |
|
Affects the way that objects are serialized. If this is set to , then an object instance is serialized only once even if it is referenced several times, which can be important is some situations. The default is . |
The
and Name
properties are useful when you need interoperability with existing SOAP message formats (as are the similarly named properties for other contracts), but otherwise you will probably not require them.Namespace
Each class member that is part of a data contract must use the
attribute, which is also found in the DataMemberAttribute
namespace. Table 22.4 lists this attribute's properties.System.Runtime.Serialization
Table 22.4 DataMemberAttribute Properties
Property | Description |
|
Specifies the name of the data member when serialized (the default is the member name). |
|
Specifies whether the member must be present in a SOAP message. |
|
An value specifying the order of serializing or deserializing the member, which might be required if one member must be present before another can be understood. Lower members are processed first. |
|
Set this to to prevent members from being included in SOAP messages if their value is the default value for the member. |
Service contracts are defined by applying the
attribute to an interface definition. You can customize the service contract with the properties shown in Table 22.5.System.ServiceModel.ServiceContractAttribute
Table 22.5 ServiceContractAttribute Properties
Property | Description |
|
Specifies the name of the service contract as defined in the element in WSDL. |
|
Defines the namespace of the service contract used by the element in WSDL. |
|
The name of the service contract as used in the configuration file. |
|
Determines whether messages used by the service have explicitly defined protection levels. Protection levels enable you to sign, or sign and encrypt, messages. |
|
The protection level to use for message protection. |
|
Determines whether sessions are enabled for messages. If you use sessions, then you can ensure that messages sent to different endpoints of a service are correlated — that is, they use the same service instance and so can share state, and so on. |
|
For duplex messaging the client exposes a contract as well as the service. This is because, as discussed earlier, the client in duplex communications also acts as a server. This property enables you to specify which contract the client uses. |
Within interfaces that define service contracts, you define members as operations by applying the
attribute. This attribute has the properties described in Table 22.6.System.ServiceModel.OperationContractAttribute
Table 22.6 OperationContractAttribute Properties
Property | Description |
|
Specifies the name of the service operation. The default is the member name. |
|
Specifies whether the operation returns a response. If you set this to , then clients won't wait for the operation to complete before continuing. |
|
If set to , the operation is implemented as two methods that you can use to call the operation asynchronously: and . |
|
See the previous section. |
|
See the previous section. |
|
If sessions are used, then this property determines whether calling this operation can start a new session. |
|
If sessions are used, then this property determines whether calling this operation terminates the current session. |
|
If you are using addressing (an advanced capability of WCF services), then an operation has an associated action name, which you can specify with this property. |
|
As with , but specifies the action name for the operation response. |
In the .NET 4.5 Framework, when you add a service reference, Visual Studio also generates asynchronous proxy methods to call the service, regardless of whether
is set to AsyncPattern
. These methods, which have the suffix true
, use the new asynchronous techniques that are included in .NET 4.5, and are asynchronous only from the point of view of the calling code. Internally, they call the synchronous WCF operations.Async
The earlier example didn't use message contract specifications. If you use these, then you do so by defining a class that represents the message and applying the
attribute to the class. You then apply MessageContractAttribute
, MessageBodyMemberAttribute
, or MessageHeaderAttribute
attributes to members of this class. All these attributes are in the MessageHeaderArrayAttribute
namespace. You are unlikely to want to do this unless you need a very high degree of control over the SOAP messages used by WCF services, so details are not provided here.System.ServiceModel
If you have a particular exception type — for example, a custom exception — that you want to make available to client applications, then you can apply the
attribute to the operation that might generate this exception.System.ServiceModel.FaultContractAttribute
C:\BegVCSharp\Chapter22
.Class1.cs
file.System.Runtime.Serialization.dll
and System.ServiceModel.dll
assemblies to the Ch22Ex02Contracts project.Person
to the Ch22Ex02Contracts project and modify the code in Person.cs
as follows:using System.Runtime.Serialization;
namespace Ch22Ex02Contracts
{
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Mark { get; set; }
}
}
IAwardService
to the Ch22Ex02Contracts project and modify the code in IAwardService.cs
as follows:using System.ServiceModel;
namespace Ch22Ex02Contracts
{
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IAwardService
{
[OperationContract(IsOneWay = true, IsInitiating = true)]
void SetPassMark(int passMark);
[OperationContract]
Person[] GetAwardedPeople(Person[] peopleToTest);
}
}
IService1.cs
and Service1.svc
from the Ch22Ex02 project.AwardService
to Ch22Ex02.IAwardService.cs
file from the Ch22Ex02 project.AwardService.svc.cs
as follows:using System.Collections.Generic;
using Ch22Ex02Contracts;
namespace Ch22Ex02
{
public class AwardService : IAwardService
{
private int passMark;
public void SetPassMark(int passMark)
{
this.passMark = passMark;
}
public Person[] GetAwardedPeople(Person[] peopleToTest)
{
List<Person> result = new List<Person>();
foreach (Person person in peopleToTest)
{
if (person.Mark > passMark)
{
result.Add(person);
}
}
return result.ToArray();
}
}
}
Web.config
as follows: <system.serviceModel>
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" />
</protocolMapping>
…
</system.serviceModel>
System.ServiceModel.dll
assembly and the Ch22Ex02Contracts project to the Ch22Ex02Client project.Program.cs
in Ch22Ex02Client as follows (ensure that you use the port number you obtained earlier in the EndpointAddress
constructor, the example code uses port 49284):using System;
using static System.Console;
using System.ServiceModel;
using Ch22Ex02Contracts;
namespace Ch22E02Client
{
class Program
{
static void Main(string[] args)
{
Person[] people = new Person[]
{
new Person { Mark = 46, Name="Jim" },
new Person { Mark = 73, Name="Mike" },
new Person { Mark = 92, Name="Stefan" },
new Person { Mark = 24, Name="Arthur" }
};
WriteLine("People:");
OutputPeople(people);
IAwardService client = ChannelFactory<IAwardService>.CreateChannel(
new WSHttpBinding(),
new EndpointAddress("http://localhost:38831/AwardService.svc"));
client.SetPassMark(70);
Person[] awardedPeople = client.GetAwardedPeople(people);
WriteLine();
WriteLine("Awarded people:");
OutputPeople(awardedPeople);
ReadKey();
}
static void OutputPeople(Person[] people)
{
foreach (Person person in people)
WriteLine("{0}, mark: {1}", person.Name, person.Mark);
}
}
}
In this example, you created a set of contracts in a class library project and used that class library in both a WCF service and a client. The service, as in the previous example, is hosted in a web server. The configuration for this service is reduced to the bare minimum.
The main difference in this example is that no metadata is required by the client, as the client has access to the contract assembly. Instead of generating a proxy class from metadata, the client obtains a reference to the service contract interface through an alternative method. Another point to note about this example is the use of a session to maintain state in the service, which requires the
binding instead of the WSHttpBinding
binding.BasicHttpBinding
The data contract used in this example is for a simple class called
, which has a Person
property called string
and an Name
property called int
. You used the Mark
and DataContractAttribute
attributes with no customization, and there is no need to reiterate the code for this contract here.DataMemberAttribute
The service contract is defined by applying the
attribute to the ServiceContractAttribute
interface. The IAwardService
property of this attribute is set to SessionMode
, as this service requires state:SessionMode.Required
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IAwardService
{
The first operation contract,
, is the one that sets state, and therefore has the SetPassMark()
property of IsInitiating
set to OperationContractAttribute
. This operation doesn't return anything, so it is defined as a one-way operation by setting true
to IsOneWay
:true
[OperationContract(IsOneWay=true,IsInitiating=true)]
void SetPassMark(int passMark);
The other operation contract,
, does not require any customization and uses the data contract defined earlier:GetAwardedPeople()
[OperationContract]
Person[] GetAwardedPeople(Person[] peopleToTest);
}
Remember that these two types,
and Person
, are available to both the service and the client. The service implements the IAwardService
contract in a type called IAwardService
, which doesn't contain any remarkable code. The only difference between this class and the service class you saw earlier is that it is stateful. This is permissible, as a session is defined to correlate messages from a client.AwardService
To ensure that the service uses the
binding, you added the following to WSHttpBinding
for the service:Web.config
<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>
This overrides the default mapping for HTTP binding. Alternatively, you could configure the service manually and keep the existing default, but this override is much simpler. However, be aware that this type of override is applied to all services in a project. If you have more than one service in a project, then you would have to ensure that this binding is acceptable to each of them.
The client is more interesting, primarily because of this code:
IAwardService client = ChannelFactory<IAwardService>.CreateChannel(
new WSHttpBinding(),
new EndpointAddress("http://localhost:38831/AwardService.svc"));
The client application has no
file to configure communications with the service, and no proxy class defined from metadata to communicate with the service. Instead, a proxy class is created through the app.config
method. This method creates a proxy class that implements the ChannelFactory<T>.CreateChannel()
client, although behind the scenes the generated class communicates with the service just like the metadata-generated proxy shown earlier.IAwardService
If you create a proxy class with
, the communication channel will, by default, time out after a minute, which can lead to communication errors. There are ways to keep connections alive, but they are beyond the scope of this chapter.ChannelFactory<T>.CreateChannel()
Creating proxy classes in this way is an extremely useful technique that you can use to quickly generate a client application on-the-fly.
So far in this chapter you have seen WCF services that are hosted in web servers. This enables you to communicate across the Internet, but for local network communications it is not the most efficient way of doing things. For one thing, you need a web server on the computer that hosts the service. In addition, the architecture of your applications might be such that having an independent WCF service isn't desirable.
Instead, you might want to use a self-hosted WCF service. A self-hosted WCF service exists in a process that you create, rather than in the process of a specially made hosting application such as a web server. This means, for example, that you can use a console application or Windows application to host your service.
To self-host a WCF service, you use the
class. You instantiate this class with either the type of the service you want to host or an instance of the service class. You can configure a service host through properties or methods, or (and this is the clever part) through a configuration file. In fact, host processes, such as web servers, use a System.ServiceModel.ServiceHost
instance to do their hosting. The difference when self-hosting is that you interact with this class directly. However, the configuration you place in the ServiceHost
section of the <system.serviceModel>
file for your host application uses exactly the same syntax as the configuration sections you've already seen in this chapter.app.config
You can expose a self-hosted service through any protocol that you like, although typically you will use TCP or named pipe binding in this type of application. Services accessed through HTTP are more likely to live inside web server processes, because you get the additional functionality that web servers offer, such as security and other features.
If you want to host a service called
, you could use code such as the following to create an instance of MyService
:ServiceHost
ServiceHost host = new ServiceHost(typeof(MyService));
If you want to host an instance of
called MyService
, you could code as follows to create an instance of myServiceObject
:ServiceHost
MyService myServiceObject = new MyService();
ServiceHost host = new ServiceHost(myServiceObject);
Hosting a service instance in a
works only if you configure the service so that calls are always routed to the same object instance. To do this, you must apply a ServiceHost
attribute to the service class and set the ServiceBehaviorAttribute
property of this attribute to InstanceContextMode
.InstanceContextMode.Single
After creating a
instance you can configure the service and its endpoints and binding through properties. Alternatively, if you put your configuration in a ServiceHost
file, the .config
instance will be configured automatically.ServiceHost
To start hosting a service once you have a configured
instance, you use the ServiceHost
method. Similarly, you stop hosting the service through the ServiceHost.Open()
method. When you first start hosting a TCP-bound service, you might, if you have it enabled, receive a warning from the Windows Firewall service, as it will block the TCP port by default. You must open the TCP port for the service to begin listening on the port.ServiceHost.Close()
In the following Try it Out you use self-hosting techniques to expose some functionality of a WPF application through a WCF service.
C:\BegVCSharp\Chapter22
.MainWindow.xaml
as follows:<Window x:Class="Ch22Ex03.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
"
Title="Stellar Evolution" Height="450" Width="430"
Loaded="Window_Loaded" Closing="Window_Closing">
<Grid Height="400" Width="400" HorizontalAlignment="Center"
VerticalAlignment="Center">
<Rectangle Fill="Black" RadiusX="20" RadiusY="20"
StrokeThickness="10">
<Rectangle.Stroke>
<LinearGradientBrush EndPoint="0.358,0.02"
StartPoint="0.642,0.98">
<GradientStop Color="#FF121A5D" Offset="0" />
<GradientStop Color="#FFB1B9FF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
<Ellipse Name="AnimatableEllipse" Stroke="{x:Null}" Height="0"
Width="0" HorizontalAlignment="Center"
VerticalAlignment="Center">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="#FFFFFFFF" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.Effect>
<DropShadowEffect ShadowDepth="0" Color="#FFFFFFFF"
BlurRadius="50" />
</Ellipse.Effect>
</Ellipse>
</Grid>
</Window>
MainWindow.xaml.cs
as follows:using System.Windows.Shapes;
using System.ServiceModel;
using System.Windows.Media.Animation;
namespace Ch22Ex03
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private AppControlService service;
private ServiceHost host;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
service = new AppControlService(this);
host = new ServiceHost(service);
host.Open();
}
private void Window_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
host.Close();
}
internal void SetRadius(double radius, string foreTo,
TimeSpan duration)
{
if (radius > 200)
{
radius = 200;
}
Color foreToColor = Colors.Red;
try
{
foreToColor = (Color)ColorConverter.ConvertFromString(foreTo);
}
catch
{
// Ignore color conversion failure.
}
Duration animationLength = new Duration(duration);
DoubleAnimation radiusAnimation = new DoubleAnimation(
radius * 2, animationLength);
ColorAnimation colorAnimation = new ColorAnimation(
foreToColor, animationLength);
AnimatableEllipse.BeginAnimation(Ellipse.HeightProperty,
radiusAnimation);
AnimatableEllipse.BeginAnimation(Ellipse.WidthProperty,
radiusAnimation);
((RadialGradientBrush)AnimatableEllipse.Fill).GradientStops[1]
.BeginAnimation(GradientStop.ColorProperty, colorAnimation);
}
}
}
IAppControlService.cs
as follows:[ServiceContract]
public interface IAppControlService
{
[OperationContract]
void SetRadius(int radius, string foreTo, int seconds);
}
AppControlService.cs
as follows:[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class AppControlService : IAppControlService
{
private MainWindow hostApp;
public AppControlService(MainWindow hostApp)
{
this.hostApp = hostApp;
}
public void SetRadius(int radius, string foreTo, int seconds)
{
hostApp.SetRadius(radius, foreTo, new TimeSpan(0, 0, seconds));
}
}
app.config
as follows:<configuration>
<system.serviceModel>
<services>
<service name="Ch22Ex03.AppControlService">
<endpoint address="net.tcp://localhost:8081/AppControlService"
binding="netTcpBinding"
contract="Ch22Ex03.IAppControlService" />
</service>
</services>
</system.serviceModel>
</configuration>
System.ServiceModel.dll
and Ch22Ex03 to the Ch22Ex03Client project.Program.cs
as follows:using Ch22Ex03;
using System.ServiceModel;
using static System.Console;
namespace Ch22Ex03Client
{
class Program
{
static void Main(string[] args)
{
WriteLine("Press enter to begin.");
ReadLine();
WriteLine("Opening channel.");
IAppControlService client =
ChannelFactory<IAppControlService>.CreateChannel(
new NetTcpBinding(),
new EndpointAddress(
"net.tcp://localhost:8081/AppControlService"));
WriteLine("Creating sun.");
client.SetRadius(100, "yellow", 3);
WriteLine("Press enter to continue.");
ReadLine();
WriteLine("Growing sun to red giant.");
client.SetRadius(200, "Red", 5);
WriteLine("Press enter to continue.");
ReadLine();
WriteLine("Collapsing sun to neutron star.");
client.SetRadius(50, "AliceBlue", 2);
WriteLine("Finished. Press enter to exit.");
ReadLine();
}
}
}
In this example you have added a WCF service to a WPF application and used it to control the animation of an
control. You have created a simple client application to test the service. Don't worry too much about the XAML code in this example if you are not familiar with WPF yet; it's the WCF plumbing that is of interest here.Ellipse
The WCF service,
, exposes a single operation, AppControlService
, which clients call to control the animation. This method communicates with an identically named method defined in the SetRadius()
class for the WPF application. For this to work, the service needs a reference to the application, so you must host an object instance of the service. As discussed previously, this means that the service must use a behavior attribute:Window1
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class AppControlService : IAppControlService
{
…
}
In
, the service instance is created in the Window1.xaml.cs
event handler. This method also begins hosting by creating a Windows_Loaded()
object for the service and calling its ServiceHost
method:Open()
public partial class Window1 : Window
{
private AppControlService service;
private ServiceHost host;
…
private void Window_Loaded(object sender, RoutedEventArgs e)
{
service = new AppControlService(this);
host = new ServiceHost(service);
host.Open();
}
When the application closes, hosting is terminated in the
event handler.Window_Closing()
The configuration file is again about as simple as it can be. It defines a single endpoint for the WCF service that listens at a
address, on port 8081, and uses the default net.tcp
binding:NetTcpBinding
<service name="Ch22Ex03.AppControlService">
<endpoint address="net.tcp://localhost:8081/AppControlService"
binding="netTcpBinding"
contract="Ch22Ex03.IAppControlService"/>
</service>
This matches up with code in the client app:
IAppControlService client =
ChannelFactory<IAppControlService>.CreateChannel(
new NetTcpBinding(),
new EndpointAddress(
"net.tcp://localhost:8081/AppControlService"));
When the client has created a client proxy class, it can call the
method with radius, color, and animation duration parameters, and these are forwarded to the WPF application through the service. Simple code in the WPF application then defines and uses animations to change the size and color of the ellipse.SetRadius()
This code would work across a network if you used a machine name, rather than
, and if the network permitted traffic on the specified port. Alternatively, you could separate the client and host application further, and connect across the Internet. Either way, WCF services provide an excellent means of communication that doesn't take much effort to set up.localhost
22.1 Which of the following applications can host WCF services?
a Web applications
b Windows Forms applications
c Windows services
d COM+ applications
e Console applications
22.2 Which type of contract would you implement if you wanted to exchange parameters of type
with a WCF service? Which attributes would you require?MyClass
22.3 If you host a WCF service in a web application, what extension will the base endpoint for the service use?
22.4 When self-hosting WCF services, you must configure the service by setting properties and calling methods of the
class. True or false?ServiceHost
22.5 Provide the code for a service contract,
, with operations defined for IMusicPlayer
, Play()
, and Stop()
. Use one-way methods where appropriate. What other contracts might you define for this service to work?GetTrackInformation()
22.5 Answers to the exercises can be found in Appendix A.
Topic | Key Concepts |
WCF fundamentals | WCF provides a framework for creating and communicating with remote services. It combines elements of the web service and remoting architectures along with new technologies to achieve this. |
Communication protocols | You can communicate with a WCF service by any one of several protocols, including HTTP and TCP. This means that you can use services that are local to your client application, or that are separated by machine or network boundaries. To do this, you access a specific endpoint for the service through a binding corresponding to the protocol and features that you require. You can control these features, such as using session state or exposing metadata, through behaviors. .NET 4.5 includes many default settings to make it very easy to define a simple service. |
Communication payload | Typically, calls to responses from WCF services are encoded as SOAP messages. However, there are alternatives, such as plain HTTP messages, and you can define your own payload types from scratch if you need to. |
Hosting | WCF services might be hosted in IIS or in a Windows service, or they can be self-hosted. Using a host such as IIS enables you to make use of the host's built-in capabilities, including security and application pooling. Self-hosting is more flexible, but it can require more configuration and coding. |
Contracts | You define the interface between a WCF service and client code through contracts. Services themselves, along with any operations they expose, are defined with service and operation contracts. Data types are defined with data contracts. Further customization of communications is achieved with message and fault contracts. |
Client applications | Client applications communicate with WCF services by means of a proxy class. Proxy classes implement the service contract interface for the service, and any calls to operation methods of this interface are redirected to the service. You can generate a proxy by using the Add Service Reference tool, or you can create one programmatically through channel factory methods. In order for communications to succeed, the client must be configured to match the service configuration. |