簡體   English   中英

根據Domain類將不同的DbContext注入通用存儲庫-Autofac

[英]Inject different DbContexts into generic repository based on Domain class - Autofac

在我的應用程序中,我需要與兩個數據庫進行交互。 我有兩個位於兩個不同數據庫中的域類。 我也有一個通用的存儲庫模式,該模式在其構造函數中接受一個UoW。 我正在尋找一種基於Domain類注入適當的UoW的方法。 我不想為第二個數據庫編寫第二個通用存儲庫。 有什么整潔的解決方案嗎?

public interface IEntity
{
    int Id { get; set; }
}

位於數據庫A中

public class Team: IEntity
{
    public int Id { get; set; }
    public string Name{ get; set; }

}

位於數據庫B中

public class Player: IEntity
{
    public int Id { get; set; }
    public string FullName { get; set; }
}

我在UoW中也有一個通用的存儲庫模式

public interface IUnitOfWork
{
    IList<IEntity> Set<T>();
    void SaveChanges();
}

public class DbADbContext : IUnitOfWork
{
    public IList<IEntity> Set<T>()
    {
        return new IEntity[] { new User() { Id = 10, FullName = "Eric Cantona" } };
    }

    public void SaveChanges()
    {

    }
}

public class DbBDataContext: IUnitOfWork
{
    public IList<IEntity> Set<T>()
    {
        return new IEntity[] { new Tender() { Id = 1, Title = "Manchester United" } };
    }

    public void SaveChanges()
    {

    }

public interface IRepository<TEntity> where TEntity: class, IEntity
{
    IList<IEntity> Table();
}

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{

    protected readonly IUnitOfWork Context;
    public BaseRepository(IUnitOfWork context)
    {
        Context = context;
    }

    IList<IEntity> IRepository<TEntity>.Table()
    {
        return Context.Set<TEntity>();
    }
}

我已經找到文章說Autofac用最后一個值覆蓋注冊。 我知道我的問題是如何注冊DbContext。

 var builder = new ContainerBuilder();
 // problem is here
        builder.RegisterType<DbADbContext >().As<IUnitOfWork>()
        builder.RegisterType<DbBDbContext >().As<IUnitOfWork>()

 builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>));
        var container = builder.Build();

那這個呢:

builder.RegisterType<DbContextBase>().As<IUnitOfWork>()

    DbADataContext: DbContextBase,IUnitOfWork
    DbBDataContext: DbContextBase,IUnitOfWork

或者在注冊時,您可以執行以下操作:

containerBuilder.RegisterGeneric(typeof(DbADataContext<>)).Named("DbADataContext", typeof(IUnitOfWork<>));
containerBuilder.RegisterGeneric(typeof(DbBDataContext<>)).Named("DbBDataContext", typeof(IUnitOfWork<>));

如果要保留單個BaseRepository及其接口,則必須以某種方式進行配置,其中實體將由哪個DbContext處理。 可以在應用程序的注冊部分完成此操作,但是在那種情況下,您不能將BaseRepostory<T>注冊為開放的泛型,而是在您的注冊中是明確的,如下所示:

containerBuilder.RegisterType<DbADataContext>().Named<IUnitOfWork>("A");
containerBuilder.RegisterType<DbBDataContext>().Named<IUnitOfWork>("B");

containerBuilder.Register(c => new BaseRepository<Team>(c.ResolveNamed<IUnitOfWork>("A")).As<IRepostory<Team>>();
containerBuilder.Register(c => new BaseRepository<Player>(c.ResolveNamed<IUnitOfWork>("B")).As<IRepository<Player>>();

(只是概念證明,未經測試的代碼)

Autofac不夠智能,無法“自動”知道要在每個存儲庫中使用哪個工作單元。

我從@tdragon的答案中得到啟發。

第一步是注冊名為DbContext

        builder.RegisterType<Database1>()
            .Keyed<IUnitOfWork>(DbName.Db1)
            .Keyed<DbContext>(DbName.Db1).AsSelf().InstancePerRequest();

        builder.RegisterType<Database2>()
            .Keyed<IUnitOfWork>(DbName.Db2)
            .Keyed<DbContext>(DbName.Db2).AsSelf().InstancePerRequest();

請注意, DbName只是一個枚舉。

以下代碼掃描數據訪問層程序集以查找Domain類。 然后,它注冊ReadOnlyRepository和BaseRepository。 該代碼的位置在DIConfig

Type entityType = typeof(IEntity);
var entityTypes =   Assembly.GetAssembly(typeof(IEntity))
                    .DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(entityType));


var baseRepoType = typeof(BaseRepository<>);
var readOnlyRepoType = typeof(ReadOnlyRepository<>);
var baseRepoInterfaceType = typeof(IRepository<>);
var readOnlyRepoInterfaceType = typeof(IReadOnlyRepository<>);
var dbContextResolver = typeof(DbContextResolverHelper).GetMethod("ResolveDbContext");

foreach (var domainType in entityTypes)
{
  var baseRepositoryMaker = baseRepoType.MakeGenericType(domainType);
  var readonlyRepositoryMarker = readOnlyRepoType.MakeGenericType(domainType);

 var registerAsForBaseRepositoryTypes = baseRepoInterfaceType.MakeGenericType(domainType);
 var registerAsForReadOnlyRepositoryTypes = readOnlyRepoInterfaceType.MakeGenericType(domainType);

 var dbResolver = dbContextResolver.MakeGenericMethod(domainType);
            // register BaseRepository
 builder.Register(c => Activator.CreateInstance(baseRepositoryMaker, dbResolver.Invoke(null, new object[] { c }))
            ).As(registerAsForBaseRepositoryTypes).InstancePerRequest(jobTag);
            //register readonly repositories
 builder.Register(c => Activator.CreateInstance(readonlyRepositoryMarker, dbResolver.Invoke(null, new object[] { c })))
           .As(registerAsForReadOnlyRepositoryTypes).InstancePerRequest(jobTag);

}

以下方法嘗試在每個DbContext中查找DbSet,以找出屬於哪個DataContext / Database的域類。

public class DbContextResolverHelper
{
    private static readonly ConcurrentDictionary<Type, DbName> TypeDictionary = new ConcurrentDictionary<Type, DbName>();


    public static DbContext ResolveDbContext<TEntity>(IComponentContext c) where TEntity : class, IEntity
    {
        var type = typeof(DbSet<TEntity>);


        var dbName = TypeDictionary.GetOrAdd(type, t =>
        {

            var typeOfDatabase1 = typeof(Database1);
            var entityInDatabase1 = typeOfDatabase1 .GetProperties().FirstOrDefault(p => p.PropertyType == type);
            return entityInDatabase1 != null ? DbName.Db1: DbName.Db2;


        });

        return c.ResolveKeyed<DbContext>(dbName);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM