C# EF6 vs Entity Framework Core:插入实体而不将主键(标识)重置为零
EF6:插入一个已经有主键值的实体,并为主键指定一个新值 EF Core:它尝试插入主键的值,但显然失败了,出现了SqlException: 当identity\u insert设置为OFF时,无法在表“资产”中为identity列插入显式值 我找到的解决方法是将PK重置为默认值(0表示int) 例如:C# EF6 vs Entity Framework Core:插入实体而不将主键(标识)重置为零,c#,entity-framework,entity-framework-core,C#,Entity Framework,Entity Framework Core,EF6:插入一个已经有主键值的实体,并为主键指定一个新值 EF Core:它尝试插入主键的值,但显然失败了,出现了SqlException: 当identity\u insert设置为OFF时,无法在表“资产”中为identity列插入显式值 我找到的解决方法是将PK重置为默认值(0表示int) 例如: Asset asset = new Asset { Id = 3, Name = "Toto",
Asset asset = new Asset
{
Id = 3,
Name = "Toto",
};
using (AppDbContext context = new AppDbContext())
{
asset.Id = 0; // needs to be reset
context.Assets.Add(asset);
context.SaveChanges();
}
我正在将一个解决方案从EF 6迁移到EF Core,我希望避免在插入时手动重置所有ID。上面的例子非常简单,有时我要插入一个完整的图,在我的例子中,这意味着重置图的所有PK和FK
我能想到的唯一自动解决方案是使用反射解析整个图形并重置ID。但我认为这不是很有效
我的问题:如何在全球范围内禁用该行为?由于EF Core不支持拦截器,我的建议是“破解它” 如果覆盖
SaveChanges
方法,则可以在每次保存时提供此功能,而无需进行大规模重构
public class MyContext : DbContext
{
//...
public override int SaveChanges()
{
foreach (var dbEntityEntry in ChangeTracker.Entries<Asset>()
.Where(t => t.State == EntityState.Added))
{
dbEntityEntry.Entity.Id = default(int);
}
return base.SaveChanges();
}
}
公共类MyContext:DbContext
{
//...
公共覆盖int SaveChanges()
{
foreach(ChangeTracker.Entries()中的var dbEntityEntry)
.Where(t=>t.State==EntityState.Added))
{
dbEntityEntry.Entity.Id=默认值(int);
}
返回base.SaveChanges();
}
}
这可能会导致每种类型都需要一个循环,或者您可以发挥创意,但一般来说,这将在不对您的业务方法进行大量更改的情况下实现您想要的功能。不幸的是,目前(从EF Core 2.0.1开始),这种行为是不可控的。我想这应该是EF6的改进,因为它允许您执行身份插入
幸运的是,EF Core构建在基于服务的体系结构上,允许您(尽管不是那么容易)替换几乎所有方面。在这种情况下,负责的服务称为IValueGenerationManager
,并由类实现。提供上述行为的行是
private static IEnumerable<IProperty> FindPropagatingProperties(InternalEntityEntry entry)
=> entry.EntityType.GetProperties().Where(
property => property.IsForeignKey()
&& property.ClrType.IsDefaultValue(entry[property]));
private static IEnumerable<IProperty> FindGeneratingProperties(InternalEntityEntry entry)
=> entry.EntityType.GetProperties().Where(
property => property.RequiresValueGenerator()
&& property.ClrType.IsDefaultValue(entry[property]));
然后在DbContext
派生类中重写onconfigurang
,并添加以下行:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// ...
optionsBuilder.UseEF6CompatibleValueGeneration();
}
问题就这样解决了。我想说的是,由于重新插入具有重复密钥的对象的糟糕决定,试图让新系统做同样的事情也是一个糟糕的想法。正确的选择是正确地修复所有代码。这不是我的代码,我的工作是移植到ef core,而不是重构。我会不管是谁,我都会把它提出来。我知道一些糟糕的代码,只是想让它正常工作,这不是我想为之工作的公司。如果这是你的情况,我真的很抱歉,我希望有人知道如何解决这个问题。“我的工作是移植,而不是修复”这不是一个好的态度,我给你的建议。@UK知道这会修改现有的条目。我在家里(我不在工作)用一个小项目尝试过它…哇,你是个专业人士。谢谢你救了我的周末!
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// ...
optionsBuilder.UseEF6CompatibleValueGeneration();
}