【发布时间】:2018-05-28 04:05:50
【问题描述】:
在实体框架核心2.0中,Post和Category之间存在多对多关系(绑定类为PostCategory)。
当用户更新Post 时,整个Post 对象(及其PostCategory 集合)被发送到服务器,在这里我想重新分配新收到的集合PostCategory(用户可以通过添加新类别和删除一些类别来显着更改此集合)。
我用来更新该集合的简化代码(我只是分配了全新的集合):
var post = await dbContext.Posts
.Include(p => p.PostCategories)
.ThenInclude(pc => pc.Category)
.SingleOrDefaultAsync(someId);
post.PostCategories = ... Some new collection...; // <<<
dbContext.Posts.Update(post);
await dbContext.SaveChangesAsync();
这个新集合中的对象与前一个集合中的对象 ID 相同(例如,用户删除了一些(但不是全部)类别)。因为,我得到了一个例外:
System.InvalidOperationException:无法跟踪实体类型“PostCategory”的实例,因为已经在跟踪具有相同键值 {'CategoryId', 'PostId'} 的另一个实例。
如何有效地重建新集合(或简单地分配新集合)而不会出现此异常?
更新
The answer in this link 似乎和我想要的有关,但它是一种好而有效的方法吗?有没有更好的办法?
更新 2
我的帖子(编辑覆盖其值)是这样的:
public async Task<Post> GetPostAsync(Guid postId)
{
return await dbContext.Posts
.Include(p => p.Writer)
.ThenInclude(u => u.Profile)
.Include(p => p.Comments)
.Include(p => p.PostCategories)
.ThenInclude(pc => pc.Category)
.Include(p => p.PostPackages)
.ThenInclude(pp => pp.Package)
//.AsNoTracking()
.SingleOrDefaultAsync(p => p.Id == postId);
}
更新 3(我的控制器中的代码,它试图更新帖子):
var writerId = User.GetUserId();
var categories = await postService.GetOrCreateCategoriesAsync(
vm.CategoryViewModels.Select(cvm => cvm.Name), writerId);
var post = await postService.GetPostAsync(vm.PostId);
post.Title = vm.PostTitle;
post.Content = vm.ContentText;
post.PostCategories = categories?.Select(c => new PostCategory { CategoryId = c.Id, PostId = post.Id }).ToArray();
await postService.UpdatePostAsync(post); // Check the implementation in Update4.
更新 4:
public async Task<Post> UpdatePostAsync(Post post)
{
// Find (load from the database) the existing post
var existingPost = await dbContext.Posts
.SingleOrDefaultAsync(p => p.Id == post.Id);
// Apply primitive property modifications
dbContext.Entry(existingPost).CurrentValues.SetValues(post);
// Apply many-to-many link modifications
dbContext.Set<PostCategory>().UpdateLinks(
pc => pc.PostId, post.Id,
pc => pc.CategoryId,
post.PostCategories.Select(pc => pc.CategoryId)
);
// Apply all changes to the db
await dbContext.SaveChangesAsync();
return existingPost;
}
【问题讨论】:
-
@DanielB,谢谢。我已经在问题的更新中提到了这一点。
-
请注意您没有包含导航属性。我不太确定 changetracking 将如何处理导航属性的设置(我现在无法尝试),但我怀疑它会起作用。您可以尝试设置每个对象的状态(在上一个导航属性和新导航属性中),但这会很乏味且容易出错,所以我怀疑给定的答案是最好的方法。
-
@DevilSuichiro,谢谢你,你是对的,我只是在这里写代码。我在问题中添加了
include。
标签: c# .net entity-framework .net-core entity-framework-core