[英]Entity Framework DbContext Confusion When two foreign keys pointing at same table
public partial class MyDbContext : DbContext
{
public MyDbContext()
: base("name=Model1")
{
}
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<UserGroup> UserGroups { get; set; }
public virtual DbSet<UserGroupMember> UserGroupMembers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) { }
}
[Table("gnr.UserGroup")]
public partial class UserGroup
{
public UserGroup()
{
ChildUserGroups = new HashSet<UserGroupMember>();
UserGroupMembers = new HashSet<UserGroupMember>();
}
public long ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
[InverseProperty("ChildUserGroup")]
public virtual ICollection<UserGroupMember> ChildUserGroupMembers { get; set; }
[InverseProperty("UserGroup")]
public virtual ICollection<UserGroupMember> UserGroupMembers { get; set; }
}
[Table("gnr.UserGroupMember")]
public partial class UserGroupMember
{
public long ID { get; set; }
public long? UserID { get; set; }
public virtual User User { get; set; }
[ForeignKey("UserGroup")]
public long UserGroupID { get; set; }
public virtual UserGroup UserGroup { get; set; }
[ForeignKey("ChildUserGroup")]
public long? ChildUserGroupID { get; set; }
public virtual UserGroup ChildUserGroup { get; set; }
}
[Table("gnr.User")]
public partial class User
{
public User()
{
UserGroupMembers = new HashSet<UserGroupMember>();
}
public long ID { get; set; }
public string Username { get; set; }
public string MobileNumber { get; set; }
public virtual ICollection<UserGroupMember> UserGroupMembers { get; set; }
}
static void Main(string[] args)
{
WorkNotCorrectly();
Console.WriteLine("Done");
Console.ReadLine();
}
private static void WorkNotCorrectly()
{
using (var db = new MyDbContext())
{
var ug = new UserGroup { Title = "It's a new UserGroup 1" };
var cugm = new UserGroupMember { ChildUserGroupID = 1 };
ug.ChildUserGroupMembers.Add(cugm);
// After This Line
db.UserGroups.Add(ug);
MyDbContext将'cugm'对象的'ChildUserGroup'属性设置为'ug'
但是预期的行为是将'cugm'对象的'UserGroup'属性设置为'ug'
“ ug”对象对Json:
{
"ID": 0,
"Title": "It's a new UserGroup 1",
"Description": null,
"ChildUserGroupMembers": [
{
"ID": 0,
"UserID": null,
"User": null,
"UserGroupID": 0,
"UserGroup": null,
"ChildUserGroup": HERE IS THE PROBLEM => The MyDbContext sets the 'ChildUserGroup' property instead of 'UserGroup' property,
"ChildUserGroupID": 1
}
],
"UserGroupMembers": []
}
和SaveChanges:
db.SaveChanges();
}
}
SaveChanges之后将'ug'对象传递给Json:
{
"ID": 2,
"Title": "It's a new UserGroup 1",
"Description": null,
"ChildUserGroupMembers": [
{
"ID": 1,
"UserID": null,
"User": null,
"UserGroupID": 2,
"ChildUserGroupID": 2 WHAT???? IT IS CHANGED TO 2 ( THE NEW UserGroup THAT IS GENERATED),
"ChildUserGroup": HERE IS THE PROBLEM => The MyDbContext sets the 'ChildUserGroup' property instead of 'UserGroup' property
}
],
????? WHAT IS THIS??? WHY UserGroupMembers PROPERTY IS FILLED
"UserGroupMembers": [
{
"ID": 1,
"UserID": null,
"User": null,
"UserGroupID": 2,
"ChildUserGroupID": 2
}
]
}
预期的行为和结果是:
{
"ID": 2,
"Title": "It's a new UserGroup 1",
"Description": null,
"ChildUserGroupMembers": [
{
"ID": 1,
"UserID": null,
"User": null,
"UserGroupID": 2,
"UserGroup": 'The ug object with ID of 2 ',
"ChildUserGroupID": 1
}
]
}
似乎EF混淆了识别正确的FK,一个是插入对象后必须自己填充的,另一个是我填充的?
更新:这是实体之间的关系:
UserGroupMember是UserGroup和User之间的桥梁表
每个用户可以是1 ... *用户组的成员
每个用户组可以有1 ... *个用户作为成员
(直到这里它像一个普通的桥表一样代表多对多关系)
4.每个UserGroup可以有1 ... * ChildUserGroup作为成员
(这意味着UserGroup可以将User或ChildUserGroup作为成员,类似于Windows的“ Users and Groups”)
真诚的
此处的主要问题是,您的代码设置的导航属性比预期的要错误。
首先,您使用InverseProperty
属性定义关系。
UserGroup.ChildUserGroupMembers
和UserGroupMember.ChildUserGroup
与外键属性作为ChildUserGroupID
构成一种关系 UserGroup.UserGroupMembers
和UserGroupMember.UserGroup
与外键属性作为UserGroupID
构成另一个关系 在EF中,当您定义关系时,有3个组成部分,分别是从主体导航到从属导航,从主体导航到与外键属性。 设置其中任何一个的值也将决定其他的值。 如果您设置了任何导航,那么外键属性将被设置为与主键属性具有相同的值。 如果导航已经加载到内存中,则也会填充这些导航。
如SSMS输出所示,您的UserGroup表中已经有ID = 1的数据。 现在在您的代码中
UserGroup
ug
。 UserGroupMember
cugm
。 cugm.ChildUserGroupID
= 1的值。给定实体具有的关系cugm.ChildUserGroup
导航(与ChildUserGroupID
fk关联),应ChildUserGroupID
分配ID = 1的UserGroup
。 由于未将其加载到内存中,因此该导航将保持为空。 如果将ID = 1的UserGroup
加载到内存中,则方法与UserGroup.ChildUserGroupMembers
集合会向其中添加cugm
。 cugm
添加到ug.ChildUserGroupMembers
集合中。 意味着FK属性cugm.ChildUserGroupID
应该采用ug.ID
值。 反向导航的cugm.ChildUserGroup
应该指向ug
。 由于EF到目前为止尚未出现,因此在添加实体之前,不会发生任何上述更改。 ug
添加到UserGroups
数据库集,以便EF开始跟踪实体及其子级。 由于ug.ChildUserGroupMembers
集合已添加了cugm
,因此EF会将cugm.ChildUserGroup
设置为ug
。 ug
对象仍然具有临时密钥,因此EF不会将ug.ID
复制到cugm.ChildUserGroupID
这就是您获得第一个JSON的方式。 它设置了ChildUserGroup
因为您设置了它的反向导航。
调用SaveChanges后,EF会将实体ug
保存到数据库。 由于您的数据库已具有ID = 1的Entity,它将获得下一个ug.ID
。由于ug.ID
不再是临时值,因此cugm.ChildUserGroupID
将取其值并更改为2
。
现在,根据您的SSMS数据,您想要创建一个新的UserGroup
对象ug
,该对象在保存时将获得ID = 2。 您想创建一个新的UserGroupMember
对象cugm
,它将获得ID = 1(带有标识列的第一行。) cugm.UserGroupId
= 2,因此cugm.UserGroup
导航应设置为ug
& cugm.ChildUserGroupID
= 1。
这就是代码的样子
var ug = new UserGroup { Title = "It's a new UserGroup 1" };
var cugm = new UserGroupMember { ChildUserGroupID = 1 };
ug.UserGroupMembers.Add(cugm); // You want to set UserGroupID = 2 so cugm should be added to its corresponding inverse navigation
db.UserGroups.Add(ug);
db.SaveChanges();
上面的代码将在数据库中生成所需的确切记录。
ug
对象的JSON输出将如下所示(这在您的问题中也不正确)
{
"ID": 2,
"Title": "It's a new UserGroup 1",
"Description": null,
"UserGroupMembers": [{
"ID": 1,
"UserID": null,
"User": null,
"UserGroupID": 2,
"UserGroup": ... // Pointing to UserGroup with ID = 2
"ChildUserGroupID": 1,
"ChildUserGroup": null
}]
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.