Archive

Archive for the ‘WCF’ Category

Logging all unhandled exceptions in WCF with Log4Net

September 5, 2012 2 comments

Scientific studies have shown that all applications contain a number of bugs. It is naïve to think that your applications would be bug free. When bugs occur is important that the necessary information gets logged to reproduce and fix the issues.

Assuring that all unhandled exceptions get logged in crucial. If you don’t now all the unhandled exceptions you will be blind for what happens in the production environment impacting your end-users. WCF produces the necessary hook to now when an unhandled exception has occurred in your services: the IErrorHandler. In this short article, I will demonstrate a simple implementation that logs these exceptions with Log4Net.

The most important component in this example is ofcourse the error handler implementation itself. The method “HandleError” gets called for every unhandled exception by the WCF stack. This method has to indicate with its return value if it has “handled” the exception or if it remains “unhandled”. In our implementation we just log it with Log4Net and indicate to WCF that it remains unhandled and has to pass the stack further.

public class Log4NetErrorHandler : IErrorHandler
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    public bool HandleError(Exception error)
    {
        log.Error("An unexpected has occurred.", error);

        return false; // Exception has to pass the stack further
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
    }
}

The only thing that remains now is to hook this IErrorHandler implementation into the WCF stack. To do this, we must implement a new service behavior. Below you can find the necessary code.

public class Log4NetServiceBehavior : IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandler = new Log4NetErrorHandler();

        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            channelDispatcher.ErrorHandlers.Add(errorHandler);
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

Next we must implement a Behavior Extension Element to this into WCF stack using the web.config or the app.config

public class Log4NetBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof (Log4NetServiceBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new Log4NetServiceBehavior();
    }
}

Now we can declare in the config that WCF must use our custom service behavior extension.

<system.serviceModel>
<services>
<!-- We define our WCF Services ... -->
</service>
</services>

<behaviors>
<serviceBehaviors>
<behavior>
<log4net />
</behavior>
</serviceBehaviors>
</behaviors>

<extensions>
<behaviorExtensions>
<add name="log4net"
type="MyProject.Log4NetBehaviorExtensionElement, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
Advertisements
Categories: C#, WCF

Caching in WCF Services: Part 2 AppFabric distributed cache

August 28, 2012 6 comments

Introduction

A couple of months ago, I wrote an article about caching in WCF using the in-process memory cache available in the .NET 4.0 framework. This component offers a lightweight caching solution that can easily be integrated in various types of .NET applications.

There are scenarios where a more advanced caching solution is required. The standard .NET 4.0 cache cannot be shared among multiple servers. Every server has its own isolated instance of the cache. Furthermore, it shares memory with the application. A 32-bit application can allocate at maximum 4 GB of RAM (on a 64-bit OS; maximum 2GB of RAM on a 32-bit OS). There can be situations where you want that the cache of a 32-bit app is larger than 2 GB (although these situations are rare).

AppFabric

Microsoft offers a distributed cache as part of Windows Server AppFabric. This cache can be installed on Window Server 2008 and higher. It is also available in the cloud on Windows Azure. The AppFabric cache is a distributed out of process cache; this means that the cache can be shared among multiple servers and applications. Because it runs outside the application process, it can survive application shut downs and it can be larger then 4 GB for 32-bit applications.

You can deploy AppFabric in two ways on your network: on every application server hosting your application or as a separate infrastructure layer independent of your application servers. The lather provides more flexibility in terms of being able to scale your caching and application server layers independent of each other, but it comes at a higher cost of servers and an increased latency to access the cache.

caching_options

Configuring the AppFabric caching server

AppFabric can be download for free from the Microsoft download site. At the moment of writing, the latest version available is AppFabric 1.1. It requires at least Windows Vista or Windows Server 2008. For testing the AppFabric cache, I have setup a Windows Server 2008 R2 VM configured in workgroup modus. I choose to deploy my WCF service, on a separate server.

Prior to the installation of AppFabric, I created a technical user account called “AppFabricCacheUser” that will be used to run the caching service. I also created a network share called “AppFabricCache”. If you would use multiple servers, the same user account must be created on all the servers. These user accounts must have the same password on all servers. Only one server must host a network share for the AppFabric cache. The AppFabric user account must have admin rights on the server hosting the share.

