ASP.NET MVC 5 实体框架替代方案或在运行时指定架构的能力?
Posted
技术标签:
【中文标题】ASP.NET MVC 5 实体框架替代方案或在运行时指定架构的能力?【英文标题】:ASP.NET MVC 5 Entity Framework alternatives or ability to specify schema at runtime? 【发布时间】:2017-10-12 01:16:54 【问题描述】:我目前在我的 ASP.NET MVC 5 项目中使用 Entity Framework 6。我们拥有的每个客户端都有自己的数据库模式。据我目前发现,在运行时使用实体框架切换模式并不容易。我创建了一个辅助类,它基本上采用 csdl 和其他文件并执行架构的搜索和替换,但显然这给应用程序增加了很大的性能影响。
有谁知道如何在运行时为 EF 更改架构?如果它不受支持,或者除了我已经完成的工作之外没有可用的解决方法,我将不得不更改我的 ORM 工具。是否还有其他支持这一点的人也允许我从数据库中生成模型?因为从头开始创建所有带有映射的类需要很长时间。
提前致谢。
【问题讨论】:
不确定是否在运行时获取架构,但您可以基于现有数据库创建上下文,以便您可以在应用中添加不同的上下文,然后在运行时在这些上下文之间切换。但通常您会根据指定的模式进行编码,因此您不妨为不同的上下文创建业务类,然后打开运行时。 【参考方案1】:假设以下情况为真:
您正在使用 SQL Server 数据库 您的每个客户都使用单独的数据库用户帐户您可以在 SQL Server 中设置用户的默认架构。
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
【讨论】:
【参考方案2】:我最近需要解决这个问题,以开发一个可靠的多租户连接,该连接不仅能够支持位于不同服务器上的租户数据库(可以使用连接字符串解决),而且还支持每个模式的租户。这里的棘手之处在于您的实体映射需要反映架构名称以解析表。这假设所有租户都使用相同的数据库架构,只是架构名称不同。
在我的情况下,我使用的是 Mehdi.me DbContextScope,它部分地支持了我需要的东西,因为您可以覆盖并提供 IDbContextFactory
实现以使用适当的连接字符串创建您的 DbContexts。第二点我对工厂进行了一些扩展,以确保租户架构细节可以通过实体配置的初始化。
随意看看它是否适合您的项目,或者给您一些想法如何获得您需要的东西。 (https://github.com/StevePy/DbContextScope)
实现需要一些设置,显然已经习惯了 DbContextFactory/DbContextLocator 模式的工作单元,但您大致需要以下设置:
创建一个类来表示实现IDbTenant
的租户连接详细信息。此类将链接到当前租户实例,返回租户表所在位置的连接字符串和架构名称。
在您的项目中实现IDbContextFactory
,它将构造DbContext
实例。这个工厂通常接受一个默认构造函数,一个连接字符串,现在创建IDbTenant
的实例是第 1 步。
初始化您的 IoC(如果存在)以使用在第 2 步中创建的 IDbContextFactory
初始化 DbContextScopeFactory
。这看起来像:
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 将负责其余的工作。因此,例如,给定一个名为“Order”的实体,您将定义一个实体类型配置,如:
public class OrderConfiguration : EntityTypeConfiguration<Order>
[ImportingConstructor]
public OrderConfiguration(IDbTenant tenant)
: base()
ToTable("Orders", tenant.SchemaName);
// HasKey(...);
// HasMany(...);
// etc. etc. etc.
不确定这是否可以适用于代码优先的实现,但我对此表示怀疑,因为该模式想要负责 db 模式定义和迁移,这在模式之间切换时会变得一团糟或服务器。
可能有很多内容需要吸收,尤其是如果您以前没有使用 Mehdi.me DbContextScope 的经验,但希望它能给您一些想法。
一旦设置了这些,与普通 Mehdi.me 模式的唯一变化是,当您使用 DbContextLocator
检索上下文时,将 IDbTenant
实例传递给它:
private MyAppContext Context
get return ContextLocator.Get<MyAppContext>(_tenant);
_tenant 是根据您登录的租户初始化的。 (即租户识别策略从 OWIN 身份验证或会话状态中提取详细信息......)从这里上下文工厂接管并使用租户详细信息初始化您的上下文。
【讨论】:
以上是关于ASP.NET MVC 5 实体框架替代方案或在运行时指定架构的能力?的主要内容,如果未能解决你的问题,请参考以下文章