代码之家  ›  专栏  ›  技术社区  ›  Mahdi

基于域类Autofac将不同的DBContext注入通用存储库

  •  1
  • Mahdi  · 技术社区  · 7 年前

    在我的应用程序中,我需要与两个数据库交互。我有两个域类,它们位于两个不同的数据库中。我还有一个通用的存储库模式,它在其构造函数中接受UoW。我正在寻找一种基于域类注入适当UoW的方法。 有什么好办法吗?

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

    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; }
    }
    

    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();
    
    3 回复  |  直到 7 年前
        1
  •  1
  •   Mahdi    7 年前

    我从@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

    以下代码扫描数据访问层程序集以查找域类。然后,它注册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);
        }
    }
    
        2
  •  0
  •   Zerghouni Idriss    7 年前

    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<>));
    
        3
  •  0
  •   tdragon    7 年前

    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>>();
    

    (只是概念证明,代码未经测试)