2017-07-21 117 views
2

在我的应用程序中,我需要与两个数据库进行交互。我有两个域类位于两个不同的数据库中。我也有一个通用的存储库模式,它的构造函数接受一个UoW。我正在寻找一种方法来基于Domain类注入适当的UoW。 我不想为第二个数据库编写第二个通用存储库。。有没有简洁的解决方案?根据域类将不同的DbContext注入到通用存储库中 - Autofac

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覆盖了最后一个值的注册。我知道我的问题是如何注册DbContexts。

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

回答

1

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

} 

以下方法尝试找到DbSet每个的DbContext为了找出域类所属的DataContext /数据库。

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

这个什么:你可以这样做

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<>)); 
+0

它不会工作,第一个不符合我的观点。 第二个也不是解决方案,因为解决方案应取决于存储库中的“TEntity”的类型 有关命名和元数据的更多信息,请参见此处 http://docs.autofac.org/zh/latest/ faq/select-by-context.html#option-4-use-metadata – Mahdi

0

如果你想保持单身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不够智能,无法“自动”知道要在每个存储库中使用哪个工作单元。

+0

该解决方案迫使我为每个域类注册Repository。这对我来说很难,因为我有超过200个域类 – Mahdi

+1

@Mahdi解决这个问题,你可以使用程序集扫描阅读[这个文档](http://docs.autofac.org/en/latest/register/scanning.html ) –

相关问题