繁体   English   中英

实体框架DbContext混淆当两个外键指向同一张表时

[英]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
        }
    ]
}

SSMS中的结果和关系图

似乎EF混淆了识别正确的FK,一个是插入对象后必须自己填充的,另一个是我填充的?

更新:这是实体之间的关系:

  1. UserGroupMember是UserGroup和User之间的桥梁表

  2. 每个用户可以是1 ... *用户组的成员

  3. 每个用户组可以有1 ... *个用户作为成员

(直到这里它像一个普通的桥表一样代表多对多关系)

4.每个UserGroup可以有1 ... * ChildUserGroup作为成员

(这意味着UserGroup可以将User或ChildUserGroup作为成员,类似于Windows的“ Users and Groups”)

真诚的

此处的主要问题是,您的代码设置的导航属性比预期的要错误。

首先,您使用InverseProperty属性定义关系。

  • UserGroup.ChildUserGroupMembersUserGroupMember.ChildUserGroup与外键属性作为ChildUserGroupID构成一种关系
  • UserGroup.UserGroupMembersUserGroupMember.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导航应设置为ugcugm.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM