WCF Simplified Part 9: SOAP, WSDL, and REST

Today we’ll look at some of the basic concepts in web services so that we can have a better understanding of how WCF works.

1. SOAP 1.2: SOAP stands for Simple Object Access Protocol. It is simply an XML document that describes a message (structured and typed information) that can be sent from one network endpoint to another. SOAP is fundamentally a stateless, one-way message exchange paradigm, but applications can create more complex interaction patterns (e.g., request/response) by combining such one-way exchanges with features provided by an underlying protocol and/or application-specific information. A SOAP message may be transferred by different underlying protocols for example, for web-based access, it could be placed in the body of a HTTP POST request. The schema for the SOAP XML can be found here. The structure of a SOAP message looks like this,

<?xml version='1.0' ?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">

  <!-- The header is optional. It contains contextual information related to the message -->
  <soap:Header>
  </soap:Header>

  <!-- The body is mandatory -->
  <soap:Body>
    <!-- Sample content -->
    <ns:GetPassengerName xmlns:ns="http://someURI">
      <ns:PassengerId>815</ns:PassengerId>
    </ns:GetPassengerName>
  </soap:Body>

</soap:Envelope>

And another could look like,

<?xml version='1.0' ?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">

  <!-- The body is mandatory -->
  <soap:Body>    
    <!-- The body has content -->
    <ns:GetPassengerNameResponse xmlns:ns="http://someURI">
      <ns:PassengerName>John Locke</ns:PassengerName>
    </ns:GetPassengerNameResponse>
    
    <!-- or can report a fault -->
    <soap:Fault>
      <soap:Code>
      </soap:Code>
      <soap:Reason>
      </soap:Reason>
      <soap:Detail>
      </soap:Detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

The above example is a little contrived, it’s recommended that you use URIs as resource identifiers, so your host might look like http://someHOST/somePATH?PassengerNameFromID=815.

The SOAP-HTTP binding is of particular interest because it is widely used.

    a. SOAP HTTP GET example,

GET /somePATH?PassengerNameFromID=815 HTTP/1.1
Host: someHOST
Accept: text/html;q=0.9, application/soap+xml

with a response,

HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset="utf-8"
Content-Length: [...]

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <env:Body>[...]</env:Body>
</env:Envelope>

    b. SOAP HTTP POST example,

POST /somePATH HTTP/1.1
Host: someHOST
Content-Type: application/soap+xml; charset="utf-8"
Content-Length: [...]

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" >
  <env:Body>[...]</env:Body>
</env:Envelope>

2. WSDL 1.1: WSDL stands for Web Services Description Language. It is simply an XML document that describes a web service. The schema for the WSDL XML is publicly available here. The WSDL XML needs to describe fully how communication between two network endpoints will occur. It is a binding mechanism, it attaches a specific protocol or format to a message, operation or endpoint. The major parts of the WSDL XML are,

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="HelloService">

  <types>
    <!-- What data types are exchanged -->
  </types>
  
  <message>
    <!--What messages are exchanged --> 
  </message>

  <portType>
    <!-- What operations are supported by the web service --> 
  </portType>

  <binding>
    <!-- How the messages will be transmitted --> 
  </binding>

  <service>
    <!-- Where is the web service located --> 
  </service>
  
</definitions>

Say you have a service contract,

[ServiceContract]
public interface IService1 {
    [OperationContract]
    string GetData(int value);
}

