Child containers and lifetime

Jul 2, 2008 at 5:55 PM
I'm trying to port some of the concepts of the SCSF using Unity. One of the behaviors of the SCSF is the ability to have seperate instances returned to seperate child work items using a single registration. A type can be registered at the parent as a singleton, however when requested from seperate child containers each child container would receive a seperate singleton instance. Once an instance has been created, it is accessible to each of that container's children, but not to it's parent or sibling.

This allows you to register some global service instances (say, authorization) at the parent item which are available to all of the children, but views and controllers can live seperately between seperate child containers.

For example:


using (IUnityContainer container = new UnityContainer())
{
	IFoo childFoo;
	IFoo parentFoo;

	container.RegisterType<IFoo, Foo>(new ContainerControlledLifetimeManager());

	using (IUnityContainer childContainer =
		container.CreateChildContainer())
	{
		childFoo = childContainer.Resolve<IFoo>();
	}

	parentFoo = container.Resolve<IFoo>();

	Assert.AreNotSame(parentFoo, childFoo);
}

While this may not seem useful (or logical), the real goal is:


using (IUnityContainer container = new UnityContainer())
{
	IFoo childFoo1, childFoo2;

	container.RegisterInstance<IFoo, Foo>(new ContainerControlledLifetimeManager());

	using (IUnityContainer childContainer1 =
		container.CreateChildContainer())
	{
		childFoo1 = childContainer1.Resolve<IFoo>();
	}

	using (IUnityContainer childContainer2 =
		container.CreateChildContainer())
	{
		childFoo2 = childContainer2.Resolve<IFoo>();
	}

	Assert.AreNotSame(childFoo1, childFoo2);
}

More importantly, the ability to also register an instance that would be returned to all child containers:


using (IUnityContainer container = new UnityContainer()) { IFoo childFoo; IFoo parentFoo; container.RegisterInstance<IFoo>(new Foo()); using (IUnityContainer childContainer = container.CreateChildContainer()) { childFoo = childContainer.Resolve<IFoo>(); } parentFoo = container.Resolve<IFoo>(); Assert.AreSame(parentFoo, childFoo); }

However, because the lifetime exists at the parent, it always returns the same instance. The only solution I can find today is to have 2 seperate container configurations, one for the parent and one for child containers, and always configure the child containers on creation. While this would work for the first level of children, it would break the ability of the 2nd level of children (the top container's grandchildren) to inherit instances from it's parent. I thought that I might be able to create a new lifetime manager, however I can't see how the current container that is resolving would be accessible from the lifetime manager (especially during GetValue() and SetValue(object) calls).

Perhaps I'm missing a concept?

Jul 2, 2008 at 6:38 PM
No, you've pretty much got it. This is not something that's easily supported. It also feels a little weird to me. The intended usage is for you to configure each child container if you need the child to do something differently from the parent.

What do you mean by "it would break the ability of the 2nd level of children (the top container's grandchildren) to inherit instances from it's parent"? The grandchildren would, when resolving values, look first in itself, then in it's parent, and so on up the parent tree until it finds a lifetime manager or ends up building the object itself. I think I'm misunderstanding what you mean, could you give me more details?

Jul 2, 2008 at 6:48 PM
Edited Jul 2, 2008 at 6:49 PM


ctavares wrote:

What do you mean by "it would break the ability of the 2nd level of children (the top container's grandchildren) to inherit instances from it's parent"? The grandchildren would, when resolving values, look first in itself, then in it's parent, and so on up the parent tree until it finds a lifetime manager or ends up building the object itself. I think I'm misunderstanding what you mean, could you give me more details?




Sorry...this was in reference to the existing Unity container. If I reconfigure every child container (UnityContainer), it will create a new lifetime at that container level so it wouldn't look at it's parent.

I'm wondering if maybe there's another approach. The SCSF combined the Unit of Work and DI concepts together into the Work Item. Maybe that's the problem? I'm trying to get the MVP pattern implemented in a Windows Application, but I didn't want to have to make the application itself responsible for creating the DI containers. I was hoping to abstract the DI mechanism from the application itself. So, instead of:

protected void Button_Click(object sender, EventArgs e) { using (IUnityContainer container = new UnityContainer()) { SomeController controller = container.Resolve<SomeController>(); controller.Execute(); } }
It might just be:

protected void Button_Click(object sender, EventArgs e) { using (SomeWorkItem childWorkItem = workItem.Get<SomeWorkItem>()) { childWorkItem.Execute(); } }
Maybe there's a better or more straightforward approach?

-Adam
Jul 2, 2008 at 6:55 PM
The most straightforward approach would be:

class MyWindow
{
    SomeController controller;

    public MyWindow(SomeController controller) { this.controller = controller; }

    protected void Button_Click(object sender, EventArgs e)
    {
        controller.Execute();
    }
}

Then use the container when creating the window object, and the controller gets injected like any other dependency.

Have you looked at the Prism (now CAPG or something hideous like that) project? They've released their code to codeplex. It's composite app guidance for WPF, and they took a very different, and much simpler, approach than CAB did for winforms. Even if you aren't interested in WPF, I think the approach they used is valuable for Winforms as well. They lean on a DI container very heavily.

Jul 2, 2008 at 7:21 PM
Edited Jul 2, 2008 at 7:22 PM
Sorry, my example was probably a poor choice. I'm really more confused with trying to create child work items, such as:


View
public event EventHandler CreateNewRecord;

protected void mnuCreateNewRecord_Click(object sender, EventArgs e)
{
	if (CreateNewRecord != null)
		CreateNewRecord(this, EventArgs.Empty);
}



Controller
protected void View_CreateNewRecord(object sender, EventArgs e)
{
	using (CreateRecordWorkItem childWorkItem =
		workItem.Get<CreateRecordWorkItem>())
	{
		childWorkItem.Execute();
	}
}

I am aiming for a Passive View, so I had hoped to make the controller responsible for execution vs. relying on the view.

I'll investigate Prism this afternoon and see if it can offer some ideas.