实体框架中具有相同键的两个不同对象不起作用
本文关键字:两个 不起作用 对象 框架 实体 | 更新日期: 2023-09-27 18:27:27
我正试图在我的主对象中插入对象引用,但如果我不使用它以前托管的对象,EntityFramework会抱怨。我只是想避免在创建对象时依赖dbContext。
简化示例:
class Movie {
public ApplicationUser Owner { get;set; }
}
var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };
// I have to attach or change its state, otherwise, EF will complain the object is not complete
db.Entry(myMovie.Owner).State = EntityState.Unchanged;
不知怎的,如果上下文以前加载过同一个ApplicationUser,我会得到以下错误:
保存或接受更改失败,因为多个"ApplicationUser"类型的实体具有相同的主键值。确保显式设置的主键值是唯一的。确保在数据库和实体框架模型中正确配置了数据库生成的主键。使用实体设计器进行数据库优先/模型优先配置。使用"HasDatabaseGeneratedOption"fluent API或"DatabaseGeneratedAttribute"进行代码优先配置。
我该如何避免这个问题?最理想的情况是,我不想告诉那个新对象的状态。
如果您有一个只读取数据而不修改数据的实例,则可以使用AsNoTracking()
,这将阻止您的上下文知道的模型的附加实例(它本质上是只读的)。
下面的代码应该可以工作,即使它已经检索了两次相同的对象。
var myMovieReadOnly = db.Movies.AsNoTracking().FirstOrDefault(m, m => m.Id = 1);
var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };
db.SaveChanges();
另一个注意事项是,在只读取数据的情况下,AsNoTraking()还可以节省性能。
编辑:只是重读一下,意识到这是ApplicationUser模型,而不是电影,但第一个实例的翻新也应该适用同样的概念。
第2版:
根据我们的评论,如果你已经知道ID,你也可以做以下事情来防止需要做查找:
class Movie {
public int OwnerId { get;set; }
public ApplicationUser Owner { get;set; }
}
var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.OwnerId = 2;
db.SaveChanges();
如果上下文以前加载过同一个ApplicationUser,我会得到这个错误
因此,首先检查ApplicationUser
是否已加载。
var newId = 2;
var newApplicationUser = db.ApplicationUsers.Local.FirstOrdefault(u => u.Id == newId)
?? new ApplicationUser { Id = newId };
myMovie.Owner = newApplicationUser;