The corresponding WSDL looks like (some things omitted for clarity),

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="Service1" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" [...]>
  
  <!-- The two messages GetData and GetDataResponse (because IsOneWay is false) -->
  <wsdl:message name="IService1_GetData_InputMessage">
    <wsdl:part name="parameters" element="tns:GetData"/>
  </wsdl:message>
  <wsdl:message name="IService1_GetData_OutputMessage">
    <wsdl:part name="parameters" element="tns:GetDataResponse"/>
  </wsdl:message>

  <!-- The web service only has one operation - GetData -->
  <wsdl:portType name="IService1">
    <wsdl:operation name="GetData">
      <wsdl:input wsaw:Action="http://tempuri.org/IService1/GetData" message="tns:IService1_GetData_InputMessage"/>
      <wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataResponse" message="tns:IService1_GetData_OutputMessage"/>
    </wsdl:operation>
  </wsdl:portType>
  
  <!-- Note the WSHttpBinding -->
  <wsdl:binding name="WSHttpBinding_IService1" type="tns:IService1">
    <wsdl:operation name="GetData">
        <wsdl:input>
          <wsp:PolicyReference URI="#WSHttpBinding_IService1_GetData_Input_policy"/>
        </wsdl:input>
        <wsdl:output>
          <wsp:PolicyReference URI="#WSHttpBinding_IService1_GetData_output_policy"/>
        </wsdl:output>
      </wsdl:operation>
  </wsdl:binding>
  
  <!-- The address for the web service -->
  <wsdl:service name="Service1">
    <wsdl:port name="WSHttpBinding_IService1" binding="tns:WSHttpBinding_IService1">
      <wsa10:EndpointReference>
        <wsa10:Address>http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary2/Service1/</wsa10:Address>
        <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
          <Dns>localhost</Dns>
        </Identity>
      </wsa10:EndpointReference>
    </wsdl:port>
  </wsdl:service>

3. REST: REST stands for Representational State Transfer. REST is basically a set of design principles for web services. Since it’s not a protocol (like SOAP), it doesn’t specify any new XML or schema but relies on HTTP’s methods (GET/POST/PUT/DELETE). The idea is to design (REST-ful) web services that deal with identifying and manipulating resources using HTTP methods. is to use The four main tenets of REST are,

    a. Use HTTP methods correctly: This means use the methods GET/POST/PUT/DELETE for what they are meant for. For example, GET should be a safe retrieval with no side effects.

    b. Be Stateless: Design the host and client so that the HTTP headers and body of a request contains all of the parameters, context, and data needed by the server-side component to generate a response. A stateless service shifts most of the responsibility of maintaining state to the client application.

   c. Expose self-documenting (directory structure-like) URIs: For example, http://someHOST/photos/2010/05/27/{id}

   d. Use MIME types and HTTP Accept: RESt-ful services usually use the common content types application/json or application/xml.

Digg This
Advertisements

WCF Simplified Part 8: Sessions, Instance Management and Concurrency

Today we’ll look at WCF sessions, instance management, and concurrency.

1. Sessions: A session is a grouping of client requests. Unlike ASP.NET there is no data store associated with a WCF session. Not all bindings support sessions (BasicHttpBinding does not). Client applications initiate sessions and then receive and process the messages sent within the session. Messages delivered during a session are processed in the order in which they are received. When a WCF service accepts a client session, all calls between a WCF client object are handled by the same user-defined service object.

We can use the SessionMode attribute to require, allow, or prohibit bindings to use sessions between endpoints.

    a. Allowed: (default) If a service contract specifies that it allows sessions but does not require one, clients can connect and either establish a session or not establish a session.

    b. Required: When a service contract specifies that it requires a session, that contract is specifying that all calls must be part of the same conversation. The service contract requires a sessionful binding. An exception is thrown if the binding is not configured to support session (can’t use BasicHttpBinding). Creating a service contract that requires a session states that the group of operations that the service contract declares must all be executed within the same session and that messages must be delivered in order.

    c: NotAllowed: The service contract never supports bindings that initiate sessions.

2. Instance Management: The refers to how WCF manages your service class object instantiation (and disposal) depending on a client request, that is, managing the lifetime of the service instance. The possible values for InstanceContextMode are.

    a. PerCall: A new service object is created for each client request, that means that even if the same client is sending requests, a new service object is created for each request and disposed after the reply is sent. To see this, use a service like this,

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class Service1 : IService1 {
    Guid guid = Guid.NewGuid();

    public string GetGuid() {
        return guid.ToString();
    }
}

