在 EF6 中更改数据库架构的最简洁方法

Posted

技术标签:

【中文标题】在 EF6 中更改数据库架构的最简洁方法【英文标题】:Cleanest way to change database schema in EF6 【发布时间】:2016-08-15 01:41:22 【问题描述】:

目前我们正在mvc5中写一个页面,一个oracle sql数据库连接entitiy framework 6。

我们目前在 oracle 数据库中有两种模式,一种用于测试,另一种用于开发。实体框架中的模型是从开发数据库生成的,并且可以与它完美配合。

问题来了,将连接字符串更改为测试模式时。更改连接字符串后,应用程序无法找到表(因为它们仍然引用开发方案)。

目前我可以通过从模型中删除所有表并从正确的架构重新创建模型或手动编辑引用该架构的每个文件来解决此问题。这两种解决方案都有些令人厌烦且容易出错。

这种情况通常如何处理?

编辑

似乎更改数据库并保留架构不会产生任何错误。所以这只是架构相关的。

【问题讨论】:

您可以选择更改为代码优先吗?在代码优先中,您可以在映射中指定架构。 @GertArnold 我可以试试。所以我可以从连接字符串中读取模式,并在映射中以编程方式设置它? 不,模式不是由连接字符串设置的,但是您可以自己在配置文件中添加任何条目,您可以通过它来设置模式。 @GertArnold 代码首先的问题是,为了创建表,它需要创建数据库。数据库必须保持不变,因为某些模式用于其他系统。 您可以使用代码优先而无需迁移。这只意味着当你改变数据库时,你也改变了模型。这并不像听起来那么糟糕,也许甚至比更新 edmx 更好,因为您 100% 处于控制之中。此外,对 edmx 的支持将在 EF7 中消失。 【参考方案1】:

我想这是使用entity framework command interceptors 的完美用例。我刚刚尝试过,它运行良好,即使对于实体框架 DB-First 方法也是如此。

您可以像这样注册自定义命令拦截器:

DbInterception.Add(new ReplaceSchemaInterceptor(newSchema: "[my]"));

此行将在查询到达数据库之前将 [dbo] 架构名称替换为 [my] 架构名称。幸运的是,Entity Framework 生成命令文本时,模式名称用方括号括起来,因此很容易匹配和替换。顺便说一句,我不是 Oracle 专家,所以我假设 Oracle 查询也包括相同格式的模式。如果没有,那么您可能需要稍微调整一下实现(以将架构替换为 EF 生成的任何格式)。

ReplaceSchemaInterceptor 是一个实现 IDbCommandInterceptor 接口的类。在这个类中,您需要用自己的模式替换模式。下面是这个类的实现:

class ReplaceSchemaInterceptor : IDbCommandInterceptor 

    private readonly string _newSchema;

    public ReplaceSchemaInterceptor(string newSchema)
    
        _newSchema = newSchema;
    

    public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    
    

    public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    
        command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
    

    public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    
    

    public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    
        command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
    

    public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    
    

    public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    
        command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
    

最后,代码并不完美。您需要为构造函数参数添加一些空检查,并且可能在替换命令文本时摆脱实现方法中的代码重复(提取为可重用方法?)。现在它只是做你所要求的。

【讨论】:

您的解决方案可能会解决我遇到的问题。阅读here。你知道这种拦截是否会导致性能问题吗? 与内置实现相比,这应该不会有明显的性能差异。它仅取决于您的代码将模式名称替换为另一个模式名称的速度。这就是它添加的全部内容。拦截器编写的好坏只会影响性能。 我倾向于使用拦截器,因为我不想手动更改每次迁移在“.resx”文件中创建的 DefaultSchema 条目。我还没有找到任何其他解决方案,除了你给我绝对控制权的解决方案。你认为这对我的案子来说是否过分?还有一件事,DbContext 来自第 3 方,所以我无法选择使用 modelBuilder.HasDefaultSchema("myschema"); 当您想在运行时在模式之间切换时,我的解决方案是完美的。但就您而言,您还有其他选择吗? 我还不知道。似乎我的情况迫使我动态地“切换回”到正确的架构,因为 EF6 正在欺骗我的代码在运行时自行使用“dbo”架构。因此,您的解决方案对我有用。我通常在第一次迁移本身中设置一个自定义模式名称。这样我就不必仅仅因为 EF6 将默认为其心爱的“dbo”而我不喜欢“dbo”而添加无用的架构重命名迁移 :-)。叹息!【参考方案2】:

使用实体框架代码优先中的流畅映射,您可以在运行时指示默认架构。这是您的 DbContext 子类中的 OnModelCreating 中的一个语句,例如:

modelBuilder.HasDefaultSchema("dev");

您习惯于从数据库中重新生成模型,因此我得出的结论是,该模型不包含许多(或任何)会使模型生成成为一项艰苦操作的定制。这也应该使迁移到代码优先变得相对容易。所以我建议你这样做。

在 Visual Studio 中,您可以从现有数据库生成代码优先模型,方法是从 Visual Studio 实体框架工具附带的模板中添加“ADO.Net 实体数据模型”。 (可能是预装的)。选择“数据库中的代码优先”选项并遵循指南。

如果你这样做,你会在包含模型的项目中找到一个连接字符串。这个连接字符串可以作为连接字符串的模板,你将把它放在执行程序集的配置文件中。你会注意到它看起来像......

metadata=res://* ... provider=System.Data.SqlClient;provider connection string=&amp;quot;...&amp;quot;"

这是属于数据库优先 edmx 模型的连接字符串。它包含作为资源生成到程序集中的元数据文件的路径。相反,连接字符串将是一个简单的 ADO.Net 连接字符串。使用代码优先,EF 将在运行时生成元数据。

如果你有这个,你可以在你的配置文件中为默认数据库架构添加一个条目,并使用它来设置我上面显示的架构。

【讨论】:

【参考方案3】:

看起来我们正在我的工作场所做某事。

为您的对象使用同义词!

一种可能是为您的测试表动态创建同义词 - 并删除文件中对架构的引用

假设连接的用户是CONNECT_USER - 必须是与您使用的架构不同的用户,即SCHEM_DEVSCHEM_TEST

这是我将如何进行切换(Oracle PL/SQL 脚本 - 连接为 CONNECT_USER):

begin
  for x in (select * from all_tables where owner='SCHEM_DEV')   
  loop
    --- drop synonyms on SCHEM_DEV objects
    execute immediate 'drop synonym '||table_name ;
    --- create synonyms on SCHEM_TEST objects
    execute immediate ' create or replace synonym '||table_name||' for SCHEM_TEST.'||table_name ;
  end loop;
end;
/

【讨论】:

这样的事情应该被禁止。在很多层面上都错了。

以上是关于在 EF6 中更改数据库架构的最简洁方法的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 架构更改的最佳实践

在 AWS RDS 上自动更改 SQL 架构

在 SQL Server 2008 中区分两个表架构的最简单方法是啥?

监控雪花数据库架构更改的最佳方法?

EF6 + SQL 14:将大量行上传到表中的最有效方法

如何避免使用自动代码优先迁移重新创建现有数据库