Question

I am having some difficulty in how to configure DI correct using SimpleInjector. I have an external Web Service which bindings are located in Web.config file. My external Web service lives in Services Layer. My Web Layer contains the composition root which calls my Domain layer to register services - the domain layer then will call the DAL layer and the serviceslayer to register services it needs. This will work ok and then in my Domain layer I can use the injected service I have created on servvices Layer in the Domain Service Layer constructor.

However in my Services Layer I have something similar to below:

public class MyService : IMyService
{

    private readonly ExternalServiceClient _externalServiceClient;

    public MyService()
    {
        _externalServiceClient = new ExternalServiceClient("WSHttpBinding_IExternalService");
    }

This design may not the best because it tightly couples MyService to relying on the external ServiceClient - what I want to achieve is the ability to have my own Stub of this external client and then easily switch between either the actual externalservice client or my stubbed version off the external service client.

So my constructor would look like:

    private readonly ExternalServiceClient _externalServiceClient;

    public MyService(ExternalServiceClient externalServiceClient)
    {
        _externalServiceClient = externalServiceClient);
    }

where - externalServiceClient is either a new

ExternalServiceClient("WSHttpBinding_IExternalService"); or my stubbed version of the external client.

What I am not sure off with SimpleInjector is how to get this wired up correctly so I can switch easily between which ExternalClient is passed into the constructor?

Was it helpful?

Solution

So the IMyService is your own abstraction, the ExternalServiceClient is some generated proxy and the MyService just delegates to the ExternalServiceClient? If that's the case, I would say it's fine that the MyService implementation is strongly coupled to the ExternalServiceClient. It contains no logic, just some infrastructure that allows hiding the ExternalServiceClient behind an abstraction. And instead of creating a stub ExternalServiceClient, you can simply create a stub IMyService.

Besides, if ExternalServiceClient is a WCF generated proxy, you might want to let the MyService class take control over the creation and disposal of the ExternalServiceClient, especially since WCF services need special care with disposing. If you would let your container control the disposal of the ExternalServiceClient, you would make your DI configuration harder, whatever container you pick.

Either way, if you decide to move the creation and disposal outside of the MyService, the simplest way to register this is the following:

container.Register<IMyService>(() => new MyService(
    new ExternalServiceClient("WSHttpBinding_IExternalService")));

Problem with this registration however is that the ExternalServiceClient is not disposed. If you explicitly register the ExternalServiceClient in the container with one of the Scoped lifestyles, the container will automatically handle dispose for you:

container.Register<IMyService, MyService>();

container.RegisterPerWebRequest<ExternalServiceClient>(
    () => new ExternalServiceClient("WSHttpBinding_IExternalService"));

A scoped lifestyle however means that the ExternalServiceClient is reused throughout the request, and this might not be what you need. So alternatively, you can register it as transient, and allow any created instance to be disposed when the request ends:

var scopedLifestyle = new WebRequestLifestyle();

container.Register<IMyService, MyService>();

container.Register<ExternalServiceClient>(
    () => new ExternalServiceClient("WSHttpBinding_IExternalService"));

container.RegisterInitializer<ExternalServiceClient>(client =>
{
    scopedLifestyle.RegisterForDisposal(container, client);
});

Problem with this configuration of course is that WCF proxies are nasty fâckers that might throw exceptions from their Dispose method, so you need to do some extra handling to get this right:

var scopedLifestyle = new WebRequestLifestyle();

container.Register<IMyService, MyService>();

container.Register<ExternalServiceClient>(
    () => new ExternalServiceClient("WSHttpBinding_IExternalService"));

container.RegisterInitializer<ExternalServiceClient>(client =>
{
    scopedLifestyle.WhenScopeEnds(container, () =>
    {
        try
        {
            client.Dispose();
        }
        catch 
        {
            // According to Marc Gravell we need to have a catch all here. 
        }
    });
});

That would effectively do the trick.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top