Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 747
Ants Avatar asked May 31 '11 10:05

Ants


2 Answers

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.

like image 107
bentayloruk Avatar answered Dec 13 '22 18:12

bentayloruk


For anyone interested here is the new way of doing this:

class DependsByProp1
{
    public DependsByProp2 Dependency { get; set; }
}

class DependsByProp2
{
    public DependsByProp1 Dependency { get; set; }
}

// ...

var cb = new ContainerBuilder();
cb.RegisterType<DependsByProp1>()
      .InstancePerLifetimeScope()
      .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
cb.RegisterType<DependsByProp2>()
      .InstancePerLifetimeScope()
      .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);

Property/Property Dependencies

like image 24
MeTitus Avatar answered Dec 13 '22 18:12

MeTitus