简体   繁体   English

具有相同类别的2个FK的EF代码第一个映射表

[英]EF code first mapping table with 2 FK's from same class

I'm trying to create a Friendship mapping table that has 2 FK's that originate from the same class (User). 我正在尝试创建一个具有2个FK的Friendship映射表,这些FK源自同一类(用户)。 On Add-Migration I get the following error: 在添加迁移时,出现以下错误:

Unable to determine the principal end of an association between the types 'GameAPI.Models.UserFriendMap' and 'GameAPI.Models.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

Here are my two model classes. 这是我的两个模型类。

public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int UserID { get; set; }

    [Required]
    public string Username { get; set; }

    public ICollection<UserFriendMap> Friendships { get; set; }
}

public class UserFriendMap
{
    [Required]
    [Key]
    [Column(Order = 1)]
    public int UserID { get; set; }

    [Required]
    [Key]
    [Column(Order = 2)]
    public int FriendID { get; set; }

    [ForeignKey("UserID"), InversePropertyAttribute("Friendships")]
    public User User { get; set; }

    [ForeignKey("FriendID"), InversePropertyAttribute("Friendships")]
    public User Friend { get; set; }

    [Required]
    public DateTime FriendshipDate { get; set; }
}

I tried override the OnModelCreating method in the DbContext: 我尝试在DbContext中重写OnModelCreating方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<UserFriendMap>()
        .HasKey(m => new { m.UserID, m.FriendID });

    modelBuilder.Entity<UserFriendMap>()
            .HasRequired(m => m.User)
            .WithMany()
            .HasForeignKey(m => m.UserID);

    modelBuilder.Entity<UserFriendMap>()
            .HasRequired(m => m.Friend)
            .WithMany()
            .HasForeignKey(m => m.FriendID);

}

This causes a new error message to be displayed. 这将导致显示新的错误消息。

Schema specified is not valid. Errors: The relationship 'GameAPI.Models.UserFriendMap_User' was not loaded because the type 'GameAPI.Models.User' is not available.

Any help would be greatly appreciated. 任何帮助将不胜感激。 Thanks in advance!! 提前致谢!!

I always try to approach such problems "bottom up", ie first try the class model without any mapping (fluent nor data annotations): 我总是尝试“自下而上”地解决此类问题,即,首先尝试没有任何映射(流利或数据注释)的类模型:

public class User
{
    public int UserID { get; set; }
    public string Username { get; set; }
    public virtual ICollection<UserAssociation> Associations { get; set; }
}

public class UserAssociation
{
    public int UserID { get; set; }
    public int FriendID { get; set; }

    public DateTime FriendshipDate { get; set; }

    public virtual User User { get; set; }
    public virtual User Friend { get; set; }
}

(Using the more neutral name UserAssociation ). (使用更中性的名称UserAssociation )。

That quickly failed because there was no key defined for UserAssociation . 很快失败了,因为没有为UserAssociation定义UserAssociation So I added the key annotations: 所以我添加了关键注释:

[Key, Column(Order = 1)]
public int UserID { get; set; }

[Key, Column(Order = 2)]
public int FriendID { get; set; }

Now the model was created, but UserID and FriendID were not used as foreign key. 现在已经创建了模型,但是没有将UserIDFriendID用作外键。 Instead, EF had added two foreign key fields itself. 相反,EF本身添加了两个外键字段。 Not good. 不好。 So I added foreign key instructions: 因此,我添加了外键说明:

[Key, Column(Order = 1)]
[ForeignKey("User"), InverseProperty("Associations")]
public int UserID { get; set; }

[Key, Column(Order = 2)]
[ForeignKey("Friend"), InverseProperty("Associations")]
public int FriendID { get; set; }

This caused an exception: 这导致异常:

Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths. 引入FOREIGN KEY约束...可能会导致循环或多个级联路径。

