[Dev Tip] Unity with generic type


ASK:

I’m trying to implement a repository solution for Entity Framework but I am having trouble registering the types that include generics using Unity.

Given:

    // IRepository interface
    public interface IRepository<TEntity>
    {
        // omitted for brevity
    }

    // Repository implementation
    public class Repository<TEntity, TContext> : IRepository<TEntity>, IDisposable 
            where TEntity : class
            where TContext : DbContext
    {
        // omitted for brevity
    }

    // service layer constructor
    public MyServiceConstructor(IRepository<Account> repository)
    {
        _repository = repository;
    }

I need to register the type mapping for IRepository to Repository. but I’m having trouble with the Unity syntax for this kind of mapping.

I have tried the following with no luck:

container.RegisterType<IRepository<>, typeof(Repository<,>)>();
container.RegisterType<typeof(IRepository<>), Repository<,>>();

EDIT

Based, on @Steven response I have the following implementation now:

// UnityRepository implementation   
public class UnityRepository<TEntity> : Repository<TEntity, MyDbContextEntities>
        where TEntity : class
{
    public UnityRepository() : base(new MyDbContextEntities()) { }
}

// Unity bootstrapper
container.RegisterType(typeof(IRepository<>), typeof(UnityRepository<>));

ANSWER:

You are trying to do the following:

container.RegisterType(typeof(IRepository<>), typeof(Repository<,>));

This would normally work, but won’t do the trick in this case, since there is IRepository<TEntity> has one generic argument and Repository<TEntity, TContext> has two, and Unity (obviously) can’t guess what type it should fill in for TContext.

What you need is this:

container.RegisterType(
    typeof(IRepository<>),
    typeof(Repository<, MyDbContextEntities>));

In other words, you’d want to supply the Repository<TEntity, TContext> as a partial open generic type (with one parameter filled in). Unfortunately, the C# compiler does not support this.

But even if the C# did support this, Unity doesn’t support partial open generic types. In fact most IoC frameworks don’t support this. And for that one framework that does support it, you would still have to do the following (nasty thing) to create the partial open generic type:

Type myDbContextEntitiesRepositoryType =
    typeof(Repository<,>).MakeGenericType(
        typeof(Repository<,>).GetGenericParameters().First(),
        typeof(MyDbContextEntities);

There’s a easy trick work around to get this to work though: define a derived class with one generic type:

// Special implementation inside your Composition Root
public class UnityRepository<TEntity> : Repository<TEntity, MyDbContextEntities>
        where TEntity : class
{
    public UnityRepository([dependencies]) : base([dependencies]) { }
}

Now we can easily register this open generic type:

container.RegisterType(typeof(IRepository<>), typeof(UnityRepository<>));

COMMENTS:

Thanks! Please see my edit above. Do you recommend placing the UnityRepository implementation in the composition root/unity bootstrapper, or in a separate class file within the Repository layer? I currently have it in the latter. And when you say public UnityRepository([dependencies]) : base([dependencies]) { }, I assume you mean public UnityRepository(TEntity entity) : base(new MyDbContextEntities()) { }. I currently have it setup as public UnityRepository(TEntity entity) : base(new MyDbContextEntities()) { } and it’s working fine. –  Mark Erasmus Sep 18 ’13 at 20:56
Since this class is tiny and just an implementation detail of the Composition Root, I usually create them as private nested classes inside the bootstrapper class. –  Steven Sep 19 ’13 at 7:03
@MarkErasmus: Since the UnityRepository is just a helper class, you typically want it to have the same constructor arguments as the original class. This way you can still let the container do the wiring for you. So I would expect the UnityRepository to accept a MyDbContextEntities, or in your case probably no arguments at all. Passing in a TEntity is odd, since you shouldn’t use your container to create entities at all. –  Steven Sep 19 ’13 at 7:06
Perfect, makes sense, thanks. –  Mark Erasmus Sep 19 ’13 at 7:25
Have a solution for design time configuration? XML? –  Issa Fram Jun 17 at 17:29
@IssaFram: Yes, the solution is simple: don’t configure your container through XML. No seriously: don’t do it. – Steven Jun 17 at 17:42
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s