【问题标题】:EF code first mapping table with 2 FK's from same classEF 代码第一个映射表,其中包含来自同一类的 2 个 FK
【发布时间】:2015-01-03 16:26:30
【问题描述】:

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

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.

任何帮助将不胜感激。提前致谢!!

【问题讨论】:

  • 这个问题的重复解决方案并没有真正达到同样的目的。我想将关系建模为数据库中的一个单独实体和一个单独的模型类,因为这种关系本身将超过友谊日期。
  • 你是对的。而且,出乎意料的是,我找不到更好的副本。所以我决定深入研究一下,并添加了一个答案。

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


【解决方案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; }

现在模型已创建,但 UserID 和 FriendID 没有用作外键。相反,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 只是不允许在一个表中使用两个级联外键。防止级联删除只能通过fluent mapping来实现,所以我去掉了所有的注解(我不喜欢把fluent mapping和注解混在一起),添加了这些映射:

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 =&gt; u.Associations))。但是,使用这样的映射我无法让它工作(不知何故,EF 似乎没有及时知道 User 类)。

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

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

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

因此,通过这些与显式联结类的自引用多对多关联,我们似乎只能勉强回避 EF 存在的各种问题。感觉不太好。

【讨论】:

  • 这一切似乎有点奇怪。按照您的回答,我收到以下错误:Sequence contains no matching element
  • 我设法让它工作。 EF 不喜欢我的反向关系被称为Friendships。将其重命名为其他任何内容都可以正确构建模型。感谢您的所有帮助!
【解决方案2】:

我有解决您问题的方法,但您需要修改模型。为了创建朋友关系,我创建了一个继承 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 生成数据库时的身份

【讨论】:

  • 此解决方案有效,但并没有真正实现我想要的。我想在它自己的数据库表中建模这种友谊,并有一个代表这种关系的模型类。此解决方案将好友关系添加到 User 表中。有没有办法用类似于我上面发布的模型来实现我想要的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
  • 2011-12-14
  • 2020-06-25
  • 2014-10-01
  • 1970-01-01
  • 2020-05-13
相关资源
最近更新 更多