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 实体框架替代方案或在运行时指定架构的能力?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET C# MVC 实体框架

ASP .NET MVC 5 - 客户-地址一对一关系

asp.net mvc 模型与实体框架模型

似乎无法使用 ASP.NET MVC 和实体框架返回我的模型

asp.net mvc实体框架代码先将外键与不同名称关联

如何在我的实体框架和 ASP.NET MVC 项目中重构 connectionString?