簡體   English   中英

使用多個dbcontext實例和依賴項注入

[英]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.

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