Request/Response Service Layer: Exposing The Service Layer Through WCF

14 commentsWritten on November 11th, 2009 by
Categories: Request/Response Service Layer

Note: This post is part of a series. Be sure to read the introduction here.

The previous posts in the series already covered everything there is to know about implementing the Request/Response Service Layer (RRSL). In this post, we'll focus on hosting the RRSL through WCF and the next posts will show you how you can actually use the RRSL from your clients (both synchronously and asynchronously). First, let's take another look at what our service actually exposes:

    public interface IRequestProcessor : IDisposable
    {
        Response[] Process(params Request[] requests);
    }

This interface doesn't contain the necessary WCF attributes, but we'll get to that in a minute. The first thing that we need to take care of is to make sure that WCF's DataContractSerializer can handle the derived Request and Response types without us having to declare the KnownType attribute on each derived class. Luckily for us, WCF has a ServiceKnownType attribute where you can define a class that will provide the KnownTypes to WCF so it can properly serialize and deserialize them. But first, we'll need a KnownTypeProvider class:

    public static class KnownTypeProvider
    {
        private static HashSet<Type> knownTypes = new HashSet<Type>();
 
        public static void ClearAllKnownTypes()
        {
            knownTypes = new HashSet<Type>();
        }
 
        public static void Register<T>()
        {
            Register(typeof(T));
        }
 
        public static void Register(Type type)
        {
            knownTypes.Add(type);
        }
 
        public static void RegisterDerivedTypesOf<T>(Assembly assembly)
        {
            RegisterDerivedTypesOf(typeof(T), assembly);
        }
 
        public static void RegisterDerivedTypesOf<T>(IEnumerable<Type> types)
        {
            RegisterDerivedTypesOf(typeof(T), types);
        }
 
        public static void RegisterDerivedTypesOf(Type type, Assembly assembly)
        {
            RegisterDerivedTypesOf(type, assembly.GetTypes());
        }
 
        public static void RegisterDerivedTypesOf(Type type, IEnumerable<Type> types)
        {
            knownTypes.UnionWith(GetDerivedTypesOf(type, types));
        }
 
        public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
        {
            return knownTypes;
        }
 
        private static List<Type> GetDerivedTypesOf(Type baseType, IEnumerable<Type> types)
        {
            return types.Where(t => !t.IsAbstract && t.IsSubclassOf(baseType)).ToList();
        }
    }

Now we just have to make sure that all of our Request and Response types are registered before the application is initialized. You basically need to make sure that the following code is called before the first request is sent to the service:

        private static void RegisterRequestAndResponseTypes(Assembly assembly)
        {
            KnownTypeProvider.RegisterDerivedTypesOf<Request>(assembly);
            KnownTypeProvider.RegisterDerivedTypesOf<Response>(assembly);
        }

Now we can actually define our Service Contract. Instead of putting the WCF attributes on the IRequestProcessor interface, i decided to put a different interface next to it. The reason i chose to go that route is merely to make sure that the actual IRequestProcessor interface and it's implementation isn't tied to WCF. And that's why we have the IWcfRequestProcessor interface:

    [ServiceContract]
    public interface IWcfRequestProcessor
    {
        [OperationContract(Name = "ProcessRequests")]
        [ServiceKnownType("GetKnownTypes", typeof(KnownTypeProvider))]
        Response[] Process(params Request[] requests);
    }

This is a 'properly' defined WCF Service Contract, and we also make sure that WCF knows where to get the list of known types by using the ServiceKnownType attribute and hooking it to our KnownTypeProvider. The implementation of this service contract looks like this:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class WcfRequestProcessor : IWcfRequestProcessor
    {
        public Response[] Process(params Request[] requests)
        {
            return IoC.Container.Resolve<IRequestProcessor>().Process(requests);
        }
    }

As you can see, there's nothing to it... The implementation of the Process method simply resolves the real IRequestProcessor implementation, calls the Process method and returns the Responses. That's it.

Now we still need to host the service somewhere. I always host it through IIS but you can use any of the other WCF hosting options as well. In case you're hosting it in a Web Application, add a RequestProcessorService.svc file to your Web Application with the following content:

<%@ ServiceHost Language="C#" Debug="true" Service="The.Namespace.Of.Your.WcfRequestProcessor" %>

