Sql server 如何在运行时在DatabaseGenerateOption.Identity、Computed和None之间切换,而不必生成空的DbMigrations
我正在将一个遗留数据库迁移到一个新的数据库,我们需要首先通过实体框架代码访问和“管理”(听起来很矛盾) 我们使用的是MS SQL Server 2014Sql 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
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
}