简体   繁体   English

实体框架更新多对多关系的中间表

[英]Entity Framework update the intermediate table of a many-to-many relation

I've got a many-to-many relation between user and project .我在userproject之间建立了多对多关系。 Like:喜欢:

class User 
{
    public ICollection<Project> Projects { get; set; }
}

class Project 
{
    public ICollection<User> Users { get; set; }
}

Entity Framework automatically generated the intermediate table.实体框架自动生成中间表。

The thing is I want to update the user, along with the entire list of projects.问题是我想更新用户以及整个项目列表。 This list could've been modified in any way, projects could've been added and deleted.可以以任何方式修改此列表,可以添加和删除项目。 at the same time before the user object gets updated.同时在用户 object 更新之前。

I always get the same error, that Entity Framework tried to add a duplicated entry in the intermediate table.我总是遇到同样的错误,即 Entity Framework 试图在中间表中添加重复的条目。

I've tried numerous things without success (a few listed below).我尝试了很多事情都没有成功(下面列出了一些)。

var tmp = Context.Entry(user); // user being the updated object.
tmp.State = EntityState.Modified;
tmp.Collection(e => e.Projects).IsModified = true;
        
Context.Users.Update(user);
Context.SaveChanges();

or或者

var tmp = Context.Users.SingleOrDefault(u => u.Id == user.Id);

if (tmp == null) 
     return null;

Context.Entry(tmp).CurrentValues.SetValues(user);
Context.SaveChanges();

return user;

or just plain old update:或者只是简单的旧更新:

Context.Users.Update(user);
Context.SaveChanges();

But none of these worked.但这些都不起作用。

The issue sounds like you have a detached User Entity with a set of Projects and you want to pass that into a method, associate with the DbContext to persist the changes.这个问题听起来像是您有一个带有一组项目的分离的用户实体,并且您想将其传递到一个方法中,与 DbContext 关联以保留更改。

You are encountering issues with doubling up records because while you attach the user to the DbContext, it will treat each of the Project entities associated with the user as new instances because they don't reference tracked instances themselves.您遇到加倍记录的问题,因为当您将用户附加到 DbContext 时,它会将与用户关联的每个项目实体视为新实例,因为它们本身不引用跟踪实例。

Updating detached entities with associations is fairly involved, especially where you expect to possibly add or remove associations in an operation.使用关联更新分离的实体是相当复杂的,尤其是在您希望可能在操作中添加或删除关联的情况下。

The recommended approach would be to load the current User and Projects from the DB then leverage Automapper to guard what values you can copy over from the detached entity, and then go through the associations to add/remove any project references that have changed.推荐的方法是从数据库加载当前用户项目,然后利用 Automapper 来保护您可以从分离的实体复制哪些值,然后 go 通过关联添加/删除任何已更改的项目引用。 If it is possible to create a brand new project to associate to the user as part of this operation, you need to handle that as well.如果可以创建一个全新的项目作为此操作的一部分与用户关联,那么您也需要处理它。

var existingUser = Context.Users.Include(x => x.Projects).Single(x => x.UserId == user.UserId);
Mapper.Map(user, existingUser); 
// Where Automapper is configured with a User to User mapping with allowed 
// values to copy over, ignoring anything that cannot legally be changed.

var newProjectIds = user.Projects.Select(x => x.ProjectId).ToList();
var existingProjectIds = existingUser.Projects.Select(x => x.ProjectId).ToList();
var projectIdsToAdd = newProjectIds.Except(existingProjectIds).ToList();
var projectIdsToRemove = existingProjectIds.Except(newProjectIds).ToList();

var projectsToAdd = Context.Projects.Where(x => projectIdsToAdd.Contains(x.ProjectId)).ToList();
var projectsToRemove = existingUser.Projects.Where(x => projectIdsToRemove.Contains(x.ProjectId)).ToList();

foreach(var project in projectsToRemove)
    existigUser.Projects.Remove(project);
foreach(var project in projectsToAdd)
    existingUser.Projects.Add(project);

Context.SaveChanges();

... This example does not cover the possibility of brand new projects. ... 这个例子不包括全新项目的可能性。 If the updated user can include a brand new project then you need to detect those when looking for projectsToAdd to add any Projects from the passed in project list where the ID is in new project IDs but not found in the DB.如果更新后的用户可以包含一个全新的项目,那么您需要在查找 projectsToAdd 时检测这些项目,以从传入的项目列表中添加任何项目,其中 ID 在新项目 ID 中但未在数据库中找到。 Those detached references can be added to the User loaded from the DbContext, however you do need to handle any navigation properties that each Project might have to avoid duplication, substituting each of those with references to tracked entities, including any bi-directional reference back to the User if present.这些分离的引用可以添加到从 DbContext 加载的用户,但是您确实需要处理每个项目可能必须避免重复的任何导航属性,将每个属性替换为对跟踪实体的引用,包括返回到的任何双向引用用户(如果存在)。

In general, dealing with detached entities has various considerations that you need to keep in mind and handle very deliberately.通常,处理分离的实体有多种注意事项,您需要牢记并慎重处理。 It is generally much better to avoid passing detached entities around and instead aim to pass a minimal representation of the data you want to associate, then load and adjust that server-side.通常最好避免传递分离的实体,而是旨在传递您想要关联的数据的最小表示,然后加载和调整该服务器端。 Usually the argument to using detached entities is to avoid having to load the data again, however this leads to more code when trying to synchronize these detached instances, and neglects the fact that data state could have changed since the detached instances were taken.通常使用分离实体的论据是避免必须再次加载数据,但是这会导致在尝试同步这些分离实例时需要更多代码,并且忽略了数据 state 自分离实例被采用后可能已经更改的事实。 The above code for instance should also be looking at entity versioning between the detached entity and the loaded state to detect if anyone else might have made changes since the detached copies were read at the start of the user process for making the changes.例如,上面的代码还应该查看分离的实体和加载的 state 之间的实体版本控制,以检测是否有其他人可能进行了更改,因为分离的副本在用户进程开始时被读取以进行更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM