Saturday, August 19, 2017

Microsoft 70-487: Create a WCF service

Exam Objectives

Create contracts (service, data, message, callback, and fault); implement message inspectors; implement asynchronous operations in the service


Quick Overview of Training Materials


Create Contracts


Creating a WCF service can involve a number of different contracts.  First and formost among these is the service contract, without which you really don't have a service.  A service contract is often defined by creating an interface (though you can create it directly on the implementing class as well), decorated with a [ServiceContract] attribute, and defining methods on that interface.  These methods will become the operations on the service, and are decorated with the [OperationContract] attribute.

The operation contract has several properties that can be set to modify it's behavior.  IsInitiating and IsTerminating are boolean properties that indicate whether the operation is the first or last operation to be called on the service, respectively.  The IsOneWay property indicates whether the operation returns anything (if set to "true" is must return void).  Action controls which requests are dispatched to the operation (this can be set to "*", which will dispatch all unmatched requests to this operation, handy if you are building a relay service).

Below is a simple example of a service contract defined on an interface, with a single operation "speak".  The contract is then implemented by the CatService class:

    [ServiceContract]
    public interface ICatService
    {
        [OperationContract]
        string speak();
    }

    public class CatService : ICatService
    {
        public string speak()
        {
            return "Meow!";
        }
    }


The above service is not terribly interesting, because it just returns a simple string, and most application will need to return more complicated data.  Complex types can be transmitted by the service through the use of serialization.  By default, WCF uses the DataContractSerializer.  Data contracts are specified by decorating the class with [DataContract], and "opting in" each of the fields by using the [DataMember] annotation.  Types marked with the [Serializable] can also be used with DataContractSerializer.  The following two complex types exhibit the same behavior:

    [DataContract]
    public class Kitten
    {
        public Kitten(string name, string color)
        {
            this.name = name;
            this.color = color;
        }

        [DataMember]
        string name;
        [DataMember]
        string color;
    }

    [Serializable]
    public class SKitten 
    {
        public SKitten(string name, string color)
        {
            this.name = name;
            this.color = color;
        }
        
        string name;
        string color;
    }


It is also possible to use another serializer, XmlSerializer.  This is an older serializer, and is most useful in situations where you need backward compatibility with .asmx services or will be integrating with non-WCF services (at least according to Aaron Skonnard in "WCF Design Concepts").  Where using DataContractSerializer required nothing more than using the serializable type in our method signature, we have to explicitly tell WCF to use XmlSerializer by using the [XmlSerializerFormat] attribute on the service or operation level.  The code below shows one operation using XmlSerializer, while the other operations use DataContractSerializer:

    [ServiceContract]
    public interface ICatService
    {
        [OperationContract]
        string speak();

        [OperationContract]
        Kitten haveKitten();

        [XmlSerializerFormat]
        [OperationContract]
        XKitten haveXKitten();

        [OperationContract]
        SKitten haveSKitten();
    }


The XKitten class is annotated differently, and has different requirements in order to be successfully serialized.  For one, it must have a noarg constructor.  Also, the data members to be included in the xml payload must have public visibility:

    public class XKitten
    {
        public XKitten() { }

        public XKitten(string name, string color)
        {
            this.name = name;
            this.color = color;
        }

        [XmlAttribute]
        public string name;
        [XmlAttribute]
        public string color;
    }


On interesting consequence of using this serializer is that the XKitten type does not show up in the generated service definition, while the Kitten and SKitten (which both are serialized by the DataContractSerializer) do show up:



It is possible to control the exact shape of the payload through the use of message contracts.  The class that will define the message is decorated with the [MessageContract] attribute.  There are several properties that can be set to change the way the message is constructed:
  • IsWrapped - when set to false, the message body elements will not be wrapped
  • WrapperName - the default wrapper name is the class name, this allows customization
  • WrapperNamespace - set a custom XML namespace for the wrapper element
  • ProtectionLevel - specify whether the message  is encrypted, signed, or both (or neither).  Must be supported by the binding and properly configured.

The [MessageHeader] and [MessageBodyMember] attributes control what part of the SOAP envelope the properties are written to.  These attributes also allow the name and namespace to be changed. ProtectionLevel can also be set on these individual members. Below is the Kitten class modified to act as a message contract:


    [MessageContract(WrapperName = "CuteKitty")]
    public class Kitten
    {
        public Kitten(string name, string color)
        {
            this.name = name;
            this.color = color;
        }

        [MessageHeader]
        string id = Guid.NewGuid().ToString();

        [MessageBodyMember]
        string name;
        [MessageBodyMember]
        string color;
    }

