Generic Mapping & ContainerControlledLifetimeManager Bug?

Apr 3, 2008 at 4:05 PM
Hey,

I'm having trouble using both Generic Mapping and a ContainerControlledLifetimeManager at the same time. Basically, I have IFoo<T> and Foo<T> and I want a single instance of Foo for each generic parameter passed in (ie a single instance for Foo<int>, a single instance for Foo<string>, and any other type T).

Resolving works fine for the first type, T1. But for the second type, T2, it is being resolved as Foo<T1>.

Here is a code snippet illustrating this:

using Microsoft.Practices.Unity;

namespace UnityRepro
{
class Program
{
static void Main(string[] args)
{
IUnityContainer unityContainer = new UnityContainer();

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

IFoo<int> intFoo = unityContainer.Resolve<IFoo<int>>();
IFoo<string> stringFoo = unityContainer.Resolve<IFoo<string>>(); //Resolves as Foo<int>.
}
}

public interface IFoo<T>
{
}

public class Foo<T> : IFoo<T>
{
}
}


Am I doing something wrong/is this a bug in ContainerControlledLifetimeManager/is a custom lifetime manager needed?


Thanks
Apr 3, 2008 at 5:37 PM
Edited Apr 3, 2008 at 5:40 PM
Oooohhhh. Never thought of this one.

Here's how it happens:

  1. There is a generic type mapping policy placed from IFoo<T> to Foo<T>
  2. An ILifetimePolicy (ContainerControlledLifetimeContainer) is registered on the registered target type: Foo<T>
  3. On the second call to resolve, the type mapping policy will kick it, transforming IFoo<string> into an Foo<string> build key
  4. The LifetimeStrategy will then look for an ILifetimePolicy for Foo<string>. It shouldn't find one but by default, PolicyList supports generics, so it will find one registered on Foo<T>.
  5. Since the first instance being created was generic on Int32, the cast will fail.

Therefore, I think LifetimeManager has to be played with a bit more to support open types / multiple instances.

That's a pretty cool bug. I wish I found this one ;)
Apr 3, 2008 at 5:43 PM
Just an idea....

Maybe if we had support for a LifetimeManager "factory" overload to RegisterType, it could help.

The UnityContainer would use this factory when no lifetime manager is registered yet for a closed type given the factory was registered on the open type.

container.RegisterType(typeof(IFoo<>), typeof(Foo<>), () => new ContainerControlledLifetimeContainer());

Apr 3, 2008 at 9:13 PM

francois_tanguay wrote:
Oooohhhh. Never thought of this one.

Here's how it happens:

  1. There is a generic type mapping policy placed from IFoo<T> to Foo<T>
  2. An ILifetimePolicy (ContainerControlledLifetimeContainer) is registered on the registered target type: Foo<T>
  3. On the second call to resolve, the type mapping policy will kick it, transforming IFoo<string> into an Foo<string> build key
  4. The LifetimeStrategy will then look for an ILifetimePolicy for Foo<string>. It shouldn't find one but by default, PolicyList supports generics, so it will find one registered on Foo<T>.
  5. Since the first instance being created was generic on Int32, the cast will fail.

Therefore, I think LifetimeManager has to be played with a bit more to support open types / multiple instances.

That's a pretty cool bug. I wish I found this one ;)


Damn, wish I'd found that one too ... we just went through the signing rigamarole for the final 1.0 release. :-(

We'll have to fix it in the next release.

Apr 4, 2008 at 11:02 AM


ctavares wrote:

francois_tanguay wrote:
Oooohhhh. Never thought of this one.

Here's how it happens:

  1. There is a generic type mapping policy placed from IFoo<T> to Foo<T>
  2. An ILifetimePolicy (ContainerControlledLifetimeContainer) is registered on the registered target type: Foo<T>
  3. On the second call to resolve, the type mapping policy will kick it, transforming IFoo<string> into an Foo<string> build key
  4. The LifetimeStrategy will then look for an ILifetimePolicy for Foo<string>. It shouldn't find one but by default, PolicyList supports generics, so it will find one registered on Foo<T>.
  5. Since the first instance being created was generic on Int32, the cast will fail.

Therefore, I think LifetimeManager has to be played with a bit more to support open types / multiple instances.

That's a pretty cool bug. I wish I found this one ;)


Damn, wish I'd found that one too ... we just went through the signing rigamarole for the final 1.0 release. :-(

We'll have to fix it in the next release.




It was a cool bug until the last sentence. Typical Repositories will fail to work on this release.
Apr 4, 2008 at 5:19 PM
I'm going to be working on a workaround today for this issue. Basically, you'll need to use a special LifetimeManager that knows about open types and will do the right thing.

Ideally, it would be automatic, but in the meantime there's a workaround.
Apr 4, 2008 at 10:22 PM
Actually, never mind, that won't work. LifetimeManagers are assumed to know about what they're managing.

Hrm, this may be harder than I though.

In the meantime, you'll need to explicitly register the LifetimeManager for the concrete types you're using. You can still resolve them via the open generic interface.

Apr 8, 2008 at 9:47 PM
I have a fix for this which appears to be working, and requires no change to the signatures of the existing API calls. Look for it in the next drop.

I'm not going to post a preview here because it required several changes in different places.