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

时间:2016-04-21 17:28:53

标签: oracle entity-framework-6

目前我们正在用mvc5编写一个页面,其中一个oracle sql数据库与权利框架6连接。

我们目前在oracle数据库中有两个模式,一个用于测试,另一个用于开发。权利框架中的模型是从开发数据库生成的,并与之完美地协同工作。

将连接字符串更改为测试架构时出现问题。当连接字符串被更改时,应用程序无法找到表(因为它们仍然引用开发方案)。

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

这种情况通常如何处理?

修改

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

3 个答案:

答案 0 :(得分:5)

我想这是使用entity framework command interceptors的完美用例。我只是尝试过,它的工作完美,即使对于Entity Framework 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);
    }
}

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

答案 1 :(得分:3)

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

modelBuilder.HasDefaultSchema("dev");

您已经习惯于从数据库中重新生成模型,我从中得出结论,该模型不包含许多(或任何)自定义,这些自定义将使模型生成成为艰苦的操作。这也应该使得移动到代码优先相对容易。所以我建议你这样做。

在Visual Studio中,您可以通过添加&#34; ADO.Net实体数据模型&#34;从现有数据库生成代码优先模型。来自Visual Studio的Entity Framework工具附带的模板。 (可能已预先安装)。从数据库中选择&#34; Code First&#34;并遵循指南。

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

  

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

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

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

答案 2 :(得分:1)

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

为对象使用同义词!

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

说连接的用户是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;
/