EF代码第一个映射表,具有来自同一类的2个FK

时间:2015-01-03 16:26:30

标签: c# entity-framework ef-code-first

我正在尝试创建一个友谊映射表,其中包含2个来自同一个类(用户)的FK。在Add-Migration上我收到以下错误:

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.

这是我的两个模型类。

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; }
}

我尝试覆盖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);

}

这会导致显示新的错误消息。

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

非常感谢任何帮助。在此先感谢!!

2 个答案:

答案 0 :(得分:1)

我总是尝试“自下而上”处理这些问题,即首先尝试没有任何映射的类模型(流畅或数据注释):

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; }
}

(使用更中性的名字UserAssociation)。

由于没有为UserAssociation定义密钥,因此很快就失败了。所以我添加了关键注释:

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

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

现在已创建模型,但UserIDFriendID未用作外键。相反,EF本身又添加了两个外键字段。不好。所以我添加了外键指令:

[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; }

这引起了异常:

  

引入FOREIGN KEY约束...可能会导致循环或多个级联路径。

这是因为两个外键都是使用级联删除创建的。 SQL Server只允许在一个表中使用两个级联外键。防止级联删除只能通过流畅的映射来完成,因此我删除了所有注释(我不喜欢混合流畅的映射和注释)并添加了这些映射:

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});
}

现在是宾果游戏。如你所见,你很亲密,但你开始从协会方面流利的映射。此外,您还有WithMany()代替WithMany(u => u.Associations))。但是,使用这样的映射我无法使其工作(不知何故,EF似乎并不及时了解User类。)

另一点是,在添加数据时,如果已将新UserAssociation添加到相应的DbSet上下文中,则EF仅接受新User.Associations,而不是将其添加到UserAssociation。此外,在现有用户之间创建关联时,我必须在新UserAssociation中设置原始外键值。设置对象引用导致

  

保存未公开其关系的外键属性的实体时发生错误。

这没有意义,因为{{1}}确实公开了这些关键属性。

所以看起来这些自我引用与显式联结类有很多很多关联,我们只是勉强避免EF存在的各种问题。感觉不舒服。

答案 1 :(得分:0)

我有一个问题的解决方案,但您需要修改您的模型。为了创建朋友关系,我创建了一个继承User的Friend类。在这个类中我声明属性FriendshipTime,你不需要为它声明一个Id,它使用Id的用户。现在,在用户中你应该有一个朋友的集合。

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; }
}

使用此模型,您可以执行以下操作:

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

我想要做的其他事情是Key字段是Integer,Code First默认为DatabaseGeneratedOption.Identity。使用Guid,您需要明确 配置这个。这些是您可以配置的唯一类型 Code First生成数据库时的身份