1

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?

Ctrl_Alt_Defeat
  • 3,933
  • 12
  • 66
  • 116

1 Answers1

3

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.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • (comment part 1 of 2) just seeing your answer - I actually had implemented what you said in the Simplest way to do this is... - however I had not considered the Disposal which is valid so I will do some re-work on this. And yes you are correct - ExternalService client is a 3rd party service and then my service is my abstraction calling only methods I need along with some extra logic. So perhaps it is ok as I had it originally. – Ctrl_Alt_Defeat May 06 '14 at 17:32
  • comment part 2 of 2) - My reason for wanting to DI it was so if other Devs on team didn't have x509 certs installed on machine needed for the service they could easily comment out the Simple container.Register ExternalServiceClient("WSHttpBinding_IExternalService") and comment in a container.Register new MyStubExternalClient – Ctrl_Alt_Defeat May 06 '14 at 17:34
  • @Steven, I am in the progress of consuming a WCF service into my commands and queries. I like to depend on the client (or clients) on the handlers. Do you suggest I setup the client and register an initializer like in your example? – janhartmann May 28 '15 at 16:33
  • @janhartmann make an application soecific abdtraction (that follows SOLID) and create an adapter that creates/disposes the wcf proxy as in my example. – Steven May 29 '15 at 06:08
  • I'll see what I can come up with, if I fail you'll probably find a question on SO. Thanks @Steven :-) – janhartmann May 29 '15 at 06:14