This is because both foreign keys are created with cascaded delete. 这是因为两个外键都是使用级联删除创建的。 SQL Server just doesn't allow two cascading foreign keys in one table. SQL Server只是不允许在一个表中使用两个级联外键。 Preventing cascading delete can only be done by fluent mapping, so I removed all annotations (I don't like to mix fluent mapping and annotations) and added these mappings: 只能通过流利的映射来防止级联删除,因此我删除了所有注释 (我不喜欢将流利的映射和注释混合在一起)并添加了以下映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().HasMany(u => u.Associations)
                .WithRequired(a => a.User).HasForeignKey(a => a.UserID)
                .WillCascadeOnDelete(false);
    modelBuilder.Entity<User>().HasMany(u => u.Associations)
                .WithRequired(a => a.Friend).HasForeignKey(a => a.FriendID)
                .WillCascadeOnDelete(false);

    modelBuilder.Entity<UserAssociation>()
                .HasKey(a => new {a.UserID, a.FriendID});
}

Now it was bingo. 现在是宾果游戏。 As you see, you were close, but you started the fluent mappings from the association's side. 如您所见,您已经很近了,但是您是从协会的一侧开始进行流利的映射的。 Also, you had WithMany() in stead of WithMany(u => u.Associations) ). 另外,您有WithMany()而不是WithMany(u => u.Associations) )。 However, using mappings like that I couldn't get it working (somehow, EF didn't seem to know the User class in time). 但是,使用这样的映射无法正常工作(以某种方式,EF似乎不及时知道User类)。

Another point is that when adding data, EF only accepted new UserAssociation if the were added to their corresponding DbSet of the context, not if they were added to User.Associations . 另一点是,在添加数据时,如果将EF添加到其对应的上下文DbSet中,则EF仅接受新的UserAssociation ,而不是将其添加到User.Associations Also, when creating an association between existing users, I had to set the primitive foreign key values in the new UserAssociation . 另外,在现有用户之间创建关联时,我必须在新的UserAssociation设置原始外键值。 Setting the object references resulted in 设置对象引用导致

An error occurred while saving entities that do not expose foreign key properties for their relationships. 保存不公开外键属性为其关系的实体时发生错误。

Which doesn't make sense, because UserAssociation does expose these key properties. 这没有任何意义,因为UserAssociation确实公开了这些关键属性。

So it looks like with these self-referencing many to many associations with an explicit junction class we're only barely dodging all kinds of issues EF has in store. 因此,看起来像这些自引用具有显式联结类的许多到许多关联,我们几乎没有回避EF存储中的所有问题。 Doesn't feel good. 感觉不好

I have a solution to your problem but you need to modify your model. 我有一个解决您的问题的方法,但是您需要修改模型。 To create the friend relationship I create a Friend class that inherit of User. 为了创建朋友关系,我创建了一个继承自User的Friend类。 In this class is where I declare the property FriendshipTime, you don't need to declare an Id for it, it uses the Id of User. 在此类中,我声明了属性FriendshipTime,您无需为此声明ID,它使用User的ID。 Now, in User you should have a collection of Friend. 现在,在“用户”中,您应该有一个“朋友”集合。

public class User
{  
    [Key]
    public int Id { get; set; }

    [Required]
    public string Username { get; set; }

    public ICollection<Friend> Friendships { get; set; }
}

public class Friend:User
{
    public int UserID { get; set; }

    [ForeignKey("UserID"), InversePropertyAttribute("Friendships")]
    public User User { get; set; }

    [Required]
    public DateTime FriendshipDate { get; set; }
}

With this model you can do something like this: 使用此模型,您可以执行以下操作:

 var user = new User()
          {
            Username = "User1",
            Friendships = new List<Friend>() { new Friend() { Username ="User2", FriendshipDate = DateTime.Now }, new Friend() { Username = "User3", FriendshipDate = DateTime.Now } }
          };

Other aclaration that I want to do is in the case where the Key field is an Integer, Code First defaults to DatabaseGeneratedOption.Identity. 我想要做的其他声明是在Key字段是Integer的情况下,Code First默认为DatabaseGeneratedOption.Identity。 With a Guid, you need to explicitly configure this. 使用Guid,您需要对此进行显式配置。 These are the only types that you can configure to be Identity when Code First is generating the database 这些是在Code First生成数据库时可以配置为Identity的唯一类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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