Please note: the configuration of AppFabric is different in case your servers are part of an AD domain. Then you don’t need to setup a network share or separate technical accounts on each server. Most companies should have an AD domain. Please consult the AppFabric MSDN pages for more information. I recommend to only use AppFabric caching in workgroup mode in a sandboxed test environment. Otherwise, always use it in AD mode if possible.

AppFabric consists of two parts, the hosting features and the distributed cache. Because this article is about caching, I only installed the necessary caching features.

setup

After the installation is finished, we must launch the “AppFabric Server Configuration Wizard”. We must specify the Caching Service account we created earlier, the XML Caching Service configuration provider and the file share we created earlier.

configure_workgroup_modus

After this is done, press “next” to continue to following screen. On this screen, we can change the network ports if needed. I choose to stick with the defaults.

configure_cache_node

Now this is done, we can start our cache using the PowerShell. In order to do this, launch the “Caching Administration Windows PowerShell” and enter the following command: Start-CacheCluster.

By default, security is enabled on the cache. So we must grant access to our client. Because I run in workgroup modus, I must create an account on my server with the exact same user name and password as will be used by my WCF service on the AppFabric caching client. Granting access to a user account can be done with the following command: Grant-CacheAllowedClientAccount -Account “YourUserAccountName”.

Please note: in case of an AD domain is available, the user account must not be created on the server. It is sufficient that the account is known in AD.

powershell_cmdlets

Configuring the AppFabric caching client

On the client, the client at least the “Cache Client” of AppFabric must be installed. No special configuration is required.

setup_client

Developing the WCF service

For the sake of simplicity, I kept the actual programming code to the strict minimum. The sample WCF application consists of two components: a (slow) repository and a WCF service.

First we must add a reference to two DLL’s installed by the AppFabric installer. They can be found in the installation folder of AppFabric.

vs_references

The slow repository is called: SlowRepository (very creative naming; I know…)

public class SlowRepository
{
    public IEnumerable<string> GetPizzas()
    {
        Thread.Sleep(10000);

        return new List<string>() { "Hawaii", "Pepperoni", "Bolognaise" };
    }
}

I kept the WCF service very basic. It creates an instance of the DataCache object inside its constructor. This object offers an interface to the distributed cache. In order to create it, we must provide the addresses and network ports of the servers hosting the distributed cache.

public class PizzaService : IPizzaService
{
    private const string CacheServerName = "WIN-53K8O7PMTOP";
    private const int CacheServerPort = 22233;
    private const string CacheKey = "availablePizzas";

    private DataCache cache;
    private SlowRepository repository;

    public PizzaService()
    {
        // Setup AppFabric Cache
        List<DataCacheServerEndpoint> servers = new List<DataCacheServerEndpoint>();
        servers.Add(new DataCacheServerEndpoint(CacheServerName, CacheServerPort));

        DataCacheFactoryConfiguration configuration = new DataCacheFactoryConfiguration();
        configuration.Servers = servers;

        //Set default properties for local cache (local cache disabled)
        configuration.LocalCacheProperties = new DataCacheLocalCacheProperties();
        configuration.SecurityProperties = 
            new DataCacheSecurity(DataCacheSecurityMode.None, DataCacheProtectionLevel.None);

        DataCacheFactory cacheFactory = new DataCacheFactory(configuration);
        this.cache = cacheFactory.GetCache("default"); // Get the default cache

        // Create our 'slow' repository
        this.repository = new SlowRepository();
    }

    public IEnumerable<string> GetAvailablePizzas()
    {
        IEnumerable<string> availablePizzas = null;

        // Try to get the item from the cache
        availablePizzas = (IEnumerable<string>)cache.Get(CacheKey);

        if (availablePizzas != null)
            return availablePizzas;
        else
        {
            availablePizzas = repository.GetPizzas();

            // Store data in the cache
            cache.Put(CacheKey, availablePizzas);

            return availablePizzas;
        }
    }
}

If you call the service twice, you will notice that the second time the data is taken from the cache instead of from the repository. If we restart the web service, the data is still taken from the cache. If we execute the PowerShell command “Get-CacheStatistics” we can see that the cache contains our data.

powershell_cachestatistics

Categories: C#, WCF

