Windows Communication Foundation (WCF) (formerly known as "Indigo") will fundamentally change the distributed programming interface for developers using Microsoft. NET Framework. The entire set of existing. net distributed technology is integrated into a programming model, which improves your experience through stable structure, greatly improved functionality and interoperability, and all the scalability you want. This article will introduce you to WCF programming and how to get started quickly.
As its name indicates, WCF provides a basis for. NET Framework to writeCodeTo be used in components and ApplicationsProgramAnd the system. The Design of WCF follows the service-oriented principle. A service is a piece of code that can interact with a message. Services are passive. They do not work until the message is passed in. The client is the initiator. The client sends a message to the service to request work.
Figure 1: Service and Endpoint
The service provides one or more endpoints to send messages to these endpoints. Each endpoint consists of an address, a binding, and a contract (see figure 1 ). The address specifies the destination location of the message to be sent. The binding describes how to send the message and the contract describes the content of the message. The client must first obtain this information before accessing the service.
The service can package the endpoint description to share it with the client. It is generally achieved by using the Web Service Description Language (WSDL. Then, the client can use the provided service description to generate code in its environment (which can send and receive correct messages) (see figure 2 ).
Figure 2: Shared endpoint description
The Windows Communication Foundation provides a new class library located in the system. servicemodel namespace to make these service-oriented concepts a reality. This is generally referred to as the WCF programming model.
WCF Programming Model
Through WCF, you can write services that provide endpoints or clients that interact with endpoints. Therefore, endpoints are the core of the WCF programming model and infrastructure. The WCF model endpoint with. Net classes and interfaces is as follows:Figure 3. Whether you write a WCF client or a service, the ing is correct.
When creating a WCF Service, it is usually first defined as the. NET interface used as the service contract. Then, implement the service contract in the. NET class (called the service type) and configure its behavior. Next, define the endpoint to be provided by the Service, and specify the location, binding, and contract for each endpoint. Finally, use the WCF hosting infrastructure to host service types in applications. After the service type is hosted, the client can retrieve its endpoint description and start integration with it.
When creating a WCF client, you must first obtain the description of the target endpoint to be accessed. This endpoint description can be used to dynamically create a type proxy. WCF provides a tool named svcutil.exe to automatically execute the process. Then, you can write code for the Type proxy and send the corresponding message to the target endpoint to access the service.
Back to Top
Service Contract and Scheduling
Use the traditional C # interface definition to create a service contract in. net. Any. net interface can be used as the starting point, as shown below:
Namespace servicelibrary {public interface iechoservice {string echo (string MSG );}}
To make it a WCF Service Contract, you must use [servicecontract] to add comments to the interface itself, and use [operationcontract] to add comments to each operation to be provided:
Using system. servicemodel; namespace servicelibrary {[servicecontract (namespace = "http://example.org/echo/")] public interface iechoservice {[operationcontract] string echo (string MSG );}}
These attributes affect the ing between the. NET domain and the soap domain. WCF uses the information in the service contract for scheduling and serialization. Scheduling is the process of determining the call method for the incoming SOAP message. Serialization is the process of ing between data in soap messages and corresponding. Net objects used in method calls. The ing is controlled by the operated data contract.
WCF schedules messages based on message actions. Each method in the service contract is automatically assigned an action value based on the service namespace and method name. For example, the default action of the echo method just shown is http://example.org/echo/echo. You can use [operationcontract] to customize the action value for each method. If the exact match does not exist, you can use the value * to represent any action.
In the following example, WCF schedules messages with urn: ECHO: String actions to the echo method, and schedules messages with any other actions to the echomessage method:
[Servicecontract (namespace = "http://example.org/echo/")] public interface iechoservice {[operationcontract (Action = "urn: ECHO: string")] string echo (string MSG ); [operationcontract (Action = "*")] Message echomessage (Message MSG );}
Back to Top
Data contract
After the target method is determined based on the action, WCF will execute serialization Based on the Data contract of the method. The data contract is defined by the type used in the method signature. In the preceding example, echomessage is generic and can be used to process multiple incoming soap messages. Therefore, I have used message to establish input and output. Message is a special type used to indicate all messages that flow through WCF. When message is used, WCF does not perform type-based serialization. Instead, you can directly access the content that exists in the SOAP message.
When message is not used, WCF will serialize the object to establish a ing between the data in the SOAP message and the corresponding. Net object that needs to call the method. For example, for ECHO, WCF maps the soap payload to a. Net string. WCF provides implicit ing for all. Net primitive types (such as string, integer, and double precision.
The way the WCF serializes the. NET class depends on the serialization engine in use. The default serialization engine is called datacontract, which is a simplified version of the default serialization engine xmlserializer currently used in asmx. Datacontract defines the attributes of the annotation class to affect the serialization process. The following is an example of using datacontract:
[Datacontract (namespace = "http://example.org/person")] public class person {[datamember (name = "first", order = 0)] Public String first; [datamember (name = "last", order = 1)] Public String last; [datamember (isrequired = false, order = 2)] private string ID ;...}
With datacontract, only Fields marked with datamember can be serialized. You can also serialize private fields. This is different from the xmlserializer method. Now, you can use person in the operation contract:
[Servicecontract (namespace = "http://example.org/echo/")] public interface iechoservice {... // the previous method has been omitted [operationcontract] person echoperson (person P );}
After echoperson is called, WCF will serialize the person instance based on the datacontract attribute specified on the person class. Datacontract will generate a very simple XML structure, that is, the element sequence. You can control the name, sequence, and whether special elements are required for each element, but you can only control these elements.
To perform more complex operations, WCF allows you to move back to use xmlserializer. You can use the [xmlserializerformat] annotation interface to indicate that you want to use xmlserializer:
[Servicecontract] [xmlserializerformat] public interface iechoservice {...}
This requires that WCF use xmlserializer for all types in the contract based on the default value of xmlserializer and the custom attributes of system. xml. serialization. This makes it easier to move the existing asmx service to WCF.
WCF also supports serialization of the type marked with [serializable], so that the. NET remote type can be used with WCF without being changed. WCF provides implicit ing for the [serializable] type, in which all public/private fields are automatically serialized.
Back to Top
Message and service contract
All examples so far rely on WCF to automatically map method signatures to soap messages. The parameter list and return type are always mapped to the soap body of the request and response respectively. To support titles, you can write another class to create the entire soap encapsulation structure for individual operations and specify which fields are mapped to the titles and subjects. Use the [messagecontract] attribute to define the ing:
[Messagecontract] public class echopersonmessage {[messagebody] public person; [messageheader] public authorization ;}
In this example, the person field is mapped to the soap subject, and the authorization field is mapped to the soap title. Now, you can use echopersonmessage in the operation contract:
[Servicecontract] public interface iechoservice {... // the previous method has been omitted [operationcontract] void echoperson (echopersonmessage MSG );}
Using messagecontract is a more advanced technology. It is only necessary to directly control the soap contract.
Back to Top
Implementation Service Contract
Now, you only need to implement the. net interface in the class to implement the service contract:
Using system. servicemodel; namespace servicelibrary {public class echoservice: iechoservice {Public String echo (string MSG) {return MSG;}... // The remaining methods have been omitted }}
This operation ensures that echoservice supports the service contract defined by iechoservice. When you use interfaces to define a Service combination, you do not need any contract-related attributes on the class definition. However, you may want to use [servicebehavior] to affect local behavior:
Using system. servicemodel; namespace servicelibrary {... // The interface definition has omitted [servicebehavior (instancecontextmode = instancecontextmode. single, concurrencymode = concurrencymode. multiple)] public class echoservice: iechoservice {...
This special example requires that a single instance of the WCF management service type and multi-thread access to the instance be allowed. There is also a [operationbehavior] attribute used to control operation-level behaviors.
Actions will affect processes in the host, but will never affect the service contract. Behavior is one of the main points of WCF scalability. Any class that implements iservicebehavior can be applied to the service by using custom attributes or configuration elements.
Back to Top
Hosting services and defining endpoints
To use echoservice, you must host it in A. Net-based application. The servicehost class allows you to directly control the WCF hosting infrastructure. You can instantiate servicehost based on a specific service type. The following code shows how to perform this operation in the console application:
Using system; using system. servicemodel; using servicelibrary; Class program {static void main (string [] ARGs) {using (servicehost host = new servicehost (typeof (echoservice), new uri ("http: // localhost: 8080/echo "))){...
In addition to the specified service type, you also need to specify the base address for each transmission you plan to use. In this example, I have specified a base address http: // localhost: 8080/ECHO. This address is used as the base address of any relative HTTP address (which I may specify when adding an endpoint. The HTTP base address is also used as the default address for retrieving service descriptions.
Then, add the service endpoint. In addition, a service may contain one or more endpoints, each of which consists of an address, a binding, and a contract. Call addserviceendpoint to provide this information for servicehost. This is the location where you specify the previously defined service contract (iechoservice. For binding, it is usually selected from many predefined bindings attached to WCF and the corresponding address of the given binding transmission. The following is an example:
Host. addserviceendpoint (typeof (iechoservice), new basichttpbinding (), "SVC"); host. addserviceendpoint (typeof (iechoservice), new nettcpbinding (), "net. TCP: // localhost: 8081/ECHO/svc ");
In this special example, I have specified iechoservice as a contract for two endpoints, but each endpoint uses a different binding and address. The first endpoint uses the relative HTTP address (http: // localhost: 8080/ECHO/svc as its absolute address) of basichttpbinding and SVC ). The second endpoint uses nettcpbinding and the address net. TCP: // localhost: 8081/ECHO/svc. Therefore, this service allows users to use different WS-* protocols (defined by each binding) to communicate with them through HTTP or TCP.
Back to Top
Select custom binding
The WCF infrastructure relies heavily on the endpoint definition to control how messages are processed. In fact, WCF uses the endpoint definition to dynamically create the corresponding message processing runtime between the host and the client. Binding has the most significant impact on this process.
Three aspects of message communication control are bound: WS-* protocol suite, including WS-Security, WS-reliablemessaging, and so on; message encoding method, for example, XML 1.0, message transmission optimization mechanism (MTOM), and binary; transmission protocol, including HTTP, TCP, and Microsoft Message Queuing (MSMQ ). A given binding specifies a message encoding method and a transmission, but multiple WS-* protocols can be specified. Therefore, there are a lot of arrays available. To simplify the requirements, WCF provides a set of predefined bindings, suitable for the most common scenarios.Figure 4Lists some bindings and describes the content they represent.
Developers who focus on interoperability but do not need the WS-* function usually use basichttpbinding. Developers who focus on interoperability but also need secure and reliable message transmission and transactions based on messages usually choose wshttpbinding. Developers who use WCF on both ends (clients and services) can select from nettcpbinding and netnamedpipebinding to provide significant optimization. Developers who build asynchronous systems can select netmsmqbinding. There are also a few bindings not listed here, such as msmqintegrationbinding for integration with existing MSMQ applications, netpeertcpbinding for establishing peer-to-peer network services, and wsfederationbinding for joint security. The Service supports any combination of these bindings.
After you select and instantiate a binding, you can customize some of its features by binding the Attributes provided on the class. The following example shows how to enable transmission-based security on basichttpbinding:
Basichttpbinding B = new basichttpbinding (); B. security. mode = basichttpsecuritymode. transport; B. security. transport. clientcredentialtype = httpclientcredentialtype. basic; host. addserviceendpoint (typeof (iechoservice), B, "SVC ");
If the pre-defined binding and Its customization still do not meet your requirements, you can also compile the custom binding by implementing a class derived from the binding.
Back to Top
Open host
Since the host has been configured using the endpoint information, WCF can establish the runtime required to support your configuration. This happens when you call open on a specific servicehost:
Host. open ();
After an open call is returned, the WCF runtime has been established and is ready to receive messages at the address specified by each endpoint. In this process, WCF generates an endpoint listener for each provided endpoint, and generates the Code required to support the WS-* Protocol specified by the binding. (The endpoint listener is a piece of code. In fact, it uses the specified transmission and address to listen for incoming messages ).
You can use the object model provided by servicehost to retrieve information about services at runtime. This allows you to retrieve any information you may want to know about the initialization service, such as the endpoint provided by the Service and the active endpoint listener.
Figure 5Shows a complete example of a console application hosting echoservice. Figure 6 shows the output. Note that there are two additional endpoint listening program addresses that I did not specify in serviceendpoint. By using the HTTP base address to retrieve the service description, WCF automatically provides these addresses.
Figure 6: Console application output
If you browse http: // localhost: 8080/Echo, you will see the default WCF help page (see figure 7). If you browse http: // localhost: 8080/Echo? The generated WSDL definition is displayed. Another endpoint listener (http: // localhost: 8080/ECHO/MEX) knows how to express WS-metadataexchange. Note that this example does not use IIS in any way. WCF provides embedded integration with httpsys, so that any application can automatically become an HTTP listener.
Figure 7: Echoservice Help Page
Back to Top
Configure service endpoints
Since the endpoint details may change over time, it is not ideal to hard encode the endpoint information to host applications. It is hard to imagine how to store the endpoint information in the configuration file or database and read the information when initializing servicehost. As this is a common requirement, WCF automatically provides this function through the predefined <system. servicemodel> Configuration section.
The following configuration file defines two identical endpoints that have been specified by calling addserviceendpoint:
<Configuration> <system. servicemodel> <services> <service type = "servicelibrary. echoservice "> <endpoint address =" SVC "binding =" basichttpbinding "Contract =" servicelibrary. iechoservice "/> <endpoint address =" net. TCP: // localhost: 8081/ECHO/svc "binding =" nettcpbinding "Contract =" servicelibrary. iechoservice "/> </service> </services> </system. servicemodel> </configuration>
If you add the configuration fileFigure 5The Host application shown in and the call to addserviceendpoint are marked. You will see the same result in the output. Since the communication details have been completely decomposed from the compiled code, this increases the deployment flexibility. You can also configure binding in <system. servicemodel>.Figure 8Shows how to configure basichttpbinding to use transport security (as I have already used in code ).
You can even use the <custombinding> element to define a new custom binding from the beginning. This is equivalent to deriving a new class from the binding. Any operations that can be performed in the code when configuring the endpoint, binding, or even behavior can also be performed during configuration.
Figure 9: Use GUI to define endpoints
Although the intelliisense for configuration files can run well in Visual Studio 2005, some developers still never use XML. The winfx SDK includes a tool named svcconfigeditor, which provides a graphical interface that can be used with multiple <system. servicemodel> Settings. Figure 9 shows how to use this tool to configure endpoints.
Back to Top
Use activation Service
Although I have been trying console applications, servicehost makes it easier to host WCF services in a variety of applications, including Windows Forms applications and Windows Services. In all these application solutions, you are responsible for managing the process of hosting WCF. This is called self-managed in WCF terminology.
In addition to self-hosting, WCF also makes activation of services possible by combining IIS with traditional web hosting technology. You can use the. SVC file (similar to. asmx) to specify the service type to be managed:
<% @ Service class = "servicelibrary. echoservice" %>
Put the file in a virtual directory and deploy the service type to its \ bin directory or Global Assembly Cache (GAC. When using this technology, you specify the service endpoint in Web. config, but because the. SVC file location already implies this address, you do not need to specify the address.
<Configuration> <system. servicemodel> <services> <service type = "servicelibrary. echoservice "> <endpoint address =" "binding =" basichttpbinding "Contract =" servicelibrary. iechoservice "/> </service>...
If you browse the. SVC file, you will notice that the Help page is displayed, indicating that servicehost has been automatically created in the ASP. NET application domain. This operation is completed by an HTTP module that filters incoming. SVC requests and automatically creates and opens the corresponding servicehost when necessary.
If you install the winfx extension of Visual Studio 2005, you will find a new web site project template named indigo service (which will be changed. When you create a Web Site Based on this template, it automatically provides you with the. SVC file and the corresponding implementation class (in app_code ). This template also includes a default endpoint configuration in Web. config.
In versions 5.1 and 6.0 of IIS, the WCF activation model is bound to the ASP. NET pipeline and therefore to HTTP. IIS 7.0 (planned to be released together with Windows Vista) introduces a neutral transmission activation mechanism called Windows activation Service (was ). With was, you can apply activation such as. SVC to any transmission.
Back to Top
Client Programming
Program the WCF client, including sending messages based on the service endpoint address, binding, and forwarding. To complete this operation, you must first create a serviceendpoint to represent the target endpoint. The following example shows how to create a serviceendpoint in the client console application based on the HTTP endpoint of the Service:
Using system; using system. servicemodel; using servicelibrary; Class program {static void main (string [] ARGs) {serviceendpoint httpendpoint = new serviceendpoint (contractdescription. getcontract (typeof (iechoservice), new basichttpbinding (), new endpointaddress ("http: // localhost: 8080/ECHO/svc ");
This Code assumes that the client can access the same iechoservice interface definition used by the Service, and that the client knows the binding and address to use. Then, you can create channelfactory Based on serviceendpoint:
Channelfactory <iechoservice> factory = new channelfactory <iechoservice> (httpendpoint );
Channelfactory is used to create a type proxy. In this example, it creates an iechoservice proxy. You can use this proxy to call the operations defined by the contract:
Iechoservice SVC = factory. createchannel (); console. writeline (SVC. Echo ("Hello, world "));
If a service requires a custom binding, you must create and customize the binding before creating a serviceendpoint. To access the TCP endpoint, you only need to create another serviceendpoint to specify the details of the TCP endpoint and provide it to channelfactory instead of the HTTP endpoint. All other projects remain unchanged.
Proxy allows you to avoid using different addresses and bindings, and allows you to write application code for the service contract.Figure 10Shows the complete client console application.
Back to Top
Configure the client endpoint
It is not ideal to hard encode the endpoint information to the client code. Therefore, WCF provides a similar configuration mechanism to specify the client endpoint in <system. servicemodel>. The following application configuration file specifies the details of the same client endpoint used in the Code:
<Configuration> <system. servicemodel> <client> <endpoint name = "httpendpoint" address = "http: // localhost: 8080/ECHO/svc" binding = "basichttpbinding" Contract = "servicelibrary. iechoservice "/> <endpoint name =" tcpendpoint "address =" net. TCP: // localhost: 8081/ECHO/svc "binding =" nettcpbinding "Contract =" servicelibrary. iechoservice "/> </client> </system. servicemodel> </configuration>
Note that I have specified a name for each client endpoint. When creating a channelfactory, you can use this name in your code, as shown in the following code:
Using (channelfactory <iechoservice> httpfactory = new channelfactory <iechoservice> ("httpendpoint") {SVC = httpfactory. createchannel (); console. writeline ("invoking HTTP endpoint: {0}", SVC. echo ("Hello, world "));}
Now, to create a channelfactory for the TCP endpoint, you only need to change the configuration name to tcpendpoint. You can also configure binding in the client configuration file, as I did in the service configuration file.
If you add this configuration fileFigure 10Client console application, code for creating serviceendpoint object, and specify the endpoint name when constructing each channelfactory.
Back to Top
Generate client proxy
The preceding example assumes that the client can access the same interface definition used to implement the service. When. net is used to execute both ends (in the traditional. in the NET remote solution), this assumption is acceptable, but it does not necessarily apply to all situations when the service fails. net Framework implementation, this assumption is not true. The above example also assumes that the client knows the accurate endpoint details in advance, but this assumption is not always true.
When clients do not have the permission to access such information, they can use the WS-metadataexchange specification to obtain this information. The service defines the description of the shared endpoint with the client through the WSDL. Most Service Platforms provide tools for generating client code from the WSDL definition. The winfx SDK provides svcutil.exe for this purpose.
When svcutil.exe is executed, you specify the URL defined by the WSDL. It generates the required. Net code and configuration elements to define the binding between the endpoint and the service endpoint. Figure 11 shows the output of svcutil.exe running through http: // localhost: 8080/ECHO. Note that two files are generated: echoservice. CS and output. config.
Figure 11: Svcutil.exe output
Echoservice. CS contains a. net interface definition equivalent to the service implementation Definition. Output. config contains the client endpoint information required to access the service (similar to the content I wrote just now ). You must manually merge the output. config content with the application configuration file. In this case, you can use the same technology used in the above example to write client code, only use the generated interface and configuration file.
In short, the Service Boundaries of the WCF client programming model are very clear. Create a type channel based on a specific server point and send messages through the channel. Although it helps to emphasize the service-oriented principle, it may make the work at this level boring. To help you better work, svcutil.exe also generates a proxy class that completely hides detailed information about creating channelfactory and serviceendpoint. If you enable echoservice. CS, you will see the following class definitions:
Public partial class echoserviceproxy: system. servicemodel. clientbase <iechoservice>, iechoservice {public echoserviceproxy () {} public echoserviceproxy (string endpointconfigurationname): Base (endpointconfigurationname ){}...
This proxy class simplifies the client application code. All you need to do is instantiate the proxy, specify the endpoint configuration name, and call the method. The following is an example:
Using (echoserviceproxy proxy = new echoserviceproxy ("iechoservice") {// call the service operation console. writeline ("invoking HTTP endpoint: {0}", proxy. echo ("Hello, world");} using (echoserviceproxy proxy = new echoserviceproxy ("iechoservice1") {// call the service operation console. writeline ("invoking TCP endpoint: {0}", proxy. echo ("Hello, world "));}
The generated configuration file (output. config) defines the iechoservice and iechoservice1 endpoint configurations.
Back to Top
Record messages
When you start using the WCF programming model, you may find it useful to trace soap messages transmitted between the client and the service. Windows Communication Foundation provides embedded support for message logging, which can be opened in the application configuration file.
Figure 12: Svctraceviewer.exe
Svcconfigeditor provides a "Diagnostics" tab where you can enable message logging. After you start the message record and re-run the application, you will see that the trace file is displayed at the specified position. The winfx SDK also provides svctraceviewer.exe to view information in the trace file (see figure 12 ).
Back to Top
Summary
For developers using. NET Framework, WCF is a new step in the field of distributed programming. If you are using any current. Net distributed technology to build a system, you should begin to pay attention to WCF and its development trend. Writing all communication-related. Net code using WCF is only a matter of time.
Note: All the code examples described in this article are based on Visual Studio 2005 and WCF November 2005 CTP.