首先使用EF代码更新多个到多个集合

时间:2021-12-15 02:10:31

I'm using EF Code First with the following configuration

我使用EF Code First进行以下配置

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<UserProfile>()
        .HasMany(x => x.TeamLeaders)
        .WithMany()
        .Map(m => m.MapLeftKey("UserId")
        .MapRightKey("TeamLeaderId")
        .ToTable("UserTeamLeaders"));
}

TeamLeaders is an ICollection of Users i.e. it is a self referencing many-many relationship.

TeamLeaders是一个用户的ICollection,即它是一个自我引用许多关系。

In plain English, a user can have more than one team leader. This configuration appears to be correct, as it creates the duel FK/PK link table as I would expect.

用简单的英语,用户可以拥有多个团队领导。这种配置似乎是正确的,因为它会像我期望的那样创建决斗FK / PK链接表。

I have an MVC4 application that allows editing, adding and removal of team leaders from the collection.

我有一个MVC4应用程序,允许从集合中编辑,添加和删除团队领导。

In my controller, I originally had the following:

在我的控制器中,我最初有以下内容:

    var original = context.UserProfiles
        .Include("TeamLeaders")
        .Single(x => x.UserId == model.UserId);

    context.Entry(original).CurrentValues.SetValues(model);

However, that last line was failing to mark the TeamLeaders collection as updated, and when I called SaveChanges(), no changes were recorded.

但是,最后一行未能将TeamLeaders集合标记为已更新,并且当我调用SaveChanges()时,未记录任何更改。

Instead I wrote a simple CopyProperties method on my User class, which uses reflection to manually copy the properties across, so in my controller I now have:

相反,我在我的User类上编写了一个简单的CopyProperties方法,它使用反射手动复制属性,所以在我的控制器中我现在有:

    var original = context.UserProfiles
        .Include("TeamLeaders")
        .Single(x => x.UserId == model.UserId);

    //context.Entry(original).CurrentValues.SetValues(model);

    original.CopyProperties(model);

However, this goes too far, and SaveChanges attempts to add a new user to the system matching the profile of the selected team leader.

但是,这太过分了,SaveChanges尝试将新用户添加到与所选团队负责人的配置文件匹配的系统中。

Can anyone advise as to which part of this I am doing wrong? I am not sure whether I need to update the mappings, or change the way I copy properties from the view model to the model

任何人都可以建议我做错了哪一部分?我不确定是否需要更新映射,或者更改将属性从视图模型复制到模型的方式

1 个解决方案

#1


3  

You must modify the loaded TeamLeaders collection in the original user according to your changes so that change detection can recognize which leaders have been removed and which have been added. When you call SaveChanges then EF will write the appropriate DELETE and INSERT statements for the join table based on the detected changes. The simplest way would look like this:

您必须根据更改在原始用户中修改已加载的TeamLeaders集合,以便更改检测可以识别已删除哪些领导以及已添加哪些领导。当您调用SaveChanges时,EF将根据检测到的更改为连接表编写相应的DELETE和INSERT语句。最简单的方法如下:

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Find(teamLeader.UserId);
    if (user != null)
        original.TeamLeaders.Add(user)
}
context.SaveChanges();

Find will fetch the team leaders from the context if they are already loaded. If they aren't loaded it will query the database.

如果已经加载,Find将从上下文中获取团队领导。如果未加载它将查询数据库。

If you want to avoid additional queries you can attach the team leaders manually to the context:

如果您想避免其他查询,可以手动将团队领导附加到上下文:

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Local
        .SingleOrDefault(o => o.UserId == teamLeader.UserId);
    if (user == null)
    {
        user = new User { UserId = teamLeader.UserId };
        context.UserProfiles.Attach(user);
    }
    original.TeamLeaders.Add(user)
}
context.SaveChanges();

Except the first query to load the original here is no further database query involved.

除了在此处加载原始文件的第一个查询之外,不涉及进一步的数据库查询。

BTW: You should be able to use the strongly typed Include version with EF Code First:

顺便说一句:你应该能够使用EF Code First的强类型包含版本:

Include(u => u.TeamLeaders)

You just need using System.Data.Entity; in your code file to get access to this version.

你只需要使用System.Data.Entity;在您的代码文件中以获取对此版本的访问权限。

#1


3  

You must modify the loaded TeamLeaders collection in the original user according to your changes so that change detection can recognize which leaders have been removed and which have been added. When you call SaveChanges then EF will write the appropriate DELETE and INSERT statements for the join table based on the detected changes. The simplest way would look like this:

您必须根据更改在原始用户中修改已加载的TeamLeaders集合,以便更改检测可以识别已删除哪些领导以及已添加哪些领导。当您调用SaveChanges时,EF将根据检测到的更改为连接表编写相应的DELETE和INSERT语句。最简单的方法如下:

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Find(teamLeader.UserId);
    if (user != null)
        original.TeamLeaders.Add(user)
}
context.SaveChanges();

Find will fetch the team leaders from the context if they are already loaded. If they aren't loaded it will query the database.

如果已经加载,Find将从上下文中获取团队领导。如果未加载它将查询数据库。

If you want to avoid additional queries you can attach the team leaders manually to the context:

如果您想避免其他查询,可以手动将团队领导附加到上下文:

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Local
        .SingleOrDefault(o => o.UserId == teamLeader.UserId);
    if (user == null)
    {
        user = new User { UserId = teamLeader.UserId };
        context.UserProfiles.Attach(user);
    }
    original.TeamLeaders.Add(user)
}
context.SaveChanges();

Except the first query to load the original here is no further database query involved.

除了在此处加载原始文件的第一个查询之外,不涉及进一步的数据库查询。

BTW: You should be able to use the strongly typed Include version with EF Code First:

顺便说一句:你应该能够使用EF Code First的强类型包含版本:

Include(u => u.TeamLeaders)

You just need using System.Data.Entity; in your code file to get access to this version.

你只需要使用System.Data.Entity;在您的代码文件中以获取对此版本的访问权限。