我使用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是一个用户集合,即它是一种自我参考的多人关系。
简单地说,一个用户可以有多个团队负责人。这个配置似乎是正确的,因为它创建了我所期望的决斗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尝试向系统中添加一个与所选团队负责人的配置文件相匹配的新用户。
有人能告诉我这件事的哪一部分做错了吗?我不确定是否需要更新映射,或者更改将属性从视图模型复制到模型的方式
您必须根据您的更改修改original
用户中加载的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
将从上下文中获取他们。如果没有加载它们,它将查询数据库。
如果你想避免额外的查询,你可以手动将团队领导附加到上下文中:
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
的第一个查询之外,这里不涉及进一步的数据库查询。
BTW:您应该能够使用带有EF Code First的强类型Include
版本:
Include(u => u.TeamLeaders)
您只需要代码文件中的using System.Data.Entity;
就可以访问此版本。