Resolving constructors for partially closed generic types

May 29, 2012 at 5:28 PM
Edited May 29, 2012 at 5:31 PM

I am attempting to use Unity (2.1.505.0) to create multi-argument generic types, and registering the generic types by specifying some of their generic arguments.  To illustrate consider the following types.

 

    public interface IRepository<TKey, TEntity>
    {
        void Add(TEntity entity);
    }

    public class BasicRepository<TKey, TEntity> : IRepository<TKey, TEntity>
    {
        private readonly Func<TKey> m_generateKey;
        private readonly IDictionary<TKey, TEntity> m_entities;

        public BasicRepository(Func<TKey> generateKey)
            : this(generateKey, Enumerable.Empty<TEntity>())
        { }

        public BasicRepository(Func<TKey> generateKey, IEnumerable<TEntity> initialEntitySet)
        {
            m_generateKey = generateKey;
            m_entities = initialEntitySet.ToDictionary(e => generateKey());
        }

        public void Add(TEntity entity)
        {
            m_entities.Add(m_generateKey(), entity);
        }
    }

 

I am registering a concrete instance of the basic repository as follows, with the intent of creating a rule that matches all repositories with integer keys.

 

    IUnityContainer container = new UnityContainer();
            
    Type intKeyRepositoryType = typeof(BasicRepository<,>).MakeGenericType(
        typeof(int),
        typeof(BasicRepository<,>).GetGenericArguments().Last());

    container.RegisterType(intKeyRepositoryType, new InjectionConstructor(new Func<int>(CreateInt)));
    IRepository<int, string> repo = container.Resolve<BasicRepository<int, string>>();

 

When I attempt to resolve an instance of the basic repository with an arbitrary entity type (i.e. string), Unity always attempts to invoke the "longest" ctor, regardless of the fact that I specified a specific ctor in the type registration.  If I use the InjectionConstructorAttribute, the correct ctor is called, but Unity attempts to resolve my functor argument, even though I specified an explicit value.

If I remove the problematic ctor, then Unity will resolve my type.  However, the argument passed to the single ctor is a delegate to resolve its single argument -- again, not the functor instance I am giving to explicit InjectionContructor directive.

Is what I am trying to do a supported feature?  It is certainly desirable to have as it eliminates the need to register the same generic type for several similar specializations.  If it is supported, how do instruct Unity to call a specific ctor with an explicit value for a partially closed generic type registration?

May 30, 2012 at 8:30 AM
Edited Jun 1, 2012 at 11:05 PM

I think what you are doing is not supported.  The reason for the failure is that to Unity the partially closed type that was registered is a different type than the closed type that is being resolved so Unity does not find the registration and defaults to invoking the "longest" constructor.

You could do something similar to what you want by using a named registration of the open generic type for each of the first generic type parameter (hopefully that makes sense!):

  container.RegisterType(typeof(BasicRepository<,>), "int", new InjectionConstructor(typeof(Func<>)));
  container.RegisterInstance<Func<int>>("int", new Func<int>(CreateInt));
  var repo = container.Resolve<BasicRepository<int, string>>("int");

You could wrap that in extension methods to make it look more elegant (and less error prone).

Also, if you didn't want to register the Func<int> as an instance then you could wrap that in a factory or provide the Func as a ParameterOverride when resolving:

  var repo2 = container.Resolve<BasicRepository<int, string>>("int",
      new ParameterOverride("generateKey", new Func<int>(CreateInt2)));

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jun 1, 2012 at 10:32 PM

Thank you for your response and suggestions, Randy.  I'm in the process of evaluating how they will work within our application.

It seems like supporting such feature would require an incremental amount of work as we can still register completely open types and resolve them against closed type specializations.  Perhaps something for the team to consider for a future release :)

Jun 1, 2012 at 11:14 PM

Steve, you can make a suggestion on UserVoice for this feature to be included in the next release.  Also, thanks for posting a clear question about a topic that could have been confusing.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com