EF4 Code First - 多对多关系问题

时间:2011-03-11 07:53:40

标签: c# asp.net-mvc c#-4.0 entity-framework-4 ef-code-first

在保存多对多关系的关系时,我的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(虽然我没有从数据库中取出)

2 个答案:

答案 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表。

第二个问题:即使您实现EqualsGetHashCode,您也只会解决两个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。