【问题标题】: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&lt;EdistributionDbContext, MigrationsConfiguration&gt;() 数据库初始化程序。

完成所有这些设置后,当我切换到不同的架构时(例如,通过 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.OracleMigrationSqlGeneratorstring.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) 的人可能会这样做,但我的公司决定不购买这个库,因此我没有测试它。

标签: c# oracle entity-framework entity-framework-migrations


【解决方案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'异常。
    猜你喜欢
    • 2013-12-29
    • 1970-01-01
    • 2017-10-31
    • 2013-01-02
    • 1970-01-01
    • 2013-06-14
    相关资源
    最近更新 更多