Caching in WCF Services: Part 1

April 9, 2012 20 comments

This is the first part of a two part article about caching in WCF services. In this part I will explain the in-process memory cache available in .NET 4.0. In the second part I will describe the Windows AppFabric distributed memory cache.

The .NET framework has provided a cache for ASP.NET applications since version 1.0. For other types of applications like WPF applications or console application, caching was never possible out of the box. Only WCF services were able to use the ASP.NET cache if they were configured to run in ASP.NET compatibility mode. But this mode has some performance drawbacks and only works when the WCF service is hosted inside IIS and uses an HTTP-based binding.

With the release of the .NET 4.0 framework this has luckily changed. Microsoft has now developed an in-process memory cache that does not rely on the ASP.NET framework. This cache can be found in the “System.Runtime.Caching.dll” assembly.

In order to explain the working of the cache, I have a created a simple sample application. It consists of a very slow repository called “SlowRepository”.

public class SlowRepository
{
    public IEnumerable GetPizzas()
    {
        Thread.Sleep(10000);

        return new List() { "Hawaii", "Pepperoni", "Bolognaise" };
    }
}

This repository is used by my sample WCF service to gets its data.

public class PizzaService : IPizzaService
{
    private const string CacheKey = "availablePizzas";

    private SlowRepository repository;

    public PizzaService()
    {
        this.repository = new SlowRepository();
    }

    public IEnumerable GetAvailablePizzas()
    {
        ObjectCache cache = MemoryCache.Default;

        if(cache.Contains(CacheKey))
            return (IEnumerable)cache.Get(CacheKey);
        else
        {
            IEnumerable availablePizzas = repository.GetPizzas();

            // Store data in the cache
            CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
            cacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
            cache.Add(CacheKey, availablePizzas, cacheItemPolicy);

            return availablePizzas;
        }
    }
}

When the WCF service method GetAvailablePizzas is called, the service first retrieves the default memory cache instance

ObjectCache cache = MemoryCache.Default;

Next, it checks if the data is already available in the cache. If so, the cached data is used. If not, the repository is called to get the data and afterwards the data is stored in the cache.

For my sample service, I also choose to restrict the maximum memory to 20% of the total physical memory. This can be done in the web.config.

<system.runtime.caching>
 <memoryCache>
  <namedCaches>
   <add name="Default" physicalMemoryLimitPercentage="20"/>
  </namedCaches>
 </memoryCache>
</system.runtime.caching>
Categories: C#, WCF

WCF REST tip 3: WebOperationContext

October 6, 2011 2 comments

People with experience in programming WCF services are probably aware that you can get information about the currently executing operation in a WCF service by retrieving the property “OperationContext.Current” inside a service method. This gives you access to incoming and outgoing message headers and properties.

This is sufficient for classic WCF services, but it lacks information that could be important for REST based WCF services. Luckily for us, Microsoft has foreseen this and they have created an additional Operation Context for these types of services: the WebOperationContext. The WebOperationContext provides easy access to various parts of the HTTP request and response messages: HTTP headers, HTTP method, …

In the following short example, I show how to retrieve the user agent of the caller and send it back formatted as JSON or as XML depending on the value of the Accept header in the request.

[DataContract]
public class UserAgentInfo
{
    public UserAgentInfo(string userAgent)
    {
        UserAgent = userAgent;
    }

    [DataMember]
    public string UserAgent { get; set; }
}
[WebGet(UriTemplate = "MyUserAgent")]
public Message GetMyUserAgent()
{
    string accept = WebOperationContext.Current.IncomingRequest.Accept;
    string userAgentValue = WebOperationContext.Current.IncomingRequest.UserAgent;
    UserAgentInfo userAgent = new UserAgentInfo(userAgentValue);

    switch (accept)
    {
        case "application/json":
            return WebOperationContext.Current.CreateJsonResponse<UserAgentInfo>(userAgent);
        default:
            return WebOperationContext.Current.CreateXmlResponse<UserAgentInfo>(userAgent);
    }
}
Categories: C#, REST, WCF

Configuring the WCF Web API without using the fluent API

Microsoft will add first class support for REST web services into the next version of WCF. A preview of this can be downloaded from: http://wcf.codeplex.com. After having seen the presentation of Glenn Block at the Belgium Microsoft Techdays, I wanted to start playing a little bit with the API and a couple of days ago I finally found the time to do it.