and a client like this,

for (int i = 0; i < 5; i++) {
    Console.WriteLine(proxy.GetGuid());
}

When a client calls the GetGuid method it will get a new Guid everytime, because the Service1 object is recreated after every reply.

image

    b. PerSession: (default) A new service object is created for each new client session and maintained for the lifetime of the session (applies to bindings that supports session like WSHttpBinsing). For BasicHttpBinding which doesn’t support sessions, its the same as PerCall. To see this, connect multiple clients to the host and each client will get a new Guid, Thus some state is maintained between requests from the same client.

image

    c. Single: A single service object handles all clients and client requests for the lifetime of the application. To see this. simply change the InstanceContextMode to InstanceContextMode.Single. Now every client and every client call will return the same Guid.

image

3. Concurrency: This refers to the number of threads executing at the same time in a service instance.

    a. Single: (default) One thread at a time can access the service object. The service object queues additional calls in the order it receives them. When the first call finishes, the next call in the queue locks the service. The benefit to this approach is that there is no risk of two calls reading the same memory and changing the same data. To see this try the service,

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class Service1 : IService1 {

public void TestConcurrency() {
    Thread.Sleep(10000);
    return;
}

This says that there is a single service object and only one thread can access it. Running two clients against this host will result in,

image

Note that after the first client took 10sec but the second took 20sec.

  b. Reentrant. One thread at a time can access the service object but the thread can exit and reenter. This is useful if the service needs to make callbacks to the client.

    c. Multiple. More than one thread at a time can access the service object. The WCF runtime assigns a thread to each client call and they execute simultaneously. Its up to you to make the service thread safe. To see this, simply change the ConcurrencyMode to Multiple in the example above, now running two clients will result in both taking 10sec,

image

To see how sessions interact with instance management in more detail see here.

WCF Simplified Part 7: Hosting a WCF service using WPF

In the last post we saw how to host a WCF service using IIS. Today we’ll look at hosting a WCF service in a WPF application.

1. Add a new WPF Application project to the solution (built earlier),

image

2. Add a reference to the WCF Service Library,

image

And a reference to System.ServiceModel.

3. The XAML is simple, in the MainWindow.xaml add the code,

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
        <Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions>
        
        <Button Height="30" Width="120" Content="Start WCF service" Click="Button_Click" />
        <Button Height="30" Width="120" Grid.Row="1" Content="Stop WCF service" Click="Button_Click1" />
    </Grid>
</Window>

 

4. And in the code behind, add,

public partial class MainWindow : Window {
    ServiceHost host;

    public MainWindow() {
        InitializeComponent();
        this.host = new ServiceHost(typeof(WcfServiceLibrary1.Service1), new Uri("http://localhost:8003/HelloWCF"));
    }

    private void Button_Click(object sender, RoutedEventArgs e) {

        // Service host is opened on the UI thread
        host.AddServiceEndpoint(typeof(WcfServiceLibrary1.IService1), new WSHttpBinding(), "HelloWCF");

        // Enable metadata exchange
        ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpGetEnabled = true };
        host.Description.Behaviors.Add(smb);

        // Enable exeption details
        ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
        sdb.IncludeExceptionDetailInFaults = true;

        host.Open();

        // Keep track of the UI thread id
        this.Title = Thread.CurrentThread.ManagedThreadId.ToString();
    }

    private void Button_Click1(object sender, RoutedEventArgs e) {
        host.Close();
    }
}

That’s it! We are now hosting a WCF service in a WPF application. As you can see the code is actually the same as what we used when hosting in a console application.

One important note – notice that we created the service host on the UI thread. To see this use the following service contract,

[ServiceContract]
public interface IService1 {
    [OperationContract]
    string GetThreadData();
}

and the implementation,

