0
votes

I'm injecting a business layer into a self-hosted (InstanceContextMode=per-call) WCF service by leveraging AutoFac's ServiceHost.AddDependencyInjectionBehavior() extension (as described in the Autofac documentation)

My business layer uses components that cannot be re-created each time a new request comes in (let's say it needs a persistent database connection).

As such, when building the container I'd like to register the BL service as a Single instance, i.e.:

builder.RegisterType<BusinessLayer>()
    .SingleInstance();

The business-layer injection into the WCF service is working fine; my issue is that:

Dispose() is not called on ANY services created in the container: not just the Business layer itself, but the 'persistent' services too.

I'd expect this to happen for the BL service itself. From the Autofac docs again:

If you have singleton components (registered as SingleInstance()) they will live for the life of the container. Since container lifetimes are usually the application lifetime, it means the component won’t be disposed until the end of the application.

, but why are none of my 'child' (Autofac-registered) services (i.e the 'IPersistentService' below) disposed when the lifetime scope is -- given that I'm not explicitly making them 'SingleInstance'? Note:: this is still the case if I manually dispose the business layer service within the lifetime scope after I close the ServiceHost

E.g. (IDisposable implementations omitted for brevity):

[ServiceContract]
public interface IMyService
{
    void DoStuff();
}
public class MyService : IMyService
{
    IBusinessLayer _bl;
    public MyService(IBusinessLayer bl)
    {
        _bl = bl;
    }

    public void DoStuff()
    {
        _bl.BLDoStuff();
    }
}

public interface IBusinessLayer
{
    void BLDoStuff();
}
public class BusinessLayer : IBusinessLayer
{
    IPersistentService _service;
    public BusinessLayer(IPersistentService service)
    {
        _service = service;
    }
    public void BLDoStuff()
    {
        // Do something that requires a 'cached' / persistent component
        _service.DoSomethingWithPersistentConnection();
    }
}

public interface IPersistentService : IDisposable
{
    void DoSomethingWithPersistentConnection();
}

With Autofac registrations looking something like:

builder.RegisterType<BusinessLayer>()
    .SingleInstance();

builder.RegisterType<MyPersistentService>()
    .As<IPersistentService>()
    .OnActivated(e => e.Instance.Start());
1
"why are none of my 'child' (Autofac-registered) services (i.e the 'IPersistentService' below) disposed when the lifetime scope is". This problem is commonly referred to as Captive Dependency.Steven
Right. Yip, that explains it -- and thanks for the link to that description.Ive

1 Answers

1
votes

As Steven mentioned, what you experience here is a Captive Dependency problem. In other words, a singleton (BusinessLayer, registered with .SingleInstance()) keeps a lifetime-scoped or transient dependency (MyPersistentService, registered by default as transient).

Put it this way, dependencies of singleton services will always be singletons themselves, no matter how they were registered in the container. The diagram in Mark Seeman's article that Steven linked to gives a good view of this.

I think you can achieve what you expect, but your registrations are wrong.

My business layer uses components that cannot be re-created each time a new request comes in (let's say it needs a persistent database connection).

As such, when building the container I'd like to register the BL service as a Single instance

This is where the problem is. It's the dependency of the business service that has to be registered as a singleton, not the business service itself. This means you could have Autofac create a different instance of BusinessLayer for each WCF call, but the MyPersistentService instance injected into would always be the same. Does this make sense? Your registrations would then look like:

builder
    .RegisterType<BusinessLayer>()
    .As<IBusinessLayer>()
    .InstancePerLifetimeScope(); // a new instance per WCF call

builder
    .RegisterType<MyPersistentService>()
    .As<IPersistentService>()
    .OnActivated(e => e.Instance.Start())
    .SingleInstance(); // one same instance for the lifetime of the application

The one instance of MyPersistenService would then be disposed of only after disposing the root container (that you created by calling builder.Build()) after you close the Service Host.