独立于模式的实体框架代码优先迁移
Posted
技术标签:
【中文标题】独立于模式的实体框架代码优先迁移【英文标题】:Schema independent Entity Framework Code First Migrations 【发布时间】:2015-12-04 02:12:18 【问题描述】:我在使用针对 Oracle 数据库的实体框架迁移时遇到了麻烦,因为架构名称包含在迁移代码中,对于 Oracle,架构名称也是用户名。我的目标是进行独立于模式的 Code First 迁移(能够拥有一组用于测试和生产环境的迁移)。
我已经尝试过这种方法(使用 Entity Framework 6.1.3):
1) 我在 Web.config 中有架构名称:
<add key="SchemaName" value="IPR_TEST" />
2) 我的 DbContext 将架构名称作为构造函数参数:
public EdistributionDbContext(string schemaName)
: base("EdistributionConnection")
_schemaName = schemaName;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.HasDefaultSchema(_schemaName);
3) 我必须为实体框架迁移实现 IDbContextFactory 才能创建没有无参数构造函数的 DbContext:
public class MigrationsContextFactory : IDbContextFactory<EdistributionDbContext>
public EdistributionDbContext Create()
return new EdistributionDbContext(GetSchemaName());
4) 我还将迁移历史记录表配置为放置在正确的架构中:
public class EdistributionDbConfiguration : DbConfiguration
public EdistributionDbConfiguration()
SetDefaultHistoryContext((connection, defaultSchema)
=> new HistoryContext(connection, GetSchemaName()));
5) 我修改了为迁移生成的代码以替换硬编码的模式名称。例如。我用CreateTable($"_schema.Users")
替换了CreateTable("IPR_TEST.Users")
。 (_schema
字段根据Web.config中的值设置)。
6) 我使用MigrateDatabaseToLatestVersion<EdistributionDbContext, MigrationsConfiguration>()
数据库初始化程序。
完成所有这些设置后,当我切换到不同的架构时(例如,通过 web.config 转换),我仍然遇到问题 - 抛出异常,告诉我数据库与我的模型不匹配并且 AutomaticMigrations 被禁用(这是需要的)。当我尝试执行add-migration
时,会生成一个新的迁移,其中所有对象都应移动到不同的架构(例如:MoveTable(name: "IPR_TEST.DistSetGroups", newSchema: "IPR");
,这绝对是不希望的。
对我来说,架构名称似乎在迁移类的模型字符串哈希中的某处硬连线(例如 201509080802305_InitialCreate.resx),即:
<data name="Target" xml:space="preserve">
<value>H4sIAAAAAAAEAO09227jO... </value>
</data>
有办法告诉 Code First 迁移忽略模式名称吗?
【问题讨论】:
我确定你已经尝试过了,但是对于 SQL Server,如果你只是没有在模型绑定中指定架构,它只会使用用户默认的架构。 谢谢@Ben,您的评论可能会解决我的问题。当您省略modelBuilder.HasDefaultSchema("SCHEMA_NAME");
时,实体框架将“dbo”设置为默认架构。但是,现在我尝试了modelBuilder.HasDefaultSchema(string.Empty);
,现在似乎(正确)使用了用户的默认模式。可悲的是,用于为迁移生成 SQL 的Oracle.ManagedDataAccess.EntityFramework.OracleMigrationSqlGenerator
与string.Empty
-schema-name 存在问题,并在生成迁移的 SQL 时抛出异常......但这是另一个问题......
您好 Jan Palas,您找到解决此问题的方法了吗?我有同样的问题,我将 EF Migration 与 Oracle 一起使用,当我更改我的架构时,所有迁移脚本都包含 MoveTable...
@toregua:我没有找到解决方案,但有一个解决方法:我使用具有适用于我们测试环境的特定模式名称的迁移。当存在新的迁移时,它会在部署到测试环境后自动更新我们的数据库模式。对于生产环境,我使用迁移来生成数据库更新脚本 (update-database -script -sourceMigration Migration13 -targetMigration Migration18
),之后我会在其中更改模式名称。部署到生产环境时,我手动执行数据库更新脚本。 (抱歉回答晚了,正在度假)
@toregua:您可以选择使用可以与string.Empty
-schema-name 一起使用的其他OracleMigrationSqlGenerator
。我记得有一个来自 Devart (devart.com/dotconnect/oracle) 的人可能会这样做,但我的公司决定不购买这个库,因此我没有测试它。
【参考方案1】:
您可以创建派生的DbContext
并在OnModelCreating
中“覆盖”modelBuilder.HasDefaultSchema(...)
:
public class TestDbContext : ProductionDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("TestSchema");
然后您可以为这两个上下文创建迁移。请参阅this question,了解如何在一个项目中创建两个迁移。
这种方法的缺点是您必须维护两个单独的迁移。但它让您有机会调整TestDbContext
的配置。
【讨论】:
【参考方案2】:我也遇到了同样的问题,多亏了你的方法,我终于找到了一个看起来效果很好的解决方案:
1) 我在 Web.config 应用设置中有架构名称:
<add key="Schema" value="TEST" />
2) 我有一个历史背景:
public class HistoryDbContext : HistoryContext
internal static readonly string SCHEMA;
static HistoryDbContext()
SCHEMA = ConfigurationManager.AppSettings["Schema"];
public HistoryDbContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(SCHEMA);
3) 我有一个引用我的历史数据库上下文的数据库配置:
public class MyDbConfiguration : DbConfiguration
public MyDbConfiguration()
SetDefaultHistoryContext((connection, defaultSchema) => new HistoryDbContext(connection, defaultSchema));
4) 这是我的数据库上下文:
public partial class MyDbContext : DbContext
public MyDbContext()
: base("name=MyOracleDbContext")
public static void Initialize()
DbConfiguration.SetConfiguration(new MyDbConfiguration());
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Migrations.Configuration>());
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.HasDefaultSchema(string.Empty);
5) 最后我从 global.asax 中调用 Initialize 方法
protected void Application_Start()
MyDbContext.Initialize();
关键是将db上下文的默认模式设置为String.Empty,并将历史上下文的模式设置为正确的模式。 因此,当您创建迁移时,它们与架构无关:迁移的 resx 的 DefaultSchema 变量将为空。但是历史数据库上下文模式仍然是正确的,允许迁移检查通过。
我正在使用以下 nugets 包:
<package id="EntityFramework" version="6.2.0" targetFramework="net452" />
<package id="Oracle.ManagedDataAccess" version="12.2.1100" targetFramework="net452" />
<package id="Oracle.ManagedDataAccess.EntityFramework" version="12.2.1100" targetFramework="net452" />
然后您可以在不同的数据库上成功使用 Oracle 迁移。
【讨论】:
确认工作。仅在 HistoryDbContext 上设置模式名称确实是产生差异的区别,它使一切都到位。我想强调一个事实,如果您使用流利的配置/数据注释并且您想采用这种方法,您必须确保您没有在流利的配置中的任何地方设置架构名称/data-annotations(只需将架构名称保留为空字符串)。 其实不行:EF在db初始化时抛出'seqOwner cannot be null'异常。以上是关于独立于模式的实体框架代码优先迁移的主要内容,如果未能解决你的问题,请参考以下文章