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.