RandyDaddis
RandyDaddis

Reputation: 1050

closed generic type registration - Autofac – cannot resolve parameter x of constructor

I am getting the following exception:

"None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'NetCore.DAL.EF.Repositories.Core.Common.SystemSettingRepository' can be invoked with the available services and parameters:\r\nCannot resolve parameter 'NetCore.DAL.EF.DatabaseFactory1[NetCore.DAL.EF.AppDbContext] databaseFactory' of constructor 'Void .ctor(NetCore.DAL.EF.DatabaseFactory1[NetCore.DAL.EF.AppDbContext])'."

Here is the Autofac registration info for SystemSettingRepository:

autofacServiceProvider.Non-Public members._lifetimeScope.ComponentRegistry.Registrations[28] = {Activator = SystemSettingRepository (ReflectionActivator), Services = [Dcs.NetCore.ApplicationCore.BLL.Domain.Features.Common.SystemSettings.ISystemSettingRepository, Dcs.NetCore.Infrastructure.Common.IRepository`1[[Dcs.NetCore.ApplicationCore.BLL.Domain.Entities.Common.SystemSetting, Dcs.NetCore.ApplicationCore.BLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], NetCore.DAL.EF.Repositories.Core.Common.SystemSettingRepository], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope}

autofacServiceProvider.Non-Public members._lifetimeScope.ComponentRegistry.Registrations[28].Target.Services[0].ServiceType = {Dcs.NetCore.ApplicationCore.BLL.Domain.Features.Common.SystemSettings.ISystemSettingRepository}

autofacServiceProvider.Non-Public members._lifetimeScope.ComponentRegistry.Registrations[28].Target.Services[1].ServiceType = {Dcs.NetCore.Infrastructure.Common.IRepository`1[Dcs.NetCore.ApplicationCore.BLL.Domain.Entities.Common.SystemSetting]}

autofacServiceProvider.Non-Public members._lifetimeScope.ComponentRegistry.Registrations[28].Target.Services[2].ServiceType = {NetCore.DAL.EF.Repositories.Core.Common.SystemSettingRepository}

As you can see, the services are registered. However, the databaseFactory parameter is not. Here is my code:

public class Startup
{
    …
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        …
        return AutofacBuilder();
    }

    private AutofacServiceProvider AutofacBuilder()
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<AutofacModule_AspNetCore>();
        builder.Populate(_services);
        this.AutofacContainer = builder.Build();
        var autofacServiceProvider = new AutofacServiceProvider(this.AutofacContainer);
        return autofacServiceProvider;
    }
}

public class AutofacModule_AspNetCore : Autofac.Module
{
    protected override void Load(ContainerBuilder builder)
    {
            RegisterDbContexts();
            RegisterComponents();
    }
    private void RegisterComponents()
    {
        _builder.RegisterGeneric(typeof(DatabaseFactory<>))
            .As(typeof(IDatabaseFactory<>))
            .InstancePerDependency();
        _builder.RegisterAssemblyTypes(_assembly)
            .AsClosedTypesOf(typeof(IRepository<>))
            .WithParameter(new ResolvedParameter((p, i) => p.Name == "databaseFactory",
                                                 (p, i) => i.Resolve<DatabaseFactory<AppDbContext>>()))
            //.WithParameter("databaseFactory", new DatabaseFactory<AppDbContext>())
            //.WithParameter(ResolvedParameter//.ForNamed<IDbContext>("coreDomainDbContext"));
            //.WithParameter("databaseFactory", )
            .InstancePerDependency();
    }
    private void RegisterDbContexts()
    {
        RegisterAppDbContextInstance();
    }

    private void RegisterAppDbContextInstance()
    {
        // register DbContextOptionsBuilderHelper
        _builder.RegisterType<AppDbContextOptionsBuilderHelper>()
            .InstancePerDependency();
        // configure DbContextOptions
        var dbContextOptionsBuilderHelper = new AppDbContextOptionsBuilderHelper();
        var dbContextOptionsBuilder = dbContextOptionsBuilderHelper.GetDbContextOptionsBuilder();
        // register DbContext
        _builder.RegisterType<AppDbContext>()
            .WithParameter("options", dbContextOptionsBuilder.Options)
            .InstancePerDependency();
    }
}

