2017-07-21 126 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 ) –

相關問題