EF4 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 映射到标签类型,并将其发送到我的服务层以进行持久化。此时通过检查实体标签同时具有 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(尽管我没有从数据库中获取它)
I'm having some trouble with my EF Code First model when saving a relation to a many to many relationship. My Models:
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; }
}
In my controller, I map one or many TagViewModels into type of Tag, and send it down to my servicelayer for persistence. At this time by inspecting the entities the Tag has both Id and Name (The Id is a hidden field, and the name is a textbox in my view)
The problem occurs when I now try to add the Tag to the Event. Let's take the following scenario:
The Event is already in my database, and let's say it already has the related tags C#, ASP.NET
If I now send the following list of tags to the servicelayer:
ID Name
1 C#
2 ASP.NET
3 EF4
and add them by first fetching the Event from the DB, so that I have an actual Event from my DbContext, then I simply do
myEvent.Tags.Add
to add the tags.. Problem is that after SaveChanges()
my DB now contains this set of tags:
ID Name
1 C#
2 ASP.NET
3 EF4
4 C#
5 ASP.NET
This, even though my Tags that I save has it's ID set when I save it (although I didn't fetch it from the DB)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我想我知道你的代码中发生了什么。让我用简单的例子解释我的观点:
第一个问题:因为在
Event
实例之上创建的动态代理使用HashSet
作为Tags
它检查添加的实体是否已存在于集合中。如果没有,则添加该实体,否则跳过该实体。为了能够正确执行此检查,您必须在标签中实现 Equals 和 GetHashCode!因此,因为您没有这样做,所以它会将添加的标签作为带有临时键的新标签,并将它们添加到带有自动生成键的Tags
表中。第二个问题:即使您实现了
Equals
和GetHashCode
,您也只能解决 C# 和 ASP.NET 标记的重复性问题。目前上下文不跟踪 EF4 标记,因此该标记仍被视为新标记。您必须通知上下文数据库中存在 EF4 标记。因此,在触发Tags
集合上的延迟加载之前,让我们将所有标签附加到上下文。默认情况下,将实体附加到上下文会将其状态设置为未更改
:如果您不在视图中创建新标签,则此方法有效。如果您也想这样做,则不得将新标签附加到上下文。您必须区分现有标签和新标签(例如新标签可以具有 Id = 0)。
I think I know what happend in your code. Let me explain my opinion in simple example:
The first problem: Because a dynamic proxy created on top of your
Event
instance usesHashSet
forTags
it checks if added entity already exists in the collection. If not, it adds the entity othewise it skips the entity. To be able to do this check correctly YOU MUST IMPLEMENT Equals and GetHashCode in Tag! So because you didn't do it, it takes added tags as new ones with temporary keys and it adds them toTags
table with autogenerated key.The second problem: Even if you implement
Equals
andGetHashCode
you will solve only duplicity of C# and ASP.NET tags. At the moment the context doesn't track EF4 tag so this tag is still considered as a new one. You must inform the context that EF4 tag exists in DB. So letsAttach
all tags to the context before you trigger lazy loading onTags
collection. Attaching entity to the context by default sets its state toUnchanged
:This works if you don't create new tags in your view. If you want to do it as well, you mustn't attach new tags to the context. You have to differ between existing tags and new tags (for example new tags can have Id = 0).
您需要从数据库获取标签,否则 EF 会将它们视为新项目并覆盖 id。
You need to get the tags from the db, otherwise EF will treat them as new items and override the id.