Learn The ABCs Of Programming Windows Communication Foundation


Explore the code.
Download the code.

This article is based on a prerelease version of WinFX. All information contained herein is subject to change.


This article discusses:

  • The WCF programming model
  • Defining contracts and behavior
  • Hosting a service and defining endpoints
  • Creating and configuring clients

This article uses the following technologies:
.NET Framework 2.0, WCF, WSDL, SOAP

Download ImageGet the sample code for this article.

NEW:Explore the sample code online!
- or -
Code download available at: WCF.exe (142KB)


Contents

WCF Programming Model
Service Contracts and Dispatch Behavior
Data Contracts
Message and Service Contracts
Implementing Service Contracts
Hosting the Service and Defining Endpoints
Choosing and Customizing Bindings
Opening the Host
Configuring Service Endpoints
Using Activation Services
Programming Clients
Configuring Client Endpoints
Generating Client Proxies
Logging Messages
Conclusion


W

indows® Communication Foundation (WCF), formerly code-named "Indigo," is about to radically change the face of distributed programming for developers using the Microsoft® .NET Framework. WCF unifies the existing suite of .NET distributed technologies into a single programming model that improves the overall developer experience through a consistent architecture, new levels of functionality and interoperability, and all the extensibility points you could want. This article introduces you to WCF programming and shows you how to get started.

As its name suggests, WCF provides the .NET Framework with a basis for writing code to communicate across components, applications, and systems. WCF was designed according to the tenets of service orientation. A service is a piece of code you interact with through messages. Services are passive. They wait for incoming messages before doing any work. Clients are the initiators. Clients send messages to services to request work.

Figure 1 Services and Endpoints
Figure 1 Services and Endpoints

Services expose one or more endpoints where messages can be sent. Each endpoint consists of an address, a binding, and a contract (see Figure 1). The address specifies where to send messages. The binding describes how to send messages. And the contract describes what the messages contain. Clients need to know this information before they can access a service.

Services can package up endpoint descriptions to share with clients, typically by using Web Services Description Language (WSDL). Then clients can use the provided service description to generate code within their environment capable of sending and receiving the proper messages (see Figure 2).

Figure 2 Sharing Endpoint Descriptions
Figure 2 Sharing Endpoint Descriptions

Windows Communication Foundation provides a new library of classes found in the System.ServiceModel namespace that bring these service-oriented concepts to life. This is what's typically referred to as the WCF programming model.

WCF Programming Model

With WCF, you're either writing services that expose endpoints or you're writing clients that interact with endpoints. Hence, endpoints are central to the WCF programming model and infrastructure. WCF models endpoints with the .NET classes and interfaces shown in Figure 3. This mapping is true whether you're writing WCF clients or services.

When building a WCF service, you typically start by defining a .NET interface definition to serve as the service contract. Then you implement the service contract in a .NET class, known as the service type, and configure its behavior. Next, you define the endpoints the service will expose, specifying the address, binding, and contract for each one. Finally, you host the service type in an application using the WCF hosting infrastructure. Once the service type is hosted, clients can retrieve its endpoint descriptions and begin integrating with it.

When building a WCF client, you first need the description of the target endpoint you want to access. The endpoint description can be used to dynamically create a typed proxy. WCF provides a tool named SvcUtil.exe for automating this process. Then you can write code against the typed proxy to access the service by sending the appropriate messages to the target endpoint.

Back to top

Service Contracts and Dispatch Behavior

You model service contracts in .NET using traditional C# interface definitions. You can use any .NET interface as a starting point, such as the one shown here:

namespace ServiceLibrary
{
    public interface IEchoService
    {
        string Echo(string msg);
    }
}

To make this a WCF service contract, you must annotate the interface itself with [ServiceContract] and each operation you want to expose with [OperationContract]:

using System.ServiceModel;

namespace ServiceLibrary
{
    [ServiceContract(Namespace="http://example.org/echo/")]
    public interface IEchoService
    {
        [OperationContract]
        string Echo(string msg);
    }
}

