dependency-injectionautofaccircular-dependencyproperty-injection

How to handle circular references with Autofac 2.4.5?


The autofac wiki page about Circular References says to use:

cb.Register<DependsByProp>().OnActivated(ActivatedHandler.InjectUnsetProperties);

But it looks like ActivatedHandler does not exist anymore in 2.4.5. Digging around in the source, I found the implementation of that class, and so I put in the method implementation in the OnActivated instead. Unfortunately, it this still doesn't work.

I've put together a minimal repro here that looks like what was on the Wiki page.

class M
{
    public VM VM { get; set; }

    public M()
    {
    }
}

class VM
{
    public VM(M m)
    {
    }
}

[Fact]
void CanResolveCircular()
{
    ContainerBuilder builder = new ContainerBuilder();

    builder.RegisterType<VM>();
    builder.RegisterType<M>().OnActivated(e => e.Context.InjectUnsetProperties(e.Instance));

    using (var container = builder.Build())
    {
        var m = container.Resolve<M>();
        Assert.NotNull(m);
    }
}

This code still throws a stack overflow exception as a Resolve is attempted. What am I missing? What is the correct way to get Autofac to handle circular dependencies?


Solution

  • The Autofac documentation for circular dependencies states:

    Note, it doesn't make sense to set up this scenario if both classes are registered with Factory scope.

    Both your M and VM registrations are InstancePerDependency (previously known as FactoryScope) so this statement applies to your scenario. As a result, you get in an endless loop of creating M and VM instances.

    If you want the property injected VM to take the same instance of M that you resolve, you should change the lifetime of M to something other than InstancePerDependency (e.g. SingleInstance). This is shown below:

    builder.RegisterType<M>().PropertiesAutowired(true).SingleInstance();
    

    Note: I'm also using the more recent PropertiesAutowired(true) extension method. You can use this in place of the OnActivated code in your repro.

    If you don't want a single instance of M per app, you could setup a LifetimeScope and use InstancePerLifetimeScope.