Sql server 如何在运行时在DatabaseGenerateOption.Identity、Computed和None之间切换,而不必生成空的DbMigrations

Sql server 如何在运行时在DatabaseGenerateOption.Identity、Computed和None之间切换,而不必生成空的DbMigrations,sql-server,entity-framework,ef-code-first,entity-framework-migrations,identity-insert,Sql Server,Entity Framework,Ef Code First,Entity Framework Migrations,Identity Insert,我正在将一个遗留数据库迁移到一个新的数据库,我们需要首先通过实体框架代码访问和“管理”(听起来很矛盾) 我们使用的是MS SQL Server 2014 旧数据库包含一些带有计算列的表典型的GUID和日期时间内容。 从技术上讲,这些列没有计算列规范,而是使用NEWID()和GETDATE() 我们都知道,很容易配置DbContext来处理这些属性,如下所示: modelBuilder.Entity<Foo>() .Property(t => t.Guid

我正在将一个遗留数据库迁移到一个新的数据库,我们需要首先通过实体框架代码访问和“管理”(听起来很矛盾)

我们使用的是MS SQL Server 2014

  • 旧数据库包含一些带有计算列的表典型的GUID和日期时间内容。

  • 从技术上讲,这些列没有计算列规范,而是使用
    NEWID()
    GETDATE()

  • 我们都知道,很容易配置
    DbContext
    来处理这些属性,如下所示:

    modelBuilder.Entity<Foo>()
                .Property(t => t.Guid)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
    modelBuilder.Entity<Bar>()
                .Property(t => t.DTS)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
    
    注意初始
    DbMigration
    中的默认值时:

    CreateTable(
        "dbo.Foos",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Guid = c.Guid(nullable: false, defaultValueSql: "NEWID()"),
            })
        .PrimaryKey(t => t.Id);
    
    
    CreateTable(
        "dbo.Bars",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                DTS = c.Guid(nullable: false, defaultValueSql: "GETDATE()"),
            })
        .PrimaryKey(t => t.Id);
    

    问题是: 但问题仍然存在:是否有一种方法可以在运行时在
    DatabaseGeneratedOption.Identity
    DatabaseGeneratedOption.Computed
    DatabaseGeneratedOption.None
    之间切换


    至少,我们如何在运行时打开/关闭
    DatabaseGeneratedOption.Identity

    上下文的某些配置始终取决于运行时环境-例如,代理生成和验证。因此,实体框架
    DbContext
    的运行时配置是我非常重视的事情

    尽管我从未使用过这种方法在每个用例的基础上切换上下文的配置,但我看不出有什么理由不起作用

    在其最简单的形式中,这可以通过为每个环境提供一组
    EntityTypeConfiguration
    类来实现。然后,根据每个环境将每个配置集连接到
    DbContext
    。同样,在最简单的形式中,这可以通过为每个环境使用
    DbContext
    类型来实现。在您的情况下,这将是每个用例

    不那么天真,我通常将上下文的配置封装在特定于环境的工作单元中。例如,Asp.Net环境的工作单元有一个底层
    DbContext
    ,配置为将验证委托给web框架,以及关闭代理生成以防止序列化问题。我想这种方法对你的问题也有类似的用处

    例如(使用暴力代码):


    顺便说一句:对于仅在创建新记录时设置的值,使用DatabaseGenerateOption.Identity而不是DatabaseGenerateOption.Computed更有效。(如果计算了该选项,EF将在每次更新后从数据库中读取这些值,假设它们的值可能已更改)(请参见此答案)如果使用此解决方案,需要注意一点:您可能需要在可选DbContext的构造函数中调用database.SetInitializer(null),否则您仍然会得到这些值“支持上下文的模型已更改…”错误。在调用
    OnModelCreating
    时,替代上下文上的配置不会重写表架构吗?如果调用
    Database.SetInitializer(null)
    如何调用
    OnModelCreating
    CreateTable(
        "dbo.Foos",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Guid = c.Guid(nullable: false, defaultValueSql: "NEWID()"),
            })
        .PrimaryKey(t => t.Id);
    
    
    CreateTable(
        "dbo.Bars",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                DTS = c.Guid(nullable: false, defaultValueSql: "GETDATE()"),
            })
        .PrimaryKey(t => t.Id);
    
    // Foo Configuration which enforces computed columns
    public class FooConfiguration : EntityTypeConfiguration<Foo>
    {
        public FooConfiguration()
        {           
            Property(p => p.DateTime).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
            Property(p => p.Guid).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
        }
    }
    
    // Foo configuration that allows computed columns to be overridden
    public class FooConfiguration2 : EntityTypeConfiguration<Foo>
    {
        public FooConfiguration2()
        {           
            Property(p => p.DateTime).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            Property(p => p.Guid).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        }
    }
    
    // DbContext that enforces computed columns
    public class MyContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new FooConfiguration());     
        }
    }
    
    // DbContext that allows computed columns to be overridden
    public class MyContext2 : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new FooConfiguration2());     
        }
    }
    
    [Fact]
    public void CanConfigureContextAtRuntime()
    {
        // Enforce computed columns
        using (var context = new EfContext())
        {
            var foo1 = new Foo();
            context.Foos.Add(foo1);                             
            context.SaveChanges();
        }
    
        // Allow overridden computed columns
        using (var context = new EfContext2())
        {              
            var foo2 = new Foo { DateTime = DateTime.Now.AddYears(-3) };
            context.Foos.Add(foo2);
            context.SaveChanges();
        }
    
        // etc
    }