Search code examples
c#dependency-injectionsimple-injector

CreateRegistration with generic parameter


I have an generic implementation of my entity reader and writer. I want to create an registration of these to the same instance. But this fails with the following exception:

An exception of type 'System.InvalidOperationException' occurred in mscorlib.dll but was not handled in user code

var registration = Lifestyle.Scoped.CreateRegistration(typeof (EntityFrameworkRepository<>), container);
container.AddRegistration(typeof (IUnitOfWork), registration);
container.AddRegistration(typeof (IEntityWriter<>), registration);
container.AddRegistration(typeof (IEntityReader<>), registration);

My implementation type signature is as follows:

internal sealed class EntityFrameworkRepository<TEntity> : IEntityWriter<TEntity>, IEntityReader<TEntity>, IUnitOfWork where TEntity : Entity 
{
     // ...
}

Is generics not supported in the CreateRegistation(..) or should I just wire them up like (probably will throw an lifestyle warning)?

container.Register(typeof (IUnitOfWork), typeof (EntityFrameworkRepository<>), Lifestyle.Scoped);
container.Register(typeof (IEntityWriter<>), typeof (EntityFrameworkRepository<>), Lifestyle.Scoped);
container.Register(typeof (IEntityReader<>), typeof (EntityFrameworkRepository<>), Lifestyle.Scoped);

Solution

  • I think we're missing a few pre condition checks here, because you can't create Registration objects for open-generic types. A Registration instance is specific for a closed-generic or non-generic type. This means that both CreateRegistration(typeof(EntityFrameworkRepository<>), ... and AddRegistration(typeof(IUnitOfWork), ... are not valid calls.

    I understand what you are trying to do, you want to have the same repository instance during a request, independently whether someone uses it as a writer or a reader. That is obvious, because Simple Injector will throw an exception telling you that you have a Torn Lifestyle.

    To apply this design, you will have to define two generic adapters, from each generic abstraction to the generic repository as follows:

    public class EnityWriterAdapter<T> : IEntityWriter<T>
    {
        public EntityWriterAdapter(EntityFrameworkRepository<T> repository) { ... }
    }
    
    public class EnityReaderAdapter<T> : IEntityReader<T>
    {
        public EnityReaderAdapter(EntityFrameworkRepository<T> repository) { ... }
    }
    

    Now the registration can become the following:

    container.Register(typeof(EntityFrameworkRepository<>),
        typeof(EntityFrameworkRepository<>), Lifestyle.Scoped);
    container.Register(typeof(IEntityWriter<>), typeof(EntityWriterAdapter<>));
    container.Register(typeof(IEntityReader<>), typeof(EntityReaderAdapter<>));
    

    Note that I'm unsure what to do with the IUnitOfWork registration, because it's unclear to me which closed EntityFrameworkRepository<T> should get injected into consumers that expect IUnitOfWork. There seems to be some ambiguity in this.