ASP.NET MVC 5实体框架的替代品或在运行时指定架构的能力?

问题描述:

我目前在我的ASP.NET MVC 5项目中使用实体框架6。我们每个客户都有自己的数据库模式。就我迄今发现的情况而言,使用实体框架在运行时切换模式并不容易。我创建了一个基本上接受csdl和其他文件的帮助类,并进行搜索和替换模式,但显然这会增加对应用程序的巨大性能影响。ASP.NET MVC 5实体框架的替代品或在运行时指定架构的能力?

有谁知道如何在EF运行时更改模式?如果它不被支持,或者除了我已经完成的工作外没有其他可用的解决办法,我将不得不更改我的ORM工具。有没有其他人支持这一点,也让我生成模型关闭数据库?因为从头开始创建所有具有映射的类并且需要很长时间。

在此先感谢。

+1

不确定在运行时获取架构,但可以基于现有数据库创建上下文,以便在应用中添加不同的上下文,然后在运行时间之间切换。但通常情况下,您将根据指定的模式进行编码,因此您不妨为不同的上下文创建业务类并切换运行时。 –

假设以下为真:

  • 您使用的是SQL Server数据库
  • 每个客户都使用单独的数据库中的用户帐户

您可以在SQL用户的默认架构服务器。

https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-user-transact-sql

How to set the default schema of a database in SQL Server 2005?

,然后在运行时间为每个客户端改变实体框架的连接字符串。

Entity Framework change connection at runtime

我最近需要解决这个问题,开发能够支持不仅租户数据块位于不同的服务器上(可以使用连接字符串来解决)了可靠的多租户连接,但还支持租户-per-架构。这里棘手的一点是您的实体映射需要反映用于解析表的模式名称。这假定所有租户使用相同的数据库模式,只是不同的模式名称。

在我的情况下,我使用的是Mehdi.me DbContextScope,它部分支持我需要的功能,因为您可以覆盖并提供IDbContextFactory实现来使用适当的连接字符串创建DbContext。第二位我为工厂构建了一些扩展,以确保租户模式详细信息能够通过实体配置的初始化。

随意看看它是否适合您的项目,或给你一些想法如何得到你需要的东西。 (https://github.com/StevePy/DbContextScope

的实施需要多一点的设置的,显然已经习惯了的DbContextFactory/DbContextLocator模式工作的单位,但你大概需要以下设置:

  1. 创建一个类来表示你的租户连接细节实施IDbTenant。这个类将被链接到当前租户实例,返回租户表所在的连接字符串和架构名称。

  2. IDbContextFactory在您的项目中的实现将构造DbContext实例。这家工厂正常接受默认的构造函数,一个连接字符串,现在创造了IDbTenant的一个实例是第1步

  3. 如果存在初始化你的IoC初始化DbContextScopeFactory在步骤2中创建的IDbContextFactory这看起来会是像:

ioc.Register<IDbContextScopeFactory>(()=> {new DbContextScopeFactory(new SqlServerTenantDbContextFactory());});

SqlServerTenantDbContextFactory是在步骤2中的上面创建的实现是大致为Autofac IoC的注册过程。基本上你只是想确保一个DbContextScopeFactory被实例化时,你提供它你的DbContextFactory

ContextFactory的实现。

public class SqlServerTenantDbContextFactory : IDbContextFactory 
{ 
    TDbContext IDbContextFactory.CreateDbContext<TDbContext>() 
    { 
     return (TDbContext)Activator.CreateInstance<TDbContext>(); 
    } 

    TDbContext IDbContextFactory.CreateDbContext<TDbContext>(IDbTenant tenant) 
    { 
     var connection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection(); 
     // based on the provider set up in <providers> configration under <entityFramework>... 
     connnection.ConnectionString = tenant?.ConnectionString; 
     return (TDbContext)Activator.CreateInstance(typeof(TDbContext), tenant, connection, true); 
    } 


    TDbContext IDbContextFactory.CreateDbContext<TDbContext>(string connectionString) 
    { 
     var connection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection(); 
     connnection.ConnectionString = connectionString; 
     return (TDbContext)Activator.CreateInstance(typeof(TDbContext), connection, true); 
    } 
} 

最后配置变化是EntityTypeConfiguration添加一个接受IDbTenant一个构造函数和[ImportingConstructor]属性添加到该构造函数。 ContextFactory将负责其余部分。因此,例如,给所谓的“订单”的实体,你会这样定义的实体类型配置:

public class OrderConfiguration : EntityTypeConfiguration<Order> 
{ 
    [ImportingConstructor] 
    public OrderConfiguration(IDbTenant tenant) 
     : base() 
    { 
     ToTable("Orders", tenant.SchemaName); 
     // HasKey(...); 
     // HasMany(...); 
     // etc. etc. etc. 
    } 
} 

不知道这是否可以适应服务代码优先的实现,但我对此表示怀疑,因为这模式想要承担数据库模式定义和迁移的责任,当涉及模式或服务器之间的切换时,这将会是一团糟。

这可能需要很多东西,特别是如果你以前没有使用Mehdi.me DbContextScope的经验,但希望它可以给你一些想法。

一旦这些设置,从正常Mehdi.me模式唯一的变化是,当你去使用DbContextLocator检索的背景下,通过它的IDbTenant实例的实例:

private MyAppContext Context 
{ 
    get { return ContextLocator.Get<MyAppContext>(_tenant); } 
} 

其中_tenant根据您登录的租户进行初始化。 (即租户识别策略从OWIN身份验证或会话状态中提取详细信息...)从这里上下文工厂接管并使用租户详细信息初始化您的上下文。