In your web.config (or app.config if you're hosting it yourself or through a windows service), add the following block:

  <system.serviceModel>
 
    <services>
      <service name="The.Namespace.Of.Your.WcfRequestProcessor" behaviorConfiguration="RequestProcessorBehavior">
        <endpoint address="" contract="The.Namespace.Of.Your.IWcfRequestProcessor"
        binding="customBinding" bindingConfiguration="binaryHttpBinding"/>
      </service>
    </services>
 
    <bindings>
      <customBinding>
        <binding name="binaryHttpBinding" receiveTimeout="00:30:00" sendTimeout="00:30:00" >
          <binaryMessageEncoding>
            <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647" />
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
        </binding>
      </customBinding>
 
       <basicHttpBinding>
          <binding name="basicHttpBinding" receiveTimeout="00:30:00"
             sendTimeout="00:30:00" maxReceivedMessageSize="2147483647">
             <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647" />
             <security mode="None" />
          </binding>
       </basicHttpBinding>
    </bindings>
 
    <behaviors>
      <serviceBehaviors>
        <behavior name="RequestProcessorBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <serviceThrottling maxConcurrentCalls="100" maxConcurrentInstances="100" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
 
  </system.serviceModel>

I included two binding configurations in this example, but you obviously only need one. In this case, the service endpoint is configured to use the custom binaryHttpBinding. This uses binary XML which (in most cases) is more compact and thus, has less bandwidth overhead. If you want to use a regular basicHttpBinding, just modify the service endpoint configuration to point to the other binding. Obviously, you can use wsHttpBinding or any other binding as well if you want to.

And that is it. That's really all you need to do to host the RRSL through WCF. You'll never have to edit or add anything to the xml configuration as you add more functionality to your service layer. It works now, and it will keep working.

Just keep in mind that you need to register the Request and Response types with the KnownTypeProvider before the service receives the first request ;)

  • http://thisoldcode.net Aaron Fischer

    So it doesn’t bother you to use the container as a global service locator?

  • http://davybrion.com Davy Brion

    @Aaron

    if that is the only place where it’s used like that, then why would i? what would be the difference between doing it yourself, or having some other kind of infrastructural library doing it for you?

    if you were to do that all over the place, then yeah it would definitely bother me. But if it’s only in one place, in a piece of infrastructural code that you never need to change, then i really don’t think there’s any _real_ problem there.

  • Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #475

  • Luc

    If I’m not mistaken, the known types must be registered before the ServiceHost is created because the GetKnownTypes is called from the CreateDescription method of the ServiceHost.

    If the assemblies containing the requests and responses are shared between the client and the server, using the NetDataContractSerializer can help since known types must not be declared for operations.
    But you need to tweak the serialization in the fault channel since it keeps using the DataContractSerializer to serialize FaultException

    • http://davybrion.com Davy Brion

      @ Luc

      it might be… i thought that registering the know types before the first service request is received was enough… it might be because i primarily use it with IIS and maybe it creates the service host as late as possible or something (as in: when the first requests is received)

  • Chris

    Thanks for the great article, I found it very useful. Just an FYI I modified your KnownTypeProvider class in this way


    private static IEnumerable GetDerivedTypesOf(Type baseType, IEnumerable types)
    {
    //return types.Where(t => !t.IsAbstract && t.IsSubclassOf(baseType)).ToList();
    return types.Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToList();
    }

    This way I can send do something like
    KnownTypeProvider(someAssembly);

  • Chris

    oops.. I meant


    KnownTypeProvider.RegisterDerivedTypesOf(assembly);

  • Chris

    KnownTypeProvider.RegisterDerivedTypesOf(assembly);

  • Kushal

    Hi,
    I am learning WCF and read your series of articles.
    i would like to create WCF service in which client can request with xml.
    Server get that xml, parse it, validate it and then send them response.
    In client xml there will be username,password and clientid that will differentiate the client with other client so server can easily recognize the client.
    After that server send them back response in xml.
    The server should be designed in a way that can handle multiple request and response to client.
    How I can go about this by request/reply message.
    Thanks

  • http://davybrion.com Davy Brion

    @Kushal

    why would you send xml yourself?

    it’s far easier to just define request/response types with the properties of the data that needs to be included in your calls… WCF will serialize/deserialize to xml (well, soap actually) automatically for you

  • Kushal

    @Davy

    Thank for the reply. To be honest I am new to WCF and not have experience in developing web service and wcf service.
    You mean to say I just have to create object with the details i want to send, then wcf will automatically serialize/deserialize it to xml.

  • scott mcfadden

    Great article. I modified your code to resolve the child types via IOC. This better facilitates loose coupling between assemblies. I had to write a custom attribute – RegisterKnownServiceType. This attribute is used in conjunction with your KnownTypeProvider. Here is an example of how you would use the modified version:

    [ServiceContract(Namespace = "MyServices")]
    public interface ICarService
    {
    [OperationContract]
    //[ServiceKnownType(typeof(PriusCarInfo))] –old / static way of providing type
    //[ServiceKnownType(typeof(LexusCarInfo))] –old / static way of providing type
    [ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypeProvider))] –factory will use IOC to resolve types for all discovered RegisterServiceKnownType attributes
    [RegisterServiceKnownType(typeof(ICarInfo))] –custom attribute used by ServiceKnownTypeProvider to dynamically register types via IOC
    ICarInfo GetCarInfo(int carId);
    }

  • Scott McFadden

    Update. I just read from Bustamante’s book where you can ditch the ServiceKnownType attribute altogether and just statically map the known types via config file:

    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.configuration.datacontractserializersection.aspx

  • Eric Roth

    Have you come up with any way to elegantly handle generic requests, i.e Request. I am new to WCF and have recently found out that this type of thing is not supported, however we found that would would greatly decrease the code we would need to write in certain cenarios if generics would work?