This is the result of calling the applicable service:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:id xmlns:h="http://tempuri.org/">c858c256-9866-4637-97ba-bc4a2539cd5e</h:id>
  </s:Header>
  <s:Body>
    <CuteKitty xmlns="http://tempuri.org/">
      <color>Calico</color>
      <name>Maxine</name>
    </CuteKitty>
  </s:Body>
</s:Envelope>

Whereas it used to look like this:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <haveSKittenResponse xmlns="http://tempuri.org/">
      <haveSKittenResult xmlns:a="http://schemas.datacontract.org/2004/07/PetService" 
                         xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:color>Grumpy</a:color>
        <a:name>Dingus</a:name>
      </haveSKittenResult>
    </haveSKittenResponse>
  </s:Body>
</s:Envelope>


When exceptions are thrown in a WCF service, what gets sent back to the client is a SOAP Fault. Intuitively this makes sense, since we can't always be sure what kind of system is calling our service. A Java client calling our service would have no knowledge of CLR based exceptions.  The point at which we can exercise some control is in specifying a fault contract.  The fault contract lets us specify a serializable type (which may or may not be a type of Exception) that represents the error.

What follows are three operations which deal with faults differently.  The first throws a bare exception, which WCF treats as "uncaught" and treats in a uniform way.  By default the fault returned by WCF in this case will contain very little information about the actual exception (basically none other than it happened).  This is done to avoid exposing system internals to hackers.  The second two operations use FaultContract attributes to specify which type will represent the error.  These are wrapped in a FaultException and thrown... the exception is serialized and returned in the fault response to the client.


    {...
        [OperationContract]
        void throwUncaught();

        [OperationContract]
        [FaultContract(typeof(Exception))]
        void throwCaught();

        [OperationContract]
        [FaultContract(typeof(KittyPoop))]
        void throwCustom();
    }


    {...
        public void throwUncaught()
        {
            throw new Exception("OH NOES YOU DIDN'T CATCH ME!!!");
        }

        public void throwCaught()
        {
            var ex = new Exception("Well, I exploded, but at least you wrapped me...");
            throw new FaultException<Exception>(ex, "Reason...");
        }

        public void throwCustom()
        {
            var ex = new KittyPoop("stanky!", "small");
            throw new FaultException<KittyPoop>(ex, "Kitty pooped on the carpet... ");
        }
   }


The call to "throwUncaught" return a generic fault that just says something went wrong, and gives suggestions for turning on more detailed error messages:


The call to "throwCaught" returns the serialized Exception object:


The last call, to "throwCustom", serves to demonstrate that the FaultContract need not be implemented for an Exception type... it can be any serializable type:


The last kind of contract is the callback or "duplex" contract.  This contract is used when you are creating a callback style operation, where the client implements the callback contract (which is basically just another service contract).  The [ServiceContract] attribute includes a parameter for "CallbackContract" that is assigned the type of the callback contract, and in so doing this creates a relationship between the two contracts.

In order to use the callback contract, you must use a bidirectional binding.  Tcp and named pipes are already bidirectional, but to use this functionality over the HTTP protocol, you have to use the WSDualHttpBinding.  The client will need to use a DuplexChannelFactory when creating the channels (SvcUtil.exe creates a duplex capable proxy as well).  It is worth noting that WSDualHttpBinding is not interoperable, as explained in this post on the Sun Oracle blog.

Aaron Skonnard's "WCF Design Concepts" PluralSight course includes a demo of using callback contracts in a chat service.  I implemented it (code can be found on GitHub)... it's more involved than one quick snippet so I'm not going to paste it here, but I did play around with customizable console coloring and registration collision avoidance that was pretty fun and made for a slightly more interesting demo:


The critical bit of code is to create the callback contract (which really is just a standard WCF contract), then set that contract as the CallbackContract type on the main service contract.  In the client application I've created a "localService" implementing the callback contract, which I then pass to a DuplexChannelFactory to create the channel with the server.



Implement Message Inspectors


Message Inspectors allow you to tie in to the WCF message processing pipeline in order to examine and even modify incoming message.  This is accomplished by creating a class that implements IDispatchMessageInspector, and IClientMessageInspector, which are targeted at the server and client respectively.  Each interface includes two methods:  AfterReceiveRequest and BeforeSentReply, which correspond to before and after the message is processed.

In a request/reply scenario, AfterReceiveRequest is called before the message is processed, while BeforeSentReply is called after the message is processed but before the response is sent back to the client.  In one-way scenarios (as in a duplex connection), there is not reply, so it is just called after the message is processed.  The return value of AfterReceiveRequest is passed into BeforeSentReply as a "correlationState" in case these methods need to share data.

One thing to note is that AfterReceiveRequest is called before the message has been deserialized, so the message is a raw WCF message; every manipulation example I found used the XML apis in .NET to work with it.

