Tuesday, December 21, 2010

WCF Best Practices

  1. Separate Contracts from Service implementation. For example:
    img1
    and the implementation:
    img2
    Even better is having the service instantiated using an IoC container:
    <%@ ServiceHost Language="C#" Debug="true" Service="JobService" Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %>
    That way you can inject every config to the service.

  2. Manipulate headers in requests and responses. This way your services could benefit by using metadata for the call and pass language, culture, client id, session values, etc., between calls. For this purpose I use WCFExtras.
  3. Use Faults. This is an example of fault declarations:
    [ServiceContract(Name = "JobService", Namespace = NS.ServiceContract)]
    [SoapHeaders]
    public interface IJobService
    {
      [OperationContract]
      [FaultContract(typeof(HeaderNullFault), Name = "HeaderNullFault")]
      [FaultContract(typeof(ArgumentOutOfRangeFault), Name = "ArgumentOutOfRangeFault")]
      GetJobsByManagerResult GetJobsByManagerId(GetJobsByManagerInfo getJobsByManagerInfo);
    
      [OperationContract]
      [FaultContract(typeof(HeaderNullFault), Name = "HeaderNullFault")]
      [FaultContract(typeof(ArgumentOutOfRangeFault), Name = "ArgumentOutOfRangeFault")]
      GetAllJobsResult GetAllJobs(GetAllJobsInfo getAllJobsInfo);
    
      [OperationContract] 
      [FaultContract(typeof(HeaderNullFault), Name = "HeaderNullFault")]
      [FaultContract(typeof(ArgumentOutOfRangeFault), Name = "ArgumentOutOfRangeFault")]
      GetJobsByLocationResult GetJobsByLocation(GetJobsByLocationInfo getJobsByLocationInfo);
    }

    I strongly recommend reading chapter 6 “Faults” of the book “Programming WCF” by Juval Löwy.
  4. Use namespaces everywhere (ServiceContract, DataContract, ServiceBehavior). This helps with versioning and solve ambiguities with complex types when the application growth and interacts with other services. I follow the convention of naming the namespace: http://company/application/year/month (ex. http://mycompany/harmony/2010/08).
  5. Use a message oriented design in the service interface design. Each service should receive a message and it should output a message. Do this even for the calls that are originally void. This way when future versions come up it wont require a change in the service interface (hopefully), as each additional parameter would be translated to adding optional properties to the input message and each additional data we want to return would result in new properties in the result message. This would guarantee backwards compatibility as long as you don’t change property names, types, etc.
  6. Use explicit values for the ServiceContract and DataContract attributes. The order of the fields matters when serializing and de-serializing between different platforms (ex. .NET and Java).img4
  7. Use DTOs (Data Transfer Objects) to exchange data in and out of the service. Map the DTOs with Domain Objects. For this I have used extensions methods and the Adapter Pattern, but I have lately switched to using AutoMapper as it does the repetitive work for me.
  8. I personally like to separate the WCF configurations away from the Web.config. This way I can edit behaviors, bindings, clients or services sections directly for each environtment. Also, this ease the deployment process.
    img5
    The only file that changes for each environment is the web.config that points to dev, local, pre or prod. An example of how this can be done in the web.config:
    <system.serviceModel>
    <services configSource="Configs\local\system.serviceModel.services.xml"/>
    <behaviors configSource="Configs\local\system.serviceModel.behaviors.xml"/>
    <bindings configSource="Configs\local\system.serviceModel.bindings.xml"/>
    <client configSource="Configs\local\system.serviceModel.clients.xml"/>
    </system.serviceModel>

    If you replace local for dev it would be pointing to the dev configs. On each deployment I copy all files but the web.config. That way the process is very straightforward and safe from errors.

  9. Use other protocols for transferring data besides SOAP. I would suggest JSON(use this link to learn how to setup support for JSON on the service calls: http://www.west-wind.com/weblog/posts/164419.aspx). Also another protocol even more efficient than JSON is the one Google uses for their web services, http://code.google.com/p/protobuf/ (the implementation for .NET is here thanks to Marc Gravell).
  10. Tweak and optimize the services. I recommend reading the following material: http://weblogs.asp.net/sweinstein/archive/2009/01/03/creating-high-performance-wcf-services.aspx and this other http://www.codeproject.com/KB/webservices/quickwins.aspx.
  11. Consider using the Agatha project in the overall design of your services so you can enable batch execution of services and because it dramatically diminish duplicated code. You can start with the blog post that was the motivation of the project (http://davybrion.com/blog/2009/07/why-i-dislike-classic-or-typical-wcf-usage/).