HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Make WCF Service testable

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
testablemakeservicewcf

Problem

I have WCF Service. It works fine and I want to have test coverage for it. Unit tests and acceptance.

The problem is the static class in the code. How is it possible to avoid it?

If it will be refactored to the code without static classes - I can use mocks for example Moq.

I have also used Ninject as DI Framework so far.

[ServiceContract]
    public interface IWorkingService
    {
        [OperationContract]
        Collection UpdateEntity(int entityID);
    }

    public class WorkingService : IWorkingService
    {
        private static readonly WorkingService Logger = ProviderLog4Net.GiveLogger(typeof(SomeService));

        /// 
        public Collection UpdateEntity(int entityID)
        {
            Logger.Info("------- UpdateEntity call ------------");

            try
            {
                return CoreFacade.UpdateEntity(entityID);
            }
            catch (Exception exception)
            {
                Logger.Fatal(exception);
                throw;
            }
        }           
    }

    public static class CoreFacade
    {
       ...
    }

Solution

You are doing it wrong.

Why are you using WCF if you are only using it as an old timer ASMX service without using the extensibility points?

First things first - clean up

[ServiceContract]
public interface IWorkingService
{
    [OperationContract]
    Collection UpdateEntity(int entityId);
}


The service interface is the same as yours, but:

  • Why are you using Collection as the return type? Use someting more generic.



-
Do not use primitives as service parameters, ever! Using primitives as parameters means that you cannot versionize your service, such as adding extra non-required properties.

public class WorkingService : IWorkingService
{
    private readonly NotStaticFacade _notStaticFacade;

    public WorkingService(NotStaticFacade notStaticFacade)
    {
        _notStaticFacade = notStaticFacade;
    }

    public Collection UpdateEntity(int entityId)
    {
        return _notStaticFacade.UpdateEntity(entityId);
    }
}


The service implementation is much more slimmer then yours because it's missing the logging stuff (be patient!). The important thing is that it isn't containing a parameter-less constructor because the service has a dependency: the NotStaticService (in your code this is the static CoreFacade). What does this mean? We will need a custom ServiceHostFactory because the default one cannot handle these kinds of situation, but don't worry; Ninject is with us!

Install-Package Ninject.Extensions.Wcf

The Ninject has an extension for WCF so install it via NuGet (remove App_Start directory after installation) and after that, create our own ServiceHostFactory:

public class WorkingServiceHostFactoryWithNinject :    NinjectServiceHostFactory
{
    private readonly IKernel _kernel;

    public WorkingServiceHostFactoryWithNinject()
    {
        _kernel = new StandardKernel(new WorkingServiceNinjectModule());

        // SetKernel is a static method in Ninject WCF!
        SetKernel(_kernel);
    }
}


Nothing fancy; we only telling the factory which IKernel instance we want to use. As you see, you will have a NinjectModule prepared to map the bindings.

public class WorkingServiceNinjectModule : NinjectModule
{
    public override void Load()
    {
        // what ever other binding is need
        Kernel.Bind().ToSelf();
    }
}


I'm only telling the kernel how to resolve the NotStaticFacade but this isn't necessary this way. We haven't finished yet because we haven't told the system to use our factory to build up our service. To do this, we need to open the *.svc markup (right-click on the file then View markup) and write into the directive the Factory attribute:



Save and close this.

Now we have a service with dependency injection and the only one real facade is our service.

WCF extensibility - IServiceBehavior, IErrorHandler

The WCF infrastructure has great possibilities for extending its capabilities, such as handling an error!

To do that we will create a new service behavior and we will use it for adding our error handlers to the service:

public class ErrorHandlerBehaviorWithNinjectKernel : IServiceBehavior
{
    private readonly IKernel _kernel;

    public ErrorHandlerBehaviorWithNinjectKernel(IKernel kernel)
    {
        _kernel = kernel;
    }

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

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

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        var errorHandlers = _kernel.GetAll().ToArray();

        if (!errorHandlers.Any())
        {
            throw new Exception("No errorhandler was found");
        }

        foreach (var channelDispatcher in serviceHostBase.ChannelDispatchers.Select(channelDispatcherBase => channelDispatcherBase as ChannelDispatcher))
        {
            foreach (var errorHandler in errorHandlers)
            {
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }
}


What can we see here? We are adding all IErrorHandler instance to all possible dispatchers to use it. IErrorHandler is coming from the WCF infrastructure. You need to create your own implementation of it like this:

public class ErrorLogger : IErrorHandler
{
    private readonly IAmLogger _logger;

    public ErrorLogger(IAmLogger logger)
    {
        _logger = logger;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        //nothing to do here
    }

    public bool HandleError(Exception error)
    {
        _logger.Fatal(error);

        return true;
    }
}


I have created the IAmLogger interface to fake your Log4Net stuff, nothing more:

```
public interface IAmLogger
{
void In

Code Snippets

[ServiceContract]
public interface IWorkingService
{
    [OperationContract]
    Collection<Ergebnis> UpdateEntity(int entityId);
}
public class WorkingService : IWorkingService
{
    private readonly NotStaticFacade _notStaticFacade;

    public WorkingService(NotStaticFacade notStaticFacade)
    {
        _notStaticFacade = notStaticFacade;
    }

    public Collection<Ergebnis> UpdateEntity(int entityId)
    {
        return _notStaticFacade.UpdateEntity(entityId);
    }
}
public class WorkingServiceHostFactoryWithNinject :    NinjectServiceHostFactory
{
    private readonly IKernel _kernel;

    public WorkingServiceHostFactoryWithNinject()
    {
        _kernel = new StandardKernel(new WorkingServiceNinjectModule());

        // SetKernel is a static method in Ninject WCF!
        SetKernel(_kernel);
    }
}
public class WorkingServiceNinjectModule : NinjectModule
{
    public override void Load()
    {
        // what ever other binding is need
        Kernel.Bind<NotStaticFacade>().ToSelf();
    }
}
<%@ ServiceHost Language="C#" Debug="true" Service="CodeReview.WorkingService" CodeBehind="WorkingService.svc.cs"
        Factory="CodeReview.WorkingServiceHostFactoryWithNinject" %>

Context

StackExchange Code Review Q#33379, answer score: 14

Revisions (0)

No revisions yet.