public class Service1 : IService1 {
    public string GetThreadData() {
        return Thread.CurrentThread.ManagedThreadId.ToString();
    }
}

And the corresponding client,

EndpointAddress epoint = new EndpointAddress("http://localhost:8003/HelloWCF/HelloWCF");
ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new WSHttpBinding(), epoint);

using (proxy as IDisposable) {   
    Console.WriteLine(proxy.GetThreadData());
}

Now connect multiple clients to the host and you’ll see that they are all on the same UI thread (the window title ‘10’ displays the UI thread id),

image

We probably don’t want the UI thread processing requests, it should only be for updating the UI. Another way is to start the service host before you start your UI thread. Now if you go back and change the UseSynchronizationContext to false (by default it is true),

[ServiceBehavior(UseSynchronizationContext=false)]
public class Service1 : IService1 {
    public string GetThreadData()
    {
        return Thread.CurrentThread.ManagedThreadId.ToString();
    }
}

You’ll see something like,

image

This means that requests are processed on a different thread when UseSynchronizationContext is set to false. However if the service host was created on a non-UI thread to begin with then the UseSynchronizationContext settings doesn’t matter requests are always processed on a new thread.

See more WCF service hosting options here.

WCF Simplified Part 6: Hosting a WCF service using IIS

We’ve been self hosting the WCF service library WcfServiceLibrary1.dll in our own console application. Today we’ll see how to host the the service library using IIS.

1. Open the host solution we built in the first post (as admin), and add a new web site to the solution,

image

Then add a WCF Service,

image

2. Now click on the web site project and add a project reference to the WcfServiceLibrary1 project.

image

3. Note that inside the App_Code folder, there is already a template to create a WCF service. But it’s recommended to keep the WCF service library separate from the hosting process, which is why we added the project reference to our WcfServiceLibrary1 and we’ll delete the two files in App_Code, IService.cs and Service.cs. Now, you solution should look like this,

image

4. Now open up the Service.svc file and change it to,

<%@ ServiceHost Service="WcfServiceLibrary1.Service1" %>

 

5. Now you should be able to run the website and see the Visual Studio auto-magically created a host for you,

image

6. Now we can run our client against this IIS-hosted endpoint. By default, this endpoint uses BasicHttpBinding, so change the client to point to the .svc file using BasicHttpBinding should work.

EndpointAddress epoint = new EndpointAddress("http://localhost:60883/WCFService1/Service.svc");
ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new BasicHttpBinding(), epoint);

7 If you want more control over the endpoint, say change the protocol to WSHttpBinding, change the Web.config to,

<?xml version="1.0"?>
<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0"/>
    </system.web>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="serviceBehavior" name="WcfServiceLibrary1.Service1">
        <endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IService1" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="serviceBehavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
    </system.webServer>
</configuration>

8. Now if you run the client you’ll see a protocol exception,

image

9. Changing the client to use WSHttpBinding will work,

EndpointAddress epoint = new EndpointAddress("http://localhost:60883/WCFService1/Service.svc");
ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new WSHttpBinding(), epoint);

WCF Simplified Part 5: Handling Exceptions and Faults

Today we’ll look at how WCF handles exceptions. Before we look at the code, a few things to note,

    1. Uncaught CLR exceptions are turned into a SOAP fault (FaultException).

    2. By default, exception details (even exception type) are not shared with clients.
        Use IncludeExceptionDetailsInFaults (includes type, message, and stack trace) for debugging purposes only.

    3. Uncaught exceptions will kill WCF sessions.

Let’s see some of these in action,

1. Open the WCF service app from the introductory post (run Visual Studio as admin) and change the IService1.cs to have this,

[DataContract]
public class MyCustomException {
    [DataMember]
    public string MyMessage { get; set; }
}

[ServiceContract]
public interface IService1 {

    [OperationContract]
    string ThrowsCLRException();

