5

我正在使用具有以下配置的 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 是用户的 ICollection,即它是自引用多对多关系。

用简单的英语来说,一个用户可以有多个团队负责人。这个配置似乎是正确的,因为它创建了我所期望的决斗 FK/PK 链接表。

我有一个 MVC4 应用程序,它允许从集合中编辑、添加和删除团队负责人。

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

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

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

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

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

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

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

    original.CopyProperties(model);

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

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

4

1 回答 1

3

您必须根据您的更改修改用户中加载的TeamLeaders集合,original以便更改检测可以识别哪些领导者已被移除以及哪些已被添加。当您调用时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如果他们已经加载,将从上下文中获取团队负责人。如果它们未加载,它将查询数据库。

如果您想避免额外的查询,您可以手动将团队负责人附加到上下文中:

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();

除了要加载的第一个查询,original这里不涉及进一步的数据库查询。

顺便说一句:您应该能够将强类型Include版本与 EF Code First 一起使用:

Include(u => u.TeamLeaders)

您只需要using System.Data.Entity;在您的代码文件中即可访问此版本。

于 2012-09-16T11:17:40.050 回答