ASP.NET MVC 5实体框架的替代品或在运行时指定架构的能力?
我目前在我的ASP.NET MVC 5项目中使用实体框架6。我们每个客户都有自己的数据库模式。就我迄今发现的情况而言,使用实体框架在运行时切换模式并不容易。我创建了一个基本上接受csdl和其他文件的帮助类,并进行搜索和替换模式,但显然这会增加对应用程序的巨大性能影响。ASP.NET MVC 5实体框架的替代品或在运行时指定架构的能力?
有谁知道如何在EF运行时更改模式?如果它不被支持,或者除了我已经完成的工作外没有其他可用的解决办法,我将不得不更改我的ORM工具。有没有其他人支持这一点,也让我生成模型关闭数据库?因为从头开始创建所有具有映射的类并且需要很长时间。
在此先感谢。
假设以下为真:
- 您使用的是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?
,然后在运行时间为每个客户端改变实体框架的连接字符串。
我最近需要解决这个问题,开发能够支持不仅租户数据块位于不同的服务器上(可以使用连接字符串来解决)了可靠的多租户连接,但还支持租户-per-架构。这里棘手的一点是您的实体映射需要反映用于解析表的模式名称。这假定所有租户使用相同的数据库模式,只是不同的模式名称。
在我的情况下,我使用的是Mehdi.me DbContextScope,它部分支持我需要的功能,因为您可以覆盖并提供IDbContextFactory
实现来使用适当的连接字符串创建DbContext。第二位我为工厂构建了一些扩展,以确保租户模式详细信息能够通过实体配置的初始化。
随意看看它是否适合您的项目,或给你一些想法如何得到你需要的东西。 (https://github.com/StevePy/DbContextScope)
的实施需要多一点的设置的,显然已经习惯了的DbContextFactory/DbContextLocator模式工作的单位,但你大概需要以下设置:
创建一个类来表示你的租户连接细节实施
IDbTenant
。这个类将被链接到当前租户实例,返回租户表所在的连接字符串和架构名称。IDbContextFactory
在您的项目中的实现将构造DbContext
实例。这家工厂正常接受默认的构造函数,一个连接字符串,现在创造了IDbTenant
的一个实例是第1步如果存在初始化你的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身份验证或会话状态中提取详细信息...)从这里上下文工厂接管并使用租户详细信息初始化您的上下文。
不确定在运行时获取架构,但可以基于现有数据库创建上下文,以便在应用中添加不同的上下文,然后在运行时间之间切换。但通常情况下,您将根据指定的模式进行编码,因此您不妨为不同的上下文创建业务类并切换运行时。 –