public class DatabaseFactory<T> : Disposable, IDatabaseFactory<T>
        where T : DbContext //, new()
{
    private T _dbContext;
    private AppDbContextOptionsBuilderHelper _appDbContextOptionsBuilderHelper;
    private DefaultDbContextOptionsBuilderHelper _defaultDbContextOptionsBuilderHelper;

    public DatabaseFactory()
    {
        this._appDbContextOptionsBuilderHelper = new AppDbContextOptionsBuilderHelper();  // TODO: refactor to ctor injection
        this._defaultDbContextOptionsBuilderHelper = new DefaultDbContextOptionsBuilderHelper();  // TODO: refactor to ctor injection
    }
    
    public T Get()
    {
        if (_dbContext == null)
            Create();
         return _dbContext;
    }

    private void Create()
    {
        switch (typeof(T).Name)
        {
            case "AppDbContext":
            {
                CreateAppDbContext();
                break;
            }
            case "DefaultDbContext":
            {
                CreateDefaultDbContext();
                break;
            }
        }
    }
    private void CreateAppDbContext()
    {
        var dbContextOptionsBuilder = _appDbContextOptionsBuilderHelper.GetDbContextOptionsBuilder();
        this._dbContext = new AppDbContext(dbContextOptionsBuilder.Options) as T;
    }
    //protected override void DisposeCore()
    //{
    //
        // cref: Autofac.Util.Disposable
        //
        //    //TODO: should I override Autofac.Util.Disposable here?
        //    var msg = "DatabaseFactory.DisposeCore() executing";
        //    if (_dbContext != null)
        //        _dbContext.Dispose();
    //}
}

public partial class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    { }
    public AppDbContext()
    {}
}

public class SystemSettingRepository : RepositoryBase<SystemSetting, AppDbContext>, ISystemSettingRepository
{
    public SystemSettingRepository(DatabaseFactory<AppDbContext> databaseFactory)
            : base(databaseFactory)
    { }
}

public interface ISystemSettingRepository : IRepository<SystemSetting>
{}

public partial interface IRepository<T> where T : class
{}

public abstract class RepositoryBase<T, U> : IRepositoryBase<T, U>
        where T : class
        where U : DbContext //, new()
{
    private U _dbContext;
    private readonly DbSet<T> _dbset;
    protected RepositoryBase(DatabaseFactory<U> databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        _dbset = DataContext.Set<T>();
    }
    protected virtual DatabaseFactory<U> DatabaseFactory
    {
        get;
        private set;
    }

    protected virtual U DataContext
    {
        get { return _dbContext = DatabaseFactory.Get(); }
    }

    public virtual async Task<T> AddAsync(T dao)
    {
        EntityEntry<T> entityEntry = _dbset.Add(dao);
        var result = await SaveChangesAsync();
        if (result > 0 && entityEntry != null && entityEntry.Entity != null)
            return entityEntry.Entity;
        else
            return null;
    }
}

public interface IRepositoryBase<T, U> 
        where T : class
        where U : DbContext //, new()
{ }

Upvotes: 1

Views: 779

Answers (1)

KozhevnikovDmitry
KozhevnikovDmitry

Reputation: 1720

The issue is caused by the way of registration of DatabaseFactory<>. This type is registered as an interface IDatabaseFactory<>. But it is resolved as itself in lambda argument of method WithParameter() on registration of repositories:

 _builder.RegisterAssemblyTypes(_assembly)
        .AsClosedTypesOf(typeof(IRepository<>))
        .WithParameter(new ResolvedParameter((p, i) => p.Name == "databaseFactory",
                                             // resolving type it self
                                             // while it was registered as interface
                                             (p, i) => i.Resolve<DatabaseFactory<AppDbContext>>()))

Autofac doesn't know how to resolve it, because it doesn't have corresponding registration. To make your code work you can resolve DatabaseFactory as an inteface like this:

.WithParameter(new ResolvedParameter((p, i) => p.Name == "databaseFactory",
                                     (p, i) => i.Resolve<IDatabaseFactory<AppDbContext>>()))

Or add AsSelf() call to registration:

_builder.RegisterGeneric(typeof(DatabaseFactory<>))
        .As(typeof(IDatabaseFactory<>))
        // Register also as DatabaseFactory<>
        .AsSelf()
        .InstancePerDependency();

Upvotes: 3

Related Questions