在保存多对多关系的关系时,我的EF Code First模型遇到了一些问题。我的模特:
public class Event
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
在我的控制器中,我将一个或多个TagViewModel映射到Tag的类型,并将其发送到我的服务层以实现持久性。此时通过检查实体标签具有Id和名称(Id是隐藏字段,名称是我视图中的文本框)
当我现在尝试将标记添加到事件时,会出现问题。我们采取以下方案:
事件已经存在于我的数据库中,让我们说它已经有了相关标签C#, ASP.NET
如果我现在将以下标签列表发送到服务层:
ID Name
1 C#
2 ASP.NET
3 EF4
并首先从数据库中获取事件来添加它们,这样我就可以从我的DbContext中获得一个实际的事件,然后我就这样做了
myEvent.Tags.Add
添加标签..问题是SaveChanges()
我的数据库现在包含这组标签:
ID Name
1 C#
2 ASP.NET
3 EF4
4 C#
5 ASP.NET
这个,即使我保存的我的标签在保存时也设置了ID(虽然我没有从数据库中取出)
答案 0 :(得分:2)
我想我知道你的代码中发生了什么。让我用简单的例子来解释我的观点:
using (var context = new Context())
{
// Just let assume these are your tags received from view model.
var csharp = ...;
var aspnet = ...;
var ef4 = ...;
// Now you load Event
var e = context.Events.Where(e => e.Id == someId);
// Ups first access to Tag collection triggers lazy loading which
// is enabled by default so, all current tags are loaded
e.Tags.Add(csharp);
e.Tags.Add(aspnet);
e.Tags.Add(ef4);
// Now e.Tags.Count == 5 !!! Why?
context.SaveChanges();
}
第一个问题:因为在Event
实例之上创建的动态代理使用HashSet
Tags
,它会检查集合中是否已存在添加的实体。如果没有,它会添加实体,但它会跳过实体。为了能够正确检查你必须在标签中实现Equals和GetHashCode !因此,由于您没有这样做,它会将添加的标记作为带有临时密钥的新标记,并使用自动生成的密钥将它们添加到Tags
表。
第二个问题:即使您实现Equals
和GetHashCode
,您也只会解决两个C#和ASP.NET标记的问题。目前,上下文不跟踪EF4标记,因此该标记仍被视为新标记。您必须通知上下文中DB4中存在EF4标记。因此,在Attach
集合上触发延迟加载之前,请将Tags
所有标记添加到上下文中。默认情况下,将实体附加到上下文会将其状态设置为Unchanged
:
using (var context = new Context())
{
foreach (var tag in TagsFromView)
{
context.Attach(tag);
}
// Now you load Event
var e = context.Events.Where(e => e.Id == someId);
foreach(var tag in TagsFromView)
{
// First access will trigger lazy loading but already
// attached instances of tags are used
e.Tags.Add(tag);
}
// Now you must delete all tags present in e.Tags and not
// present in TagsFromView
context.SaveChanges();
}
如果您未在视图中创建新标记,则此方法有效。如果您也想这样做,则不得在上下文中附加新标记。您必须在现有标签和新标签之间有所不同(例如,新标签可以具有Id = 0)。
答案 1 :(得分:1)
您需要从数据库中获取标记,否则EF会将它们视为新项目并覆盖ID。