2016-11-18 63 views
4

想想看,我有一個.NET核心Web應用程序配置EF:允許最終用戶在運行時切換的實體框架提供

services.AddDbContext<ApplicationDbContext>(options => 
    options.UseSqlServer(...)); 

我還可以下載一個軟件包,例如SQLite的支持:

services.AddDbContext<ApplicationDbContext>(options => 
    options.UseSqlite(...)); 

我們如何讓用戶在應用安裝時「選擇」提供商?我的意思是 - 例如,在WordPress中,您可以從下拉列表中進行選擇。

這是可能在.NET核心?我看到的唯一方法是重新啓動應用程序只...

+0

什麼是用例?從我的觀點來看,這似乎毫無意義。對於一個CMS或博客,你永遠不會「只」改變數據庫類型,而不需要關閉/重新啓動應用程序 – Tseng

+0

是的 - 但「第一次安裝」時,你可以選擇數據庫提供商。 我希望允許客戶端決定使用哪個提供程序 - 無需修改源代碼。 – pwas

+2

好吧,雖然技術上可以從應用程序中編輯project.json,然後重新啓動應用程序,但我認爲從安全角度來看這不是一個明智之舉。在wordpress中,安裝腳本隨後被刪除,這在編譯系統中更難以解決,並且在那裏出現安全問題。當然,沒有人強迫你在啓動時使用'AddDbContext',並通過一個抽象工廠解決你的數據庫上下文,該工廠讀取一個配置值並選擇正確的提供者並手動構建它,但是你需要自己管理它的一生即處置它) – Tseng

回答

2

這裏是你如何能實現DbContextFactoryDbContextProxy<T>這將創建正確的供應商和返回的例子它。

public interface IDbContextFactory 
{ 
    ApplicationContext Create(); 
} 

public class DbContextFactory() : IDbContextFactory, IDisposable 
{ 
    private ApplicationContext context; 
    private bool disposing; 

    public DbContextFactory() 
    { 
    } 

    public ApplicationContext Create() 
    { 
     if(this.context==null) 
     { 
      // Get this value from some configuration 
      string providerType = ...; 
      // and the connection string for the database 
      string connectionString = ...; 

      var dbContextBuilder = new DbContextOptionsBuilder(); 
      if(providerType == "MSSQL") 
      { 
       dbContextBuilder.UseSqlServer(connectionString); 
      } 
      else if(providerType == "Sqlite") 
      { 
       dbContextBuilder.UseSqlite(connectionString); 
      } 
      else 
      { 
       throw new InvalidOperationException("Invalid providerType"); 
      } 

      this.context = new ApplicationContext(dbContextBuilder); 
     } 

     return this.context; 
    } 

    public void Dispose(){ 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing){ 
     if (disposing){ 
      disposing?.Dispose(); 
     } 
    } 
} 

另外,還要確保你實施一次性模式如上圖所示,這樣的背景下被儘快工廠被佈置佈置,防止的DbContext在內存中,一旦剩餘時間超過必要的和免費的非託管資源儘可能。

最後註冊的工廠爲範圍的,因爲你將上下文本身:

services.AddScopedd<IDbContextFactory, DbContextFactory>(); 

更先進和通用的/可擴展的方法是通過創建一個IDbContextProxy<T>類使用位反射,以獲得正確的構造和DbContextOptionsBuilder

還可以創建IDbContextBuilder,它抽象提供者的創建。

public class SqlServerDbContextBuilder IDbContextBuilder 
{ 
    public bool CanHandle(string providerType) => providerType == "SqlServer"; 

    public T CreateDbContext<T>(connectionString) 
    { 
     T context = ... // Create the context here 

     return context; 
    } 
} 

然後你就可以選擇正確的供應商W /只是做

// Inject "IEnumerable<IDbContextBuilder> builders" via constructor 
var providerType = "SqlServer"; 
var builder = builders.Where(builder => builder.CanHandle(providerType)).First(); 
var context = builder.CreateDbContext<ApplicationContext>(connectionString); 

,並增加新的類型提供的OA硬編碼if/elseswitch塊是那麼容易,因爲添加的依賴和XxxDbContextBuilder類。

請參閱here,herehere以獲取有關此方法和類似方法的更多信息。

+0

謝謝,這看起來不錯! – pwas

0

我想你可以使用使用您指定的數據庫上下文的存儲庫,您可以傳遞參數給上下文構造函數來選擇端點。我不確定這一點,但它可能適合你的情況。

我跟着這篇文章庫模式,我建議閱讀:)

http://cpratt.co/generic-entity-base-class/