Search code examples
c#genericsautofacunit-of-work

Register partically closed generic type with Autofac


I have UnitofWork class and it implement IUnitOfWork. I try to register that with Autofac:

var builder = new ContainerBuilder();
builder
    .RegisterGeneric(typeof(UnitOfWork<Repository<>,>))
    .As(typeof(IUnitOfWork))
    .InstancePerDependency();

Implementation is:

public class UnitOfWork<T, O> : IUnitOfWork
    where T : Repository<O>
    where O : BaseEntity
{
}

public interface IUnitOfWork : IDisposable
{
    void SaveChanges();
}

Gives an error "Type expected"

but this one work on another project:

public class Repository<T> : GenericRepository<T> 
    where T : BaseEntity
{
    public Repository(IDbContext context) : base(context) { }   
}

public abstract class GenericRepository<T> 
    : IRepository<T>, IQueryable<T> where T : BaseEntity
{
}

builder
    .RegisterGeneric(typeof(Repository<>))
    .As(typeof(IRepository<>))
    .InstancePerHttpRequest();

Solution

  • You cannot have partially opened classes (e.g. with UnitOfWork<Repository<>,> you have specified T but not O) inside a typeof, try it with:

    var builder = new ContainerBuilder();
    builder
        .RegisterGeneric(typeof(UnitOfWork<,>))
        .As(typeof(IUnitOfWork))
        .InstancePerDependency();
    

    The where T : Repository<O> generic constraint will take care of that the first argument should be an Repository<>

    But it won't work with RegisterGeneric because it requires a generic interface so need to create a IUnitOfWork<T,O>

    But your model is very strange. Why does your UnitOfWork need a Repository<> type argument?

    Instead of having it as a type argument you can get an Repository<> in your UnitOfWork<E> constructor:

    public class UnitOfWork<E> : IUnitOfWork<E> where E : BaseEntity
    {
        private readonly Repository<E> repository;
    
        public UnitOfWork(Repository<E> repository)
        {
            this.repository = repository;
        }
    
        //.. other methods
    
    }
    

    Where IUnitOfWork<E>

    public interface IUnitOfWork<E> : IDisposable where E : BaseEntity
    {
        void SaveChanges();
    }
    

    And the Autofac registration:

    var builder = new ContainerBuilder();
    builder
        .RegisterGeneric(typeof(Repository<>)).AsSelf();
    builder
        .RegisterGeneric(typeof(UnitOfWork<>))
        .As(typeof(IUnitOfWork<>))
        .InstancePerDependency();
    var container = builder.Build();
    
    // sample usage
    var u = container.Resolve<IUnitOfWork<MyEntity>>();