我正在使用Entity Framework Core将对象图存储在数据库中。在构建图的各个时间,我都会创建一个实体,将其存储到数据库中,然后释放上下文。但是,我遇到了一个问题,其中EFC尝试插入一个在连接到新实体时已经插入的实体。最好用代码解释。这是一段简短的repro代码(这是直线代码,但是上下文的两种用法发生在代码中的不同时间和位置)。
在第二次致电context.SaveChanges()
时,出现以下异常:
SqlException:
当IDENTITY_INSERT设置为OFF时,无法在表“名称空间”中为标识列插入显式值。
当我查看正在执行的SQL时,它正在尝试再次插入名称空间实体,大概是因为myType已保存到数据库,并且它引用了dbNamespace
实体。
// see if namespace is in the db and add it if not
string someNamespaceString = "foobar";
CodeDatabase.Models.Namespace dbNamespace;
using (var context = new CodeFactsContext())
{
dbNamespace = context.Namespace.FirstOrDefault(ns => ns.Namespace1 == someNamespaceString);
if (dbNamespace == null)
{
dbNamespace = new Namespace() { Namespace1 = someNamespaceString };
context.Namespace.Add(dbNamespace);
}
context.SaveChanges();
}
// Type entity created somewhere from the code
var myType = new CodeDatabase.Models.Type()
{
FullName = "foobar.mytype",
ShortName = "mytype",
Namespace = dbNamespace // this is already in the DB
};
// check if myType is in the db and add it if not
using (var context = new CodeFactsContext())
{
var dbType = context.Type.FirstOrDefault(t => t.FullName == myType.FullName);
if (dbType == null)
{
dbType = myType;
context.Add(dbType);
}
context.SaveChanges(); // throws exception
}
有什么主意如何让EF Core识别(在第二个context.SaveChanges()
中)myType
应该插入数据库,但是myType.Namespace
不应该因为它已经存在?这两个实体都有一个由数据库自动生成的int id,并且在第一次调用SaveChanges
之后,命名空间的id被设置为数据库值。我以为EF Core会认识到ID不为0,因此不尝试保存它。任何帮助/建议都非常欢迎。
答案 0 :(得分:2)
我认为EFC会认识到ID不为0,因此不尝试保存它。
问题是您正在使用Add
方法,该方法将所有可访问且未跟踪的实体都标记为新实体,而不管其键值如何(这是为了允许标识插入方案)。 Disconnected Entities - Working with graphs - All new/all existing entities中对此进行了说明。当您的场景陷入Mix of new and existing entities时。
有什么主意如何让EFC识别(在第二个
context.SaveChanges
中)myType
应该插入数据库,但是myType.Namespace
不应该因为它已经存在?这两个实体都有一个int
ID,该ID由数据库自动生成,并且在第一次调用SaveChanges
之后,命名空间的ID被设置为数据库值。
实际上,第二个文档链接中介绍了一个简单的解决方案:
使用自动生成的键,即使图形包含需要插入的实体和需要更新的实体的混合,更新也可以再次用于插入和更新
其中“再次”指Saving single entities:
Update方法通常将实体标记为要更新,而不是插入。但是,如果实体具有自动生成的键,并且未设置键值,则该实体会自动标记为插入。
幸运的是,您的实体使用自动生成的密钥,因此只需使用Update
而不是Add
:
if (dbType == null)
{
dbType = myType;
context.Update(dbType); // <--
}