I am quite happy with the way how they will handle REST in .Net and it will make developing real REST services (instead of REST like services …) more easy. The only thing that annoyed me slightly was the fact that is not possible to configure the WCF Web API from the web.config. In my company we have different environments (development, test, …) and we have to promote the assemblies from one environment to the other, but we can have different config files depending on the environment. In the classical WCF, this allows us to enable custom logging or monitoring in for example test but not in production. With the fluent API, this kind of deployment time configuration is not possible. With our deployment system, if we would want to add WCF Web message handlers or operation handlers in test, it would also go to production or we would first have to promote a modified version to test without logging.

In order to fix this, I have written a custom service behavior that allows adding message handlers using the configuration file. Of course this could easily be extended with the other configuration options of the fluent API, but I want to keep my example as simple as possible. If people would be interested in a version with complete web.config support, then please let me know.

Hooking into the WCF Web API was remarkable easy. It adds endpoints of type HttpEndpoint for the REST services to the WCF stack. The HttpEndpoint provides access to features like the active message handlers. So all I had to do was looping over the endpoints in my service behavior and if it was of type HttpEndpoint than I added all the message handlers that were defined in the config file.

public class HttpConfigBehavior : IServiceBehavior
{
    private Type[] messageHandlers;

    public HttpConfigBehavior(IEnumerable<Type> messageHandlers)
    {
        if (messageHandlers == null)
            throw new ArgumentNullException("messageHandlers");

        this.messageHandlers = messageHandlers.ToArray();
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
        Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
        foreach (ServiceEndpoint serviceEndpoint in endpoints)
        {
            HttpEndpoint httpEndpoint = serviceEndpoint as HttpEndpoint;
            if (httpEndpoint != null)
            {
                httpEndpoint.MessageHandlerFactory = new HttpMessageHandlerFactory(messageHandlers);
            }
        }
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

In order to add in from the config file, I also had to create a BehaviorExtensionElement and the necessary configuration classes.

public class HttpConfigBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(HttpConfigBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new HttpConfigBehavior(
            MessageHandlers.OfType<MessageHandlerConfigurationElement>().Select(e => e.Type));
    }

    [ConfigurationProperty("messageHandlers")]
    public MessageHandlerConfigurationCollection MessageHandlers
    {
        get
        {
            return (MessageHandlerConfigurationCollection)base["messageHandlers"];
        }
        set
        {
            base["messageHandlers"] = value;
        }
    }
}
[ConfigurationCollection(typeof(MessageHandlerConfigurationElement))]
public class MessageHandlerConfigurationCollection : ConfigurationElementCollection
{
    public MessageHandlerConfigurationElement this[int index]
    {
        get
        {
            return (MessageHandlerConfigurationElement)base.BaseGet(index);
        }
    }

    protected override ConfigurationElement CreateNewElement()
    {
        return new MessageHandlerConfigurationElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((MessageHandlerConfigurationElement)element).Name;
    }
}
public class MessageHandlerConfigurationElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired=true)]
    public string Name
    {
        get
        {
            return (string)base["name"];
        }
        set
        {
            base["name"] = value;
        }
    }

    [ConfigurationProperty("type", IsRequired = true)]
    [TypeConverter(typeof(TypeNameConverter))]
    public Type Type
    {
        get
        {
            return (Type)base["type"];
        }
        set
        {
            base["type"] = value;
        }
    }
}
public class TypeNameConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(Type);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return Type.GetType((string)value);
    }
}

We can now define WCF Web API message handlers from the config file:

<system.serviceModel>

  <behaviors>
    <serviceBehaviors>
      <behavior>
        <httpConfig>
          <messageHandlers>
            <add name="logger"
                  type="MvcApplication45.Util.LoggingChannel, MvcApplication45, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          </messageHandlers>
        </httpConfig>
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <extensions>
    <behaviorExtensions>
      <add name="httpConfig" type="MvcApplication45.Util.HttpConfigBehaviorExtensionElement, MvcApplication45, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </behaviorExtensions>
  </extensions>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

Conclusion