    [OperationContract(IsOneWay=true)]
    void ThrowsCLRExceptionOneWay();

    [OperationContract]
    string ThrowsFaultException();

    [OperationContract(IsOneWay = true)]
    void ThrowsFaultExceptionOneWay();

    [OperationContract]
    [FaultContract(typeof(IndexOutOfRangeException))]
    string ThrowsTypedCLRFaultException();

    [OperationContract]
    [FaultContract(typeof(MyCustomException))]
    string ThrowsTypedCustomFaultException();
}

 

2. Change the implementation file, Service1.cs to this,

public class Service1 : IService1 {
    public string ThrowsCLRException(){
        // Throw a CLR exception
        throw new IndexOutOfRangeException("ThrowsCLRException: IndexOutOfRangeException");
    }

    public void ThrowsCLRExceptionOneWay() {
        throw new IndexOutOfRangeException("ThrowsCLRExceptionOneWay: IndexOutOfRangeException");
    }

    public string ThrowsFaultException() {
        // Throw a fault exception
        throw new FaultException("ThrowsFaultException: FaultException", new FaultCode("CalculationError"));
    }

    public void ThrowsFaultExceptionOneWay() {
        throw new FaultException("ThrowsFaultExceptionOneWay: FaultException");
    }

    public string ThrowsTypedCLRFaultException() {
        // Throw a typed FaultException with a CLR exception
        throw new FaultException<IndexOutOfRangeException>(new IndexOutOfRangeException(), 
            new FaultReason("ThrowsTypedCLRFaultException: FaultException<IndexOutOfRangeException>"));
    }

    public string ThrowsTypedCustomFaultException() {
        // Throw a typed FaultException with a custom exception
        MyCustomException fault = new MyCustomException();
        fault.MyMessage = "ThrowsTypedCustomFaultException: FaultException<MyCustomException>";
        throw new FaultException<MyCustomException>(fault, new FaultReason("No reason!"));
    }
}

3. And the Main method to,

using (ServiceHost host = new ServiceHost(typeof(WcfServiceLibrary1.Service1), new Uri("http://localhost:8000/HelloWCF"))) {
    // Set up a service endpoint [Contract, Binding, Address]
    host.AddServiceEndpoint(typeof(WcfServiceLibrary1.IService1), new BasicHttpBinding(), "HelloWCF");

    // Enable metadata exchange
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpGetEnabled = true };
    host.Description.Behaviors.Add(smb);

    // Enable exeption details
    ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
    sdb.IncludeExceptionDetailInFaults = false;

    host.Open();

    Console.WriteLine("Ready...");
    Console.ReadLine();
}

4. Now run the service app and update the Service Reference in the client console app. Change the client code to,

EndpointAddress epoint = new EndpointAddress("http://localhost:8000/HelloWCF/HelloWCF");
ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new BasicHttpBinding(), epoint);

using (proxy as IDisposable) {
    
    // Even though a CLR exception is thrown; WCF converts it to a FaultException on the client
    try { proxy.ThrowsCLRException(); }
    catch (FaultException fex) { Console.WriteLine("1. " + fex.Message); }
    Console.WriteLine();

    // One-way - no reply message - nothing gets caught
    try { proxy.ThrowsCLRExceptionOneWay(); }
    catch (FaultException fex) { Console.WriteLine("2. " + fex.Message); }
    catch (Exception ex) { Console.WriteLine("2. " + ex.Message); }
    Console.WriteLine();

    // WCF service threw a FaultException
    try { proxy.ThrowsFaultException(); }
    catch (FaultException fex) {
        if (fex.Code.Name == "CalculationError") { Console.WriteLine("3. " + fex.Message); }
    }
    Console.WriteLine();

    // One-way - no reply message - nothing gets caught
    try { proxy.ThrowsFaultExceptionOneWay(); }
    catch (FaultException fex) { Console.WriteLine("4. " + fex.Message); }
    catch (Exception ex) { Console.WriteLine("4. " + ex.Message); }
    Console.WriteLine();

    // WCF service threw a typed FaultException - note the catch
    try { proxy.ThrowsTypedCLRFaultException(); }
    catch (FaultException<IndexOutOfRangeException> fex) { Console.WriteLine("5. " + fex.Message); }
    Console.WriteLine();

    // WCF service threw a typed FaultException - note the catch
    try { proxy.ThrowsTypedCustomFaultException(); }
    catch (FaultException<ServiceReference1.MyCustomException> fex) 
    { 
        Console.WriteLine("6. Reason:" + fex.Message);
        Console.WriteLine(fex.Detail.MyMessage);
    }
}
Console.ReadLine();