These attributes influence the mapping between the worlds of .NET and SOAP. WCF uses the information found in the service contract to perform dispatching and serialization. Dispatching is the process of deciding which method to call for an incoming SOAP message. Serialization is the process of mapping between the data found in a SOAP message and the corresponding .NET objects used in the method invocation. This mapping is controlled by an operation's data contract.

WCF dispatches based on the message action. Each method in a service contract is automatically assigned an action value based on the service namespace and method name. For example, the default action for the Echo method just shown is http://example.org/echo/Echo. You can customize the action value for each method using [OperationContract]. A value of * can be used for any action when a specific match doesn't exist.

In the following example, WCF will dispatch messages with an action of urn:echo:string to the Echo method. Messages with any other action are dispatched 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 Contracts

Once the target method has been determined based on the action, WCF relies on the method's data contract to perform serialization. The data contract is defined by the types used in the method signature. In the previous example, EchoMessage is generic and could be used to process a variety of incoming SOAP messages. Therefore I've used Message to model the input and output. Message is a special type used to represent all messages flowing through WCF. When using Message, WCF does not perform type-based serialization. Instead, it just gives you direct access to what's found in the SOAP message.

When Message is not used, WCF performs serialization to map between the data found in the SOAP message and the corresponding .NET objects needed to invoke the method. For example, in the case of Echo, WCF maps the SOAP payload to a .NET string. WCF provides an implicit mapping for all .NET primitive types (string, int, double, and so on).

The way WCF serializes .NET classes depends on the serialization engine in use. The default serialization engine is known as DataContract, a simplified version of XmlSerializer, the default serialization engine used in ASMX today. DataContract defines attributes for annotating class definitions to influence the serialization process. Here's an example of it in use:

[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 will be serialized. And you can serialize private fields. This is much different from the way XmlSerializer works. Now you can use Person in an operation contract:

[ServiceContract(Namespace="http://example.org/echo/")]
public interface IEchoService
{
    ... // previous methods omitted

    [OperationContract]
    Person EchoPerson(Person p);
}

When EchoPerson is invoked, WCF will serialize Person instances according to the DataContract attributes specified on the Person class. DataContract produces a very simple XML structure—a sequence of elements. You can control the name of each element, the order, and whether a particular element is required, but that's about it.

If you need to do anything more sophisticated, WCF lets you fall back to using XmlSerializer. You indicate your desire to use XmlSerializer by annotating the interface with [XmlSerializerFormat]:

[ServiceContract]
[XmlSerializerFormat]
public interface IEchoService { ... }

This tells WCF to use XmlSerializer for all types in the contract, according to the XmlSerializer defaults and the various System.Xml.Serialization customization attributes. This makes it much easier to move existing ASMX services forward to WCF.

WCF also supports serializing types marked with [Serializable], which allows .NET remoting types to work with WCF without change. WCF provides an implicit mapping for [Serializable] types where all public/private fields are automatically serialized.

Back to top

Message and Service Contracts

All of the examples up to this point have relied on WCF to automatically map the method signature to the SOAP message. The parameter list and return type are always mapped to the SOAP body for the request and response, respectively. If you need to support headers, you can write another class that models the structure of the entire SOAP envelope for the particular operation, specifying which fields map to headers versus the body. You define this mapping with the [MessageContract] attributes:

[MessageContract]
public class EchoPersonMessage
{
    [MessageBody]
    public Person Person;
    [MessageHeader]
    public Authorization Authorization;
}

In this example, the Person field is mapped to the SOAP body while the Authorization field is mapped to a SOAP header. Now you can use EchoPersonMessage in an operation contract:

[ServiceContract]
public interface IEchoService
{
    ... // previous methods omitted

    [OperationContract]
    void EchoPerson(EchoPersonMessage msg);
}

Using MessageContract is a more advanced technique that is only necessary when you need direct control over the SOAP contract.

Back to top

Implementing Service Contracts

Now you can implement the service contract by simply implementing the .NET interface in a class:

using System.ServiceModel;
namespace ServiceLibrary
{
    public class EchoService : IEchoService
    {
        public string Echo(string msg)
        {
            return msg;
        }
        ... // remaining methods omitted
    }
}

By doing this, EchoService is guaranteed to support the service contract defined by IEchoService. When using an interface to define the service contract, you don't need any contract-related attributes on the class definition. However, you may want to use [ServiceBehavior] to influence its local behavior:

using System.ServiceModel;

namespace ServiceLibrary
{
    ... // interface definition omitted

    [ServiceBehavior(
       InstanceContextMode=InstanceContextMode.Single,
       ConcurrencyMode=ConcurrencyMode.Multiple)]
    public class EchoService : IEchoService
    {
       ...

This particular example tells WCF to manage a singleton instance of the service type and to allow multithreaded access to the instance. There is also an [OperationBehavior] attribute for controlling operation-level behavior.

Behaviors influence processing within the host, but have no impact on the service contract whatsoever. Behaviors are one of the primary WCF extensibility points. Any class that implements IServiceBehavior can be applied to a service through the use of a custom attribute or configuration element.

Back to top

Hosting the Service and Defining Endpoints

To use EchoService, you need to host it in a .NET-based application. The ServiceHost class gives you direct control over the WCF hosting infrastructure. You instantiate ServiceHost based on a particular service type. The following code shows how to do this in a 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 specifying the service type, you also specify the base addresses for the different transports you plan to use. In this example, I've specified a base address of http://localhost:8080/echo. This will be used as the base for any relative HTTP addresses I might specify when adding endpoints. The base HTTP address is also used as the default for retrieving the service description.

You then add the service endpoints. Again, a service may have one or more endpoints and each endpoint consists of an address, a binding, and a contract. You provide this information to your ServiceHost by calling AddServiceEndpoint. This is where you specify the service contract you defined earlier (IEchoService). For the binding, you typically choose from one of the many predefined bindings that ship with WCF and an appropriate address given the binding's transport. Here's an example:

host.AddServiceEndpoint(typeof(IEchoService), 
    new BasicHttpBinding(), "svc");
host.AddServiceEndpoint(typeof(IEchoService), 
    new NetTcpBinding(), "net.tcp://localhost:8081/echo/svc");

In this particular example, I've specified IEchoService as the contract for both endpoints, but with each endpoint using a different binding and address. The first endpoint uses the BasicHttpBinding and a relative HTTP address of svc (which would make its absolute address http://localhost:8080/echo/svc). The second endpoint uses the NetTcpBinding and an address of net.tcp://localhost:8081/echo/svc. Therefore, this service allows consumers to communicate with it over either HTTP or TCP using different WS-* protocols, as defined by each binding.

Back to top

Choosing and Customizing Bindings

The WCF infrastructure relies heavily on endpoint definitions to control how messages are processed. In fact, WCF uses the endpoint definitions to dynamically build the appropriate message processing runtime within hosts and clients. The binding has the most significant influence on this process.

The binding controls three aspects of message communication: the suite of WS-* protocols, including WS-Security, WS-ReliableMessaging, and so on; the message encoding, such as XML 1.0, Message Transmission Optimization Mechanism (MTOM), and binary; and the transport protocol, including HTTP, TCP, and Microsoft Message Queuing (MSMQ). A given binding specifies one message encoding and one transport, but it can specify numerous WS-* protocols. Given that, there are an overwhelming number of permutations that could be used. To simplify things, WCF provides a set of predefined bindings that fit the most common use cases. Figure 4 lists some of these and describes what they embody.

Developers who care primarily about interoperability and don't need WS-* functionality will typically use BasicHttpBinding. Developers who care about interoperability, but also need message-based security, reliable messaging, and transactions will typically choose WSHttpBinding. Developers using WCF on both sides (client and service) can choose from NetTcpBinding and NetNamedPipeBinding, which provide significant optimizations. Those building asynchronous systems can choose NetMsmqBinding. There are also a few bindings that aren't listed here, such as MsmqIntegrationBinding for integrating with existing MSMQ applications, NetPeerTcpBinding for building peer-to-peer services, and WSFederationBinding for federated security. A service can support any combination of these bindings.

Once you choose a binding and instantiate it, you can customize some of its characteristics through the properties exposed on the binding class. The following example shows how to enable transport-based security on the BasicHttpBinding:

BasicHttpBinding b = new BasicHttpBinding();
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = 
    HttpClientCredentialType.Basic;
host.AddServiceEndpoint(typeof(IEchoService), b, "svc");

If the predefined bindings and their customizations still don't fit your needs, you can also write a custom binding by implementing a class that derives from Binding.

Back to top

Opening the Host

Now that you've configured the host with endpoint information, WCF can build the runtime needed to support your configuration. This occurs when you call Open on a particular ServiceHost:

host.Open();

Once the call to Open returns, the WCF runtime is built and ready to receive messages at the addresses specified by each endpoint. During this process, WCF generates an endpoint listener for each supplied endpoint and the code needed to support the WS-* protocols specified by the binding. (An endpoint listener is the piece of code that actually listens for incoming messages using the specified transport and address.)

You can retrieve information about the service at run time through the object model exposed by ServiceHost. This allows you to retrieve anything you might want to know about the initialized service, such as what endpoints it exposes and what endpoint listeners are currently active.

Figure 5 shows a complete example of the console application that hosts EchoService. Figure 6 shows the output. Notice there are two additional endpoint listener addresses that I didn't specify in a ServiceEndpoint. WCF automatically provided these using the base HTTP address for retrieving the service description.

Figure 6 Output of Console App
Figure 6 Output of Console App

If you browse to http://localhost:8080/echo, you'll see the default WCF help page (see Figure 7) and if you browse to http://localhost:8080/echo?wsdl, you'll see the generated WSDL definition. The other endpoint listener (http://localhost:8080/echo/mex) knows how to speak WS-MetadataExchange. It's important to note that this sample is not using IIS in any way. WCF provides built-in integration with httpsys, which allows any application to automatically become an HTTP listener.

Figure 7 EchoService Help Page
Figure 7 EchoService Help Page

Back to top

Configuring Service Endpoints

Hardcoding endpoint information into the host application is not ideal since endpoint details may change over time. It's not hard to imagine how you could store the endpoint information in a configuration file or database and read it while initializing ServiceHost. Since this is a common need, WCF automatically provides this functionality through the predefined <system.serviceModel> configuration section.

The following configuration file defines the same two endpoints that were 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 this configuration file to the host application shown in Figure 5 and comment out the calls to AddServiceEndpoint, you'll see the same results in the output. This increases deployment flexibility since the communication details are completely factored out of the compiled code. You can also configure bindings within <system.serviceModel>. Figure 8 shows how to configure basicHttpBinding to use transport security like I had done in code.

You can even define new custom bindings from scratch using the <customBinding> element, which would be equivalent to deriving a new class from Binding. When it comes to configuring endpoints, bindings, and even behaviors, anything you can do in code, you can also do through configuration.

Figure 9 Using the GUI to Define an Endpoint
Figure 9 Using the GUI to Define an Endpoint

Although IntelliSense® for the configuration file works well in Visual Studio® 2005, some developers still prefer to avoid XML altogether. The WinFX® SDK includes a tool called SvcConfigEditor, which provides a graphical interface for working with the various <system.serviceModel> settings. Figure 9 shows how to configure endpoints using this tool.

Back to top

Using Activation Services

Although I've been experimenting with console applications, ServiceHost makes it easy to host WCF services in a variety of applications, including Windows Forms applications and managed Windows Services. In all of these application scenarios, you're responsible for managing the process hosting WCF. This is known as self-hosting in WCF terms.

In addition to self-hosting, WCF also makes it possible to activate services by using IIS along with traditional Web hosting techniques. You can accomplish this by using a .svc file (similar to .asmx) that specifies the service type to host:

<%@Service class="ServiceLibrary.EchoService" %>

You place this file in a virtual directory and deploy your service type to its \bin directory or the Global Assembly Cache (GAC). When using this technique you specify the service endpoint in the web.config, but you don't need to specify the address since it's implied by the location of the .svc file.

<configuration>
  <system.serviceModel>
    <services>
      <service type="ServiceLibrary.EchoService">
        <endpoint address="" binding="basicHttpBinding" 
          contract="ServiceLibrary.IEchoService"/>
      </service>
      ...

Now if you browse to the .svc file, you'll notice the help page is displayed, showing that a ServiceHost was automatically created within an ASP.NET application domain. This is accomplished by an HTTP module that filters incoming .svc requests and automatically builds and opens the appropriate ServiceHost when needed.

If you install the WinFX extensions for Visual Studio 2005, you'll find a new Web Site project template called Indigo Service (this name will change). When you create a new Web site based on this template, it automatically gives you the .svc file along with the corresponding implementation class (found in App_Code). It also comes with a default endpoint configuration in web.config.

On IIS versions 5.1 and 6.0 the WCF activation model is tied to the ASP.NET pipeline, and therefore to HTTP. IIS 7.0, which is planned for release with Windows Vista, introduces a transport-neutral activation mechanism known as Windows Activation Services (WAS). With WAS, you'll be able to leverage .svc-like activation over any transport.

Back to top

Programming Clients

Programming WCF clients involves sending messages to a service endpoint according to its address, binding, and contract. To accomplish this, you first create a ServiceEndpoint representing the target endpoint. The following example shows how to create a ServiceEndpoint, based on the service's HTTP endpoint, in a client console application:

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 has access to the same IEchoService interface definition that the service used and that the client knows what binding and address to use. Then you can create a ChannelFactory based on the ServiceEndpoint:

ChannelFactory<IEchoService> factory =
    new ChannelFactory<IEchoService>(httpEndpoint);

ChannelFactory creates typed proxies. In this case, it creates proxies of type IEchoService, which you can use to invoke the operations defined by the contract:

IEchoService svc = factory.CreateChannel();
Console.WriteLine(svc.Echo("Hello, world"));

If the service requires a customized binding, you will need to create the binding and customize it before creating ServiceEndpoint. If you want to access the TCP endpoint, simply create another ServiceEndpoint specifying the TCP endpoint details and supply it to ChannelFactory instead of the HTTP endpoint. Everything else would work the same.

The proxy shields you from the different addresses and bindings in use, allowing you to write application code against the service contract. Figure 10 shows the complete client console application.

Back to top

Configuring Client Endpoints

Hardcoding endpoint information into client code is also less than ideal. So WCF provides a similar configuration mechanism for specifying client endpoints within <system.serviceModel>. The following application configuration file specifies the same client endpoint details 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>

Notice that I've given each client endpoint a name. You can use this name in your code when creating a ChannelFactory, as illustrated 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 when you want to create a ChannelFactory for the TCP endpoint, you can simply change the configuration name to tcpEndpoint. You can also configure bindings in the client configuration file just like I did in the service configuration file.

If you add this configuration file to the client console application shown in Figure 10, comment out the code to create the ServiceEndpoint objects, and specify the endpoint names when constructing each ChannelFactory, the result will be the same.

Back to top

Generating Client Proxies

The preceding examples assumed the client had access to the same interface definition used to implement the service. This is common when .NET is used to implement both sides (in traditional .NET remoting scenarios), but that's not always the case, and it's never the case when the service wasn't implemented with the .NET Framework. The preceding examples also assumed the client knew the precise endpoint details in advance, which isn't always common either.

When clients don't have access to such information, they can discover it using the WS-MetadataExchange specification. Services share endpoint descriptions with clients through WSDL definitions. Most service platforms provide tools for generating client-side code from WSDL definitions. The WinFX SDK provides SvcUtil.exe for this purpose.

When executing SvcUtil.exe, you specify the URL of the WSDL definition and it will generate the necessary .NET code and configuration elements that define the endpoints and bindings used by the service endpoints. Figure 11 shows the output of running SvcUtil.exe against http://localhost:8080/echo. Notice that it generates two files: EchoService.cs and output.config.

Figure 11 SvcUtil.exe Output
Figure 11 SvcUtil.exe Output

EchoService.cs contains a .NET interface definition equivalent to the one the service implemented. Output.config contains the client endpoint information needed to access the service (similar to what I just wrote manually). You'll need to merge the contents of output.config with your application configuration file manually. With this in place, you can write client code using the same techniques shown in the previous examples, only using the generated interface and configuration file.

In general, the WCF client programming model is explicit about the service boundary. You create a typed channel against a specific service endpoint and then send messages through it. Although this helps emphasize the tenets of service orientation, it can be tedious to work at this level all the time. To help you, SvcUtil.exe also generates a proxy class that completely hides the ChannelFactory and ServiceEndpoint creation details. If you open EchoService.cs, you'll find the following class definition:

public partial class EchoServiceProxy : 
   System.ServiceModel.ClientBase<IEchoService>, IEchoService
{
    public EchoServiceProxy()
    {
    }    
    public EchoServiceProxy(string endpointConfigurationName) : 
            base(endpointConfigurationName)
    {
    }
...

This proxy class can simplify your client application code. All you have to do is instantiate the proxy, specify the name of the endpoint configuration, and make method calls. Here's an example:

using (EchoServiceProxy proxy = new EchoServiceProxy("IEchoService"))
{
    // invoke service operation
    Console.WriteLine("Invoking HTTP endpoint: {0}",
        proxy.Echo("Hello, world"));
}
using (EchoServiceProxy proxy = new EchoServiceProxy("IEchoService1"))
{
    // invoke 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

Logging Messages

As you begin working with the WCF programming model, you may find it useful to trace the SOAP messages traveling between clients and services. Windows Communication Foundation provides built-in support for message logging, which can be turned on through your application configuration file.

Figure 12 SvcTraceViewer.exe
Figure 12 SvcTraceViewer.exe

SvcConfigEditor provides a Diagnostics tab where you can enable the messaging logging features. Once you enable messaging logging and rerun the app, you'll see the trace file appear in the specified location. The WinFX SDK also provides SvcTraceViewer.exe for viewing the information in a trace file (see Figure 12).

Back to top

Conclusion

WCF is a new step in distributed programming for developers using the .NET Framework. If you currently build systems using any of today's .NET distributed technologies, it's time to start paying attention to WCF and the future it holds. It's only a matter of time before all .NET-targeted code related to communications will be written using WCF.

Note that all of the code samples shown in this article are based on Visual Studio 2005 and the WCF November 2005 CTP.

Back to top

Download Image NEW: Explore the sample code online! - or - Code download available at: WCF.exe (142KB)


Aaron Skonnard is a co-founder of Pluralsight, a Microsoft .NET training provider. Aaron is the author of Pluralsight's Applied Web Services 2.0, Applied BizTalk Server 2006, and Introducing WCF courses. Aaron has spent years developing courses, speaking at conferences, and teaching professional developers. Reach him at pluralsight.com/aaron.


Subscribe  From the February 2006 issue of MSDN Magazine.

Source: Distributed .NET: Learn The ABCs Of Programming Windows Communication Foundation -- MSDN Magazine, February 2006

posted on 2007-04-05 21:11  Joey Liang  阅读(477)  评论(0编辑  收藏  举报