Once the inspector is created, it is necessary to include it in the application.  The documentation covers how to do this with a behavior extension, which includes a couple of extra steps but in the case of my toy example proved very easy to implement.  I wanted my behavior available on the entire service, so I created a class implementing IServiceBehavior that simply loops through all the message inspector collections for each endpoint and adds the inspector.  I then created a class implementing the BehaviorExtensionElement class that simply returns the type and instance of this behavior.  With these classes in place, I could add the class strictly using configuration.

The Microsoft documentation for Message Inspectors includes a "schema validation" example, but I thought it would be more interesting to extend the functionality of the color chat app, so I used IDispatchMessageInspector to create a "ProfanityInterceptor" that does a find/replace on obscenities in the message text.  The results were rather amusing, though surprisingly tricky to pull off.  Both of the referenced blog posts on "how to modify messages in message inspectors" gave me pieces that I need. My sample code is on GitHub.





Implement Asynchronous Operations


Calling operations asynchronously from a generated client (add service reference) is as simple as configuring the service reference correctly.  Right click the service reference, choose "Configure Service Reference..." and select "Allow generation of asynchronous operations. Easy peasy.  The style of async ops you get depends on which option you select.




Selecting "task-based" operations will generate the synchronous methods with names matching the service contract, as well as async operations ending with "Async", which with return a task typed to the return type of the synchronous service:




Selecting "asynchronous operations" generates the same methods (though the "Async" version of methods are now void return types).  Additionally, there are methods names prepended with "Begin" and "End".  So "haveKitten()" will also have a "haveKittenAsync", as well as "BeginhaveKitten" and "EndhaveKitten" (I hope you'll forgive my use of Java method naming conventions):



Looking at the asynchronous patterns document, the Begin-End pattern and the -Async + event patterns exposed by the "Generate asynchronous operations" are not the recommended style of doing async any more.  Instead, Microsoft recommends using the Task based approach.  To implement a Task based service implementations, add the "async" keyword to the method and change the return type to Task<T> instead of T.  This will be transparent to the client, which will still see the return type as T.

Frankly, I don't think making service operations "asynchronous" with the task based pattern actually works.  I messed around quite a bit comparing synchronous calls to async calls, and it seems that setting the instance context mode and concurrency mode actually have much more to do with how threads are utilized.  The basis of my experiment was this:

Two methods, on is async, the other synchronous, returning the same type of result, and both call Thread.Sleep() for some random interval.  I call each 15 times in a loop asynchronously from the client.  The tasks are stored in a dictionary and checked periodically until they've completed, at which point the results are printed to the console.

With this setup, I actually wasn't able to get the two methods to behave differently!  With an HTTP binding, by default the instance mode is "PerCall", so every request was getting it's own instance of the service anyway.  If I set it to "Single", by default the async methods are called one at a time as they finish, so the result is the same as if they were synchronous.  Changing the Concurrency mode to "multiple" make this act the say I would expect, but then the synchronous call just gets called by multiple threads and again the results are the same.  I was able to get the expected result (async ran in parallel while the synchronous method ran sequentially) by added a lock into the synchronous method, but this felt kind of like a cheat.  Ultimately I'm skeptical about the value of using async and await in service operations vs just managing the way the service is instantiated.  Maybe I would just have to see a non-toy example.  Below are the two service calls:



public async Task<Kitten> taskKitten()
{
    return await Task.Factory.StartNew(() => {
        int interval = Rand(200, 2000);
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("Sleeping for " + interval + "ms on thread" + 
            Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(interval);
        return new Kitten("AsyncKitty (" + Thread.CurrentThread.ManagedThreadId + ")", 
            "so lazy, slept for " + interval + "ms");
    }, TaskCreationOptions.LongRunning);
}

public Kitten syncKitten()
{
    int interval = 0;

    lock (this)
    {
        interval = Rand(200, 2000);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Sleeping for " + interval + "ms on thread" + 
            Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(interval);
    }

    return new Kitten("SyncKitty (" + Thread.CurrentThread.ManagedThreadId + ")", 
        "so lazy, slept for " + interval + "ms and blocked!");
}



It's a spurious example, because locking on a Singleton object is always going to create a synchronization bottleneck, and the exact same thing happens if I call lock(this) from within the task body.  So I really think that the lesson is "don't bother with async services".  Now, services that do a callback are another matter, and would be a sensible way of doing asynchronous workflows.



The MSDN article on implementing an asynchronous service operation used the IAsyncResult pattern, yet all the literature I found on the subject said that the "task" based method is what is preferred and recommended by Microsoft (the article is dated March 2017 so go figure).

No comments:

Post a Comment