Although it is not provided out of the box, the WCF Web API can easily be configured using the web.config file. All that is required is some good old “classic” WCF knowledge. Your investment in learning classic WCF is thus definitely not lost. Glimlach

Categories: C#, REST, WCF

Dynamically updating WCF endpoint addresses in WSDL meta data

Introduction

I have already written some blog articles about WCF and how flexible and extensible it is. A lot of WCF its internal components can be extended or replaced by custom, user developed implementations. Not surprisingly, we can also hook into the WSDL meta data generation.

Recently I was in a situation were I had to develop a self hosted WCF service, but I did not know the target hosting server(s) address(es) upfront and it was impossible for me to change the address in the app.config before deployment. Because WCF requires an absolute address for self hosted services, I had to use localhost. This works fine and gives no issues when calling the service from another computer, but it has one important drawback: WCF will use the localhost address in the generated WSDL meta data. In order to fix this, I created a custom WCF behavior.

WSDL before

IWsdlExportExtension

We can hook into the WSDL meta data generation by implementing the interface IWsdlExportExtension. This interface contains 2 methods: ExportContract and ExportEndpoint. If the class also implements IContractBehavior, the method ExportContract will be invoked by the WCF pipeline and vice versa for IEndpointBehavior and ExportEndpoint.

Because I only wanted to modify the endpoint addresses, I only had to implement the interface IEndpointBehavior and the method ExportContract. I wanted this also as a service behavior, so I also implemented the interface IServiceBehavior and I also extended the behavior from Attribute. This way, the behavior can be easily injected into the WCF pipeline by annotating the service implementation class with the behavior.

The modification itself is very simple; every occurrence of localhost in the endpoint addresses is replaced by the DNS name of the host machine.

public class HostNameAddressBehavior : Attribute, IWsdlExportExtension, IEndpointBehavior, IServiceBehavior
{
    private readonly Regex addressRegex;
    private readonly string hostName;

    public HostNameAddressBehavior()
    {
        hostName = Dns.GetHostName();
        addressRegex = new Regex(@"^(?<scheme>[a-z\.]+)://localhost(?<path>.*)",
            RegexOptions.IgnoreCase);
    }

    public void AddBindingParameters(ServiceEndpoint endpoint,
        BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
        BindingParameterCollection bindingParameters)
    {
        // If we are added as service behavior, add us to all the endpoints
        foreach (ServiceEndpoint endpoint in endpoints)
        {
            endpoint.Behaviors.Add(this);
        }
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void ExportContract(WsdlExporter exporter,
        WsdlContractConversionContext context)
    {
    }

    public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
    {
        // Overwrite the address in the service meta data
        EndpointAddress address = context.Endpoint.Address;

        string absoluteUri = address.Uri.AbsoluteUri;
        Match m = addressRegex.Match(absoluteUri);
        if (m.Success)
        {
            string scheme = m.Groups["scheme"].Value;
            string path = m.Groups["path"].Value;

            // Update base address
            Uri newAbsoluteUri = new Uri(string.Format("{0}://{1}{2}",
                scheme, hostName, path));
            context.Endpoint.Address = new EndpointAddress(newAbsoluteUri,
                address.Identity, address.Headers, address.GetReaderAtMetadata(),
                address.GetReaderAtExtensions());
        }
    }
}

If we also want to add this behavior using the config file, we have to create a BehaviorExtensionElement.

public class HostNameAddressBehaviorExtension : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get
        {
            return typeof(HostNameAddressBehavior);
        }
    }

    protected override object CreateBehavior()
    {
        return new HostNameAddressBehavior();
    }
}

Now the behavior can be added declarative as endpoint behavior or service behavior using the config file:

</system.serviceModel>

  ...

  <behaviors>
    <serviceBehaviors>
      <behavior name="HelloBehavior">
        <serviceMetadata httpGetEnabled="true" />
        <hostNameAddress />
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <extensions>
    <behaviorExtensions>
      <add name="hostNameAddress" type="HelloConsoleService.HostNameAddressBehaviorExtension, HelloConsoleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </behaviorExtensions>
  </extensions>
</system.serviceModel>

With the behavior in place, the meta data now contains the DNS name instead of “localhost” in the endpoint addresses.

WSDL after

Conclusion

