satnhak
satnhak

Reputation: 9861

How to achieve object scoped singletons in the Unity Container?

Assume that I have the following class structure:

public class Outer
{
    [Dependency]
    public Func<Inner> InnerFactory { get; set; }
}

public class Inner
{
}

In Autofac this can be done easily in the following way (and describes the behaviour I am looking for; ignore the fact that this is a stupid contrived example):

[TestMethod]
public void AutofacMakesThisEasy()
{
    var builder = new ContainerBuilder();

    builder.RegisterType<Outer>().PropertiesAutowired();
    builder.RegisterType<Inner>().InstancePerOwned<Outer>();

    var container = builder.Build();

    var outer1 = container.Resolve<Owned<Outer>>().Value;

    var inner1 = outer1.InnerFactory();
    var inner2 = outer1.InnerFactory();

    var outer2 = container.Resolve<Owned<Outer>>().Value;

    var inner3 = outer2.InnerFactory();
    var inner4 = outer2.InnerFactory();

    Assert.AreNotSame(outer1, outer2, "outer1 == outer2");
    Assert.AreSame(inner1, inner2, "inner1 != inner2");
    Assert.AreNotSame(inner2, inner3, "inner2 == inner3");
    Assert.AreSame(inner3, inner4, "inner3 != inner4");
}

How can I achieve the same behaviour in unity so the following test passes?

[TestMethod]
public void UnityHasMeScratchingMyHead()
{
    var container = new UnityContainer();

    //// What shoud I do here?

    var outer1 = container.Resolve<Outer>();

    var inner1 = outer1.InnerFactory();
    var inner2 = outer1.InnerFactory();

    var outer2 = container.Resolve<Outer>();

    var inner3 = outer2.InnerFactory();
    var inner4 = outer2.InnerFactory();

    Assert.AreNotSame(outer1, outer2, "outer1 == outer2");
    Assert.AreSame(inner1, inner2, "inner1 != inner2");
    Assert.AreNotSame(inner2, inner3, "inner2 == inner3");
    Assert.AreSame(inner3, inner4, "inner3 != inner4");
}

Note: I would dearly love to ditch Unity for a decent IoC, but that is going to be a very hard sell.

Upvotes: 3

Views: 1087

Answers (1)

Scott Chamberlain
Scott Chamberlain

Reputation: 127553

If InnerFactory was just Inner instead of Func<Inner> The solution would be PerResolveLifetimeManager

[TestMethod]
public void UnityHasMeScratchingMyHead()
{
    var container = new UnityContainer();

    container.RegisterType<Inner>(new PerResolveLifetimeManager());

    var outer1 = container.Resolve<Outer>();
    //... and so on.

However because you have a factory you need to make it a little more complicated. You need to make the factory PerResolve then have the objects the factory makes all the same instance (ContainerControlledLifetimeManager). You do that via the following:

[TestMethod]
public void UnityHasMeScratchingMyHead()
{
    var container = new UnityContainer();

    container.RegisterType<Func<Inner>>(new PerResolveLifetimeManager(), new InjectionFactory(x =>
    {
        var child = x.CreateChildContainer();
        child.RegisterType<Inner>(new ContainerControlledLifetimeManager());
        return new Func<Inner>(() => child.Resolve<Inner>());
    }));

    var outer1 = container.Resolve<Outer>();

    var inner1 = outer1.InnerFactory();
    var inner2 = outer1.InnerFactory();

    var outer2 = container.Resolve<Outer>();

    var inner3 = outer2.InnerFactory();
    var inner4 = outer2.InnerFactory();

    Assert.AreNotSame(outer1, outer2, "outer1 == outer2");
    Assert.AreSame(inner1, inner2, "inner1 != inner2");
    Assert.AreNotSame(inner2, inner3, "inner2 == inner3");
    Assert.AreSame(inner3, inner4, "inner3 != inner4");
}

One thing of note, Unity is easily extensible. If you are going to be doing this frequently with a little work you could make a InstancePerOwnedLifetimeManager<T> and just use that and not need to worry about generating factories at all.

Upvotes: 3

Related Questions