Fer*_*osa 7 sql-server entity-framework identity-insert ef-code-first ef-migrations
我正在将一个遗留数据库迁移到一个新数据库,我们需要主要通过Entity Framework Code-First来访问和"管理"(尽管它可能听起来像是一样).
我们正在使用MS SQL Server 2014.
遗留数据库包含一些带有计算列的表.典型的GUID和DateTime的东西.
从技术上讲,这些列没有计算列规范,而是使用NEWID()和给定默认值GETDATE()
我们都知道配置DbContext处理这些属性非常容易,如下所示:
modelBuilder.Entity<Foo>()
            .Property(t => t.Guid)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
modelBuilder.Entity<Bar>()
            .Property(t => t.DTS)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
上述内容将指示实体框架忽略在INSERTs和期间提交此类属性的任何提供值UPDATEs.
但现在我们需要允许导入旧记录并维护OLD值,包括PRIMARY KEY,标记为IDENTITY 
这意味着我们将不得不设置Id,Guid以及DTS性能DatabaseGeneratedOption.None,同时插入这些记录.
对于这种情况Id,我们必须以某种方式SET IDENTITY_INSERT ... ON/OFF在连接会话中执行.
我们也希望通过Code-First执行此导入过程.
如果我修改模型并"临时"并DatabaseGeneratedOption.None在创建数据库之后将这些属性设置为,我们将得到典型的:
自创建数据库以来,支持上下文的模型已更改.考虑使用Code First Migrations来更新数据库.
我知道我们可以生成一个空的编码迁移,-IgnoreChanges以"建立"这个最新版本的上下文,但这不是一个可接受的策略,因为我们必须来回运行空迁移这个目的.
我们考虑过将这些属性赋予可空类型,即
public class Foo
{
    ...
    public Guid? Guid { get; set; }
}
public class Bar
{
    ...
    public DateTime? DTS { get; set; }
}
在关注初始中的默认值时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框架,以及关闭代理生成以防止序列化问题.我想这种方法对你的问题有类似的用处.
例如(使用强力代码):
// 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());     
    }
}
这显然可以整理 - 我们通常使用工厂和策略模式的组合来封装运行时特定上下文的创建.与DI容器结合使用,可以在每个环境的基础上注入正确的设置配置类.
用法示例:
[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
}
| 归档时间: | 
 | 
| 查看次数: | 3281 次 | 
| 最近记录: |