2016-11-18 36 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/