EF 代码第一个映射表,其中包含来自同一类的 2 个 FK
本文关键字:一类 FK 映射 第一个 代码 包含 EF | 更新日期: 2023-09-27 18:36:15
我正在尝试创建一个友谊映射表,其中包含来自同一类(用户)的 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.
任何帮助将不胜感激。提前感谢!!
我总是尝试"自下而上"地处理这些问题,即首先尝试没有任何映射(流利或数据注释)的类模型:
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; }
这导致了异常:
引入外键约束...可能导致循环或多个级联路径。
这是因为这两个外键都是使用级联删除创建的。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
类)。
另一点是,在添加数据时,EF 仅接受添加到上下文的相应DbSet
的新UserAssociation
,而不是将它们添加到User.Associations
中。此外,在现有用户之间创建关联时,我必须在新UserAssociation
中设置原始外键值。设置对象引用会导致
保存未公开其关系的外键属性的实体时出错。
这没有意义,因为UserAssociation
确实公开了这些关键属性。
因此,看起来通过这些与显式联结类的自引用多对多关联,我们只能勉强回避 EF 存储的各种问题。感觉不好。
我有解决您问题的方法,但您需要修改模型。为了创建朋友关系,我创建了一个继承用户的 Friend 类。在此类中,我声明了属性 FriendshipTime,你不需要为它声明一个 ID,它使用 User 的 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 } }
};
我想做的其他声明是在键字段是整数的情况下,代码优先默认为 DatabaseGenerated Option.Identity。使用 GUID,您需要显式配置此项。这些是您可以配置为的唯一类型代码优先生成数据库时的标识