[英]Using multiple dbcontext instances and dependency injection
這是幾周前我在這里提出的一個類似的問題,其中一個重要的變化需求。
我有一個新的和獨特的(我在stackoverflow搜索中找不到這樣的東西)業務要求:
我創建了兩個獨立的實體框架6 DbContexts,指向兩個結構不同的數據庫,讓我們稱它們為PcMaster和PcSubs。 雖然PcMaster是一個直接的數據庫,而PcMasterContext將有一個靜態連接字符串,但PcSubs數據庫用作模板來創建新的數據庫。 顯然,由於這些復制的數據庫將具有相同的確切結構,因此想法只是在實例化dbcontext時將連接字符串中的數據庫(目錄)名稱更改為指向不同的數據庫。 我還使用了存儲庫模式和依賴注入(目前是Ninject,但考慮轉向Autofac)。
我沒有看到DbContext的IDbContext接口,除非你想自己創建一個。 但后來,我看到很多人說這不是一個好主意,也不是最好的做法。
基本上,我想要做的是,在某些條件下,不僅應用程序需要在PCMasterContext和PCSubsContext之間切換,而且如果PCSubsContext是當前上下文,還要將連接字符串修改為PCSubsContext。 我在存儲庫中使用的dbContext需要指向不同的數據庫。 我不知道如何使用像Ninject或Autofac這樣的IoC容器來做到這一點。 這是我到目前為止創建的一些代碼片段。 非常感謝幫助一些真正的工作解決方案。
這是我的基礎存儲庫的界面
public interface IPCRepositoryBase<T> where T : class
{
void Add(T entity);
void Delete(T entity);
T FindOne(Expression<Func<T, bool>> predicate);
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
IQueryable<T> GetAll();
void SetConnection(string connString);
//...
//...
}
這是我的抽象存儲庫庫
public abstract class PCRepositoryBase<T> : IPCRepositoryBase<T>, IDisposable where T : class
{
protected readonly IDbSet<T> dbSet;
protected DbContext dbCtx;
public PCRepositoryBase(DbContext dbCtx)
{
this.dbCtx = dbCtx;
dbSet = dbCtx.Set<T>();
}
public void SetConnection(string connString)
{
dbCtx.Database.Connection.ConnectionString = connString;
}
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
{
return dbSet.Where(predicate); // DataContext.Set<T>().Where( predicate );
}
public virtual IQueryable<T> GetAll()
{
return dbSet;
}
public T FindOne(Expression<Func<T, bool>> predicate)
{
return dbSet.SingleOrDefault(predicate);
}
//... Not all implementations listed
//...
}
現在,這是一個派生存儲庫的接口:
public interface ISubscriberRepository : IPCRepositoryBase<Subscriber>
{
IQueryable<Subscriber> GetByStatusName( PCEnums.enumSubsStatusTypes status );
IQueryable<Subscriber> GetByBusinessName( string businessName );
//...
//...
}
public class SubscriberRepository : PCRepositoryBase<Subscriber>, ISubscriberRepository
{
public SubscriberRepository( DbContext context ) : base( context ) { }
public IQueryable<Subscriber> GetByStatusName( PCEnums.enumSubsStatusTypes status )
{
return FindBy(x => x.SubsStatusType.Name == status.ToString());
}
public IQueryable<Subscriber> GetByBusinessName( string businessName )
{
return FindBy( s => s.BusinessName.ToUpper() == businessName.ToUpper() );
}
//... other operations omitted for brevity!
}
現在,我的PCSubs dbContext由設計師生成:
public partial class PCSubsDBContext : DbContext
{
public PCSubsDBContext() : base("name=PCSubsDBContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Currency> Currencies { get; set; }
public virtual DbSet<DurationName> DurationNames { get; set; }
public virtual DbSet<Subscriber> Subscribers { get; set; }
}
有沒有辦法,我可以為兩個數據庫使用和/或注入一個通用dbcontext以及不同數據庫的連接字符串。 如何在沒有相應接口的情況下在Ioc容器中注冊“DbContext”,並且仍然能夠在運行時注入連接字符串? 一些代碼示例將真正有所幫助。
解決方案實際上非常簡單。 您需要確保您的PCSubsDBContext
具有一個構造函數,該構造函數接收連接字符串,連接字符串名稱,數據庫名稱或類似名稱。 這樣,您可以根據PCSubsDBContext
在的上下文創建正確的PCSubsDBContext
。注入其ctor的值可能取決於登錄用戶或某個請求。 這是你已經知道該怎么做的事情。
如何注冊取決於您的容器,但您通常必須為此注冊一個委托。 這可能如下所示:
// Autofac
builder.Register<PCSubsDBContext>(c =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
// Ninject
kernel.Bind<PCSubsDBContext>().ToMethod(m =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
// Simple Injector
container.Register<PCSubsDBContext>(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
由於創建上下文取決於請求的可用性,因此甚至可以很好地更改您的設計,以便可以懶惰地加載PCSubsDBContext
,同時您仍然可以在不存在Web請求的情況下構建對象圖的其余部分。 這非常有價值,因為這可以讓您驗證容器的配置 。
解決方案(一如既往)是引入一種新的抽象,例如:
public interface IPcSubsContextProvider
{
PCSubsDBContext Context { get; }
}
現在,您可以在執行期間注入IPcSubsContextProvider
並使用其Context
屬性(而不是在構建對象圖時),而不是將PCSubsDBContext
直接注入到使用者中。 這允許僅在需要時才創建PCSubsDBContext
,並且僅在構建了對象圖的其余部分之后才創建。 實施將是微不足道的:
class LazyPcSubsContextProvider : IPcSubsContextProvider
{
private readonly Lazy<PCSubsDBContext> context;
public LazyPcSubsContextProvider(Func<PCSubsDBContext> factory) {
this.context = new Lazy<PCSubsDBContext>(factory);
}
public PCSubsDBContext Context { get { return this.context.Value; } }
}
可以使用作用域/請求生活方式注冊此實現:
// Autofac
builder.Register<IPcSubsContextProvider>(c =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())))
.InstancePerHttpRequest();
// Ninject
kernel.Bind<IPcSubsContextProvider>().ToMethod(m =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())))
.InRequestScope();
// Simple Injector
container.RegisterPerWebRequest<IPcSubsContextProvider>(() =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())));
在此之后,Simple Injector將使驗證配置變得非常容易:
container.Verify();
它還允許您診斷配置 。
使用其他容器,這將更難做到。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.