5. When you run this code, you’ll see,

image

Some things to note,

1. There is no output for ThrowsCLRExceptionOneWay and ThrowsFaultExceptionOneWay obviously.

2. In the ThrowsCLRException case – a CLR exception is caught as a FaultException, but there are no associated details about the exception.

3. We can throw a typed FaultException using a <CLRException> or a <CustomException>.

Now to see CLR exception details, in step3 change to,

sdb.IncludeExceptionDetailInFaults = true;

Now running the client you should get the CLR exception details,

image

The last thing to discuss is that throwing a CLR exception will actually kill the WCF session/channel, but a FaultException will not. Since this example was written using BasicHttpBinding which has no concept of WCF sessions, everything worked. To see the problem change the binding on both the host app and the client app to use WSHttpBinding. Now running the service and client results in,

image

because the WCF session was crashed by the thown CLR exception (even though it shows up as a FaultException on the client). This can be fixed by designing your service code to catch all CLR exceptions and always throwing FaultException from the service.

WCF Simplified Part 4: Comparing the Request/Reply and One-Way Patterns

Today we’ll look at the differences between a request/reply pattern and a one-way pattern, using the simple WCF service application and client console app that we built in the introductory post.

1. In the WCF service app, in file IService1.cs, add a new OperationContract called PostData,

[ServiceContract]
public interface IService1 {
    [OperationContract]
    string GetData(int value);

    // Note the void return value
    [OperationContract]
    void PostData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
}

2. Now in the file Service1.cs, add the implementation code,

public void PostData(int value) {
    // Do something with 'value' – simulate a 10-sec long process
    Thread.Sleep(10000);

    // Then throw an exception
    throw new Exception("Something went very wrong in the service library!");
}

3. And in the Main method in Program.cs,

using (ServiceHost host = new ServiceHost(typeof(WcfServiceLibrary1.Service1), new Uri("http://localhost:8000/HelloWCF"))) {
    // Set up a service endpoint [Contract, Binding, Address]
    host.AddServiceEndpoint(typeof(WcfServiceLibrary1.IService1), new BasicHttpBinding(), "HelloWCF");

    // Enable metadata exchange
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpGetEnabled = true };
    host.Description.Behaviors.Add(smb);

    // Enable exception details
    ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
    sdb.IncludeExceptionDetailInFaults = true;

    host.Open();

    Console.WriteLine("Ready...");
    Console.ReadLine();
}

4. Now on the client app, delete and re-add (or update) the Service Reference to the WCF service host. Then change the code in Main to,

EndpointAddress epoint = new EndpointAddress("http://localhost:8000/HelloWCF/HelloWCF");
ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new BasicHttpBinding(), epoint);

using (proxy as IDisposable) {
    // Call the WCF service using the proxy
    Console.WriteLine("Start:" + DateTime.Now.ToLongTimeString());
 
    try { proxy.PostData(42); }
    catch (FaultException fex) { Console.WriteLine(fex.Message); }
 
    Console.WriteLine("Done:" + DateTime.Now.ToLongTimeString());
}
Console.ReadLine();

5. Now running the client app, you should see that the client blocks for 10sec and then shows the exception message,

image