Of course creating the endpoints dynamically in code at runtime could also have solved this problem, but that would result in code that is not reusable and the WCF service programmer would have been responsible for the implementation. By developing this as a behavior, it is reusable across services.

Categories: C#, WCF

Using an IoC container to create WCF service instances

Introduction

Using dependency injection and an IoC container increase the robustness of an application. Dependency injection is a design pattern that decouples behavior from dependency resolution. Thus making it easier to replace the actual implementation of components by mocked implementations during unit tests. IoC containers are frameworks that help you resolve dependencies for your .Net classes. Various implementations exist: Microsoft Unity, Ninject, Spring.Net, …

By default WCF requires that service implementation classes have a parameterless constructor. The sole exception are self hosted singleton services. In that case, you could also pass the required instance to the constructor of your ServiceHost class.

Luckily for us, WCF is very extensible and it allows us to change the component that is responsible for creating service instances. This component is called the instance provider and implements an interface called IInstanceProvider. In this article, I will show you how to create a new implementation that uses Ninject, but you could also use the IoC container of preference.

Getting Ninject

First we need to add Ninject to our project. Because NuGet is so popular these days in the Microsoft world, lets use it. So right click on References in Visual Studio and chose “Add Library Package Reference” and then search for Ninject.

AddLibraryPackageReference

When you click “install”, NuGet will download and add all the required assemblies of Ninject to the Visual Studio project. It will not install them in the GAC.

Creating the InstanceProvider

When you create a custom InstanceProvider, all you have to do is implement the interface IInstanceProvider. The required code to use an IoC container is very straightforward. We pass the Ninject kernel and the service type to the instance provider constructor and use the Ninject kernel to create instances of the service when required.

public class NinjectInstanceProvider : IInstanceProvider
{
    private Type serviceType;
    private IKernel kernel;

    public NinjectInstanceProvider(IKernel kernel, Type serviceType)
    {
        this.kernel = kernel;
        this.serviceType = serviceType;
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return this.GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        // Create the instance with your IoC container of choice, here I use Ninject.
        return kernel.Get(this.serviceType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}

Hooking into the WCF pipeline

Now that we have created our custom instance provider we have to inject it into the WCF pipeline. This can be done in code or in the web.config file. Personally I prefer the web.config, but that’s a matter of taste.

To add behaviors to a WCF service, you have to create a class that implements IServiceBehavior. WCF calls the method ApplyDispatchBehavior when its loading the service and before it will start accepting client requests. In this method we will inject our custom instance provider.

public class NinjectBehaviorAttribute : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
        Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        Type serviceType = serviceDescription.ServiceType;
        IInstanceProvider instanceProvider = new NinjectInstanceProvider(new StandardKernel(), serviceType);

        foreach(ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
            {
                DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
                dispatchRuntime.InstanceProvider = instanceProvider;
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

If the service behavior class extends from .Net Attribute class; you can use it to annotate your service implementation. WCF is able to detect such service behaviors and it will load and attach them to the service.

[NinjectBehaviorAttribute]
public class HelloService : IHelloService
{
}

If you don’t want to add the service behavior in code, but instead in the web.config file, you have to implement a class that extends from BehaviorExtensionElement. Again, this is very easy to do:

public class NinjectBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof (NinjectBehaviorAttribute); }
    }

    protected override object CreateBehavior()
    {
        return new NinjectBehaviorAttribute();
    }
}

Now we can configure WCF to use Ninject from the web.config:

<system.serviceModel>
  <services>
    <!-- A dummy WCF service ... -->
    <service name="WcfIoc.HelloService" behaviorConfiguration="HelloServiceBehavior">
      <endpoint address=""
                binding="wsHttpBinding"
                contract="WcfIoc.IHelloService" />
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior name="HelloServiceBehavior">
        <!-- Add the Ninject behavior to the WCF service. -->
        <ninject />
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <extensions>
    <behaviorExtensions>
      <!-- Add the Ninject behavior extension -->
      <add name="ninject"
            type="WcfIoc.NinjectBehaviorExtensionElement, WcfIoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>
</system.serviceModel>

We are done Glimlach I think that everybody will agree that WCF is very powerful and configurable, yet at the same time its internals are very intuitive and easy to understand.

Categories: C#, IoC, Ninject, WCF