DI for Generic SingleInstance with constructor arguments

Topics: Core, Writing modules
Aug 13, 2012 at 2:56 PM

(The motivation behind this is to build an Orchard module using SolrNet, but I've reduced it down to a simpler test case for the purpose of this discussion)

I have an autofac module which registers some types with Autofac, which I'm then trying to have injected automatically into a controller.  Most of the time this works fine.

However, if I have a generic interface (non-generic is fine), which is marked for Autofac with SingleInstance, and the constructor of the concrete implementation has arguments, then the dependencies fail to resolve.

Here's an example - take the following interfaces/classes:

public interface ITestInterface<T> { }
public interface ITest2Interface { }
public class TestClass1<T> : ITestInterface<T>
   public TestClass1(ITest2Interface impl) { }
public class TestClass2 : ITest2Interface { }

now register these in the Load function of the autofac module:

protected override void Load(ContainerBuilder builder)

If I create a controller which takes ITestInterface as a constructor argument eg.:

public class TestController : Controller
    private readonly ITestInterface<string> _mytest;
    public TestController(ITestInterface<string> myTest)
        _mytest = myTest;

Then an Autofac.Core.DependencyResolutionException is raised in OrchardControllerFactory.cs.

Line 24:                 var key = new KeyedService(serviceKey, typeof (T));
Line 25:                 object value;
Line 26:                 if (workContext.Resolve<ILifetimeScope>().TryResolveService(key, out value)) {
Line 27:                     instance = (T) value;
Line 28:                     return true;


I wondered if it might be an autofac error, but this test passes:


var builder = new ContainerBuilder();
var container = builder.Build();
var testClass = container.Resolve<ITestInterface<string>>(); Assert.IsInstanceOf(typeof(TestClass1<string>), testClass);


Which suggests to me it is in fact an Orchard issue.

If .SingleInstance is removed, then the problem goes away.  If there are no constructor arguments in the Generic SingleInstance class, then the problem goes away.  And if it is just a normal type registration, not a generic registration, then the problem goes away.

I'm really new to Autofac outside of Orchard doing things automatically, and a reasonable novice to DI, so I may be missing something here.

Is it an Orchard or Autofac bug, or am I doing something wrong?



Aug 14, 2012 at 5:30 PM

I've been through the Orchard source code to try and understand better what's going on and re-written my test to mimick it (or mimick what I think is going on!) and now get the test failing in the same way as Orchard does:

var builder = new ContainerBuilder();
var container = builder.Build();
var shellLifetime = container.BeginLifetimeScope("shell", bldr => 
var testClass = shellLifetime.Resolve<ITestInterface<string>>();
Assert.IsAssignableFrom(typeof(TestClass1<string>), testClass);

which gives (when run in Linqpad)

DependencyResolutionException: None of the constructors found with 'Public binding flags' on type 'UserQuery+TestClass1`1[System.String]' can be invoked with the available services and parameters:
Cannot resolve parameter 'ITest2Interface impl' of constructor 'Void .ctor(ITest2Interface)'.

It would therefore appear to be something to do with the heirarchical contexts in Autofac.

I don't yet understand heirarchical contexts in Autofac.  Could anyone who does possibly identify whether this is a bug in Autofac that I need to report over there, or if it is to do with the way Autofac is being used / works?

Aug 15, 2012 at 6:56 AM

I've done some more reading on Autofac (http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/ was particularly helpful) and this is an Autofac rather than an Orchard issue, so sorry if I've wasted anyone's time.

In case it helps anyone else, when an autofac module is put in an Orchard module, it is called within a lifetime scope named 'shell' (unsurprisingly, scoped to the Orchard shell).  Therefore all registrations are in the 'shell' lifetime not the 'root' scope, except when SingleInstance() is used, in which case Autofac resolves the instance in the root scope.

From the aforementioned article, "When the resolve operation has moved from a child scope to the parent, any further dependencies will be resolved in the parent scope.".

Therefore generally for SingleInstance objects the dependencies should also resolve in the root scope.

However, when it is an open generic being resolved, autofac doesn't seem to support this moving between scopes and fails.

Whilst I think this may be a bug in Autofac, in my instance, reading further suggests to me that changing the scope to InstancePerLifetimeScope rather than SingleInstance would be a better fit anyway, and this also resolves the scope resolution problem.