This shows that in a request/reply pattern the client blocks till it gets the reply from the service, and also the client is able to process the FaultException. Now let’s see what happens in a one-way pattern, simply change the PostData OperationContract to add a IsOneWay attribute,

    [ServiceContract]
    public interface IService1 {
        [OperationContract]
        string GetData(int value);

        // Add the IsOneWay attribute
        [OperationContract(IsOneWay=true)]
        void PostData(int value);

        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);
    }
 

Now when you run the client (remember to delete and add the Service Reference – update should work too), you’ll see,

image

The client didn’t block on the call and returned immediately. It was also unable to process the FaultException because there is no reply message in a one-way pattern. To see this, try the following implementation of PostData,

public void PostData(int value) {
    // Do something with 'value' – simulate a 10-sec long process
    //Thread.Sleep(10000);

    // Just throw an exception – a one-way implementation will not catch this
    throw new Exception("Something went very wrong in the service library!");
}

WCF Simplified Part 3: Using the WCF Duplex MEP

Now that we are up and running with WCF, lets try another simple example. We have already seen how to use the request/reply pattern in the introductory post. In this post we’ll how to use the Duplex (one-way) pattern. This will be an extension to the two Visual Studio 2010 solutions we built in the previous introductory post.

1. Open the WCF host application (remember to run Visual Studio 2010 as admin). Add the following code to the IService1.cs file,

// Add the callback interface
public interface ICallback {
    [OperationContract(IsOneWay = true)]
    void MyCallbackFunction(string callbackValue);
}

// Add the SessionMode and CallbackContract
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallback))]
public interface IService1 {
    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
}

// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract]
public class CompositeType {
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue {
        get { return stringValue; }
        set { stringValue = value; }
    }
}

2. In Service1.cs change the GetData method to call a client method,

public string GetData(int value) {
    // Get the client callback method...
    ICallback callBack = OperationContext.Current.GetCallbackChannel<ICallback>();

    // Call the client method from the server!
    callBack.MyCallbackFunction(value.ToString());
            
    return string.Format("You entered: {0}", value);
}

 

3. And in the Program.cs change the Main method to use WsDualHttpBinding instead of BasicHttpBinding,

using (ServiceHost host = new ServiceHost(typeof(WcfServiceLibrary1.Service1), new Uri("http://localhost:8000/HelloWCF"))) {
    // Set up a service endpoint [Contract, Binding, Address]
    host.AddServiceEndpoint(typeof(WcfServiceLibrary1.IService1), new WSDualHttpBinding(), "HelloWCF");

    //Enable metadata exchange
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpGetEnabled = true };

    host.Description.Behaviors.Add(smb);
    host.Open();

    Console.WriteLine("Ready...");
    Console.ReadLine();
}

 

4. We are all set on the server side, for the client open the client console application we built in the previous introductory post. Delete the Service Reference and re-add the Service Reference to the WCF host application (run the host first). Then change the code in Program.cs to,

public class Callbacks : ServiceReference1.IService1Callback {
    void ServiceReference1.IService1Callback.MyCallbackFunction(string callbackValue) {
        Console.WriteLine("Server called this client method: {0}", callbackValue);
    }
}

class Program {
    static void Main(string[] args) {
        // Create an endpoint
        EndpointAddress epoint = new EndpointAddress("http://localhost:8000/HelloWCF/HelloWCF");
        DuplexChannelFactory<ServiceReference1.IService1> dualFactory 
                          = new DuplexChannelFactory<ServiceReference1.IService1>(new Callbacks(), new WSDualHttpBinding(), epoint);

        ServiceReference1.IService1 proxy = dualFactory.CreateChannel();
        using (proxy as IDisposable)
        {
            // Call the WCF service using the proxy
            string s = proxy.GetData(42);
            Console.WriteLine(s);
        }
        Console.ReadLine();
    }
}

5. That’s it! We are all set now, running the client app, you should see,

image