实体框架代码优先,同一张表上的多对多关系

Posted

技术标签:

【中文标题】实体框架代码优先,同一张表上的多对多关系【英文标题】:Entity Framework code-first, Many-to-Many relationship on the same table 【发布时间】:2016-10-10 16:25:11 【问题描述】:

我正在建立一个具有好友功能的社交网络。 我的想法是我已经有了默认的 ApplicationUser 类,所以我创建了一个名为 Friend

的新表
public class Friend

    [Key]
    [Column(Order = 1)]
    public string SenderId  get; set; 

    [Key]
    [Column(Order = 2)]
    public string ReceiverId  get; set; 

    //Status == true : Friend request accepted
    //Status == false : Friend request not accepted
    public bool Status  get; set; 

ApplicationUser 中,我定义了 2 个导航属性 SendersReceivers(链接到 Friend 表)

public class ApplicationUser : IdentityUser

    [Required]
    [StringLength(50)]
    public string Name  get; set; 

    [Required]
    public bool Gender  get; set; 

    [StringLength(255)]
    public string Address  get; set; 

    [StringLength(255)]
    public string Job  get; set; 

    [StringLength(255)]
    public string Image  get; set; 

    public DateTime Birthday  get; set; 

    public ICollection<ApplicationUser> Senders  get; set; 
    public ICollection<ApplicationUser> Receivers  get; set; 

最后在 ApplicationDbContext 中,我使用 Fluent Api 声明了 2 个表之间的关系

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>


    public DbSet<Friend> Friends  get; set; 

    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    
    

    public static ApplicationDbContext Create()
    
        return new ApplicationDbContext();
    

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        modelBuilder.Entity<ApplicationUser>()
            .HasMany(a => a.Senders)
            .WithMany(a => a.Receivers)
            .Map(m =>
            
                m.MapLeftKey("ReceiverId");
                m.MapRightKey("SenderId");
                m.ToTable("Friends");
            );
        base.OnModelCreating(modelBuilder);

    

但是当我添加迁移时,它会创建 2 个这样的表,它们都不是我需要的(一个没有外键,一个没有 Status 属性)

public override void Up()
    
        CreateTable(
            "dbo.Friends1",
            c => new
                
                    SenderId = c.String(nullable: false, maxLength: 128),
                    ReceiverId = c.String(nullable: false, maxLength: 128),
                    Status = c.Boolean(nullable: false),
                )
            .PrimaryKey(t => new  t.SenderId, t.ReceiverId );

        CreateTable(
            "dbo.Friends",
            c => new
                
                    SenderId = c.String(nullable: false, maxLength: 128),
                    ReceiverId = c.String(nullable: false, maxLength: 128),
                )
            .PrimaryKey(t => new  t.SenderId, t.ReceiverId )
            .ForeignKey("dbo.AspNetUsers", t => t.SenderId)
            .ForeignKey("dbo.AspNetUsers", t => t.ReceiverId)
            .Index(t => t.SenderId)
            .Index(t => t.ReceiverId);

    

我该怎么办:(我在网上搜索过,这似乎是合法的,但它不起作用

【问题讨论】:

【参考方案1】:

但是当我添加迁移时,它会创建 2 个这样的表,它们都不是我需要的(一个没有外键,一个没有 Status 属性)

这是因为您混合了 EF 支持的两种可能的 many-to-many 关联 - 一种使用隐式联结表(来自 Fluent API 配置),另一种使用显式联结表(Friend 实体)和两个 one-to-many 关联.

由于只有在您没有与关联关联的其他数据并且您有一个(Status 属性)时才能使用第一种方法,因此您需要使用第二种方法。

为此,请按如下方式更改导航属性的类型:

public class ApplicationUser : IdentityUser

    // ...
    public ICollection<Friend> Senders  get; set; 
    public ICollection<Friend> Receivers  get; set; 

和配置:

modelBuilder.Entity<ApplicationUser>()
    .HasMany(e => e.Senders)
    .WithRequired()
    .HasForeignKey(e => e.ReceiverId);

modelBuilder.Entity<ApplicationUser>()
    .HasMany(e => e.Receivers)
    .WithRequired()
    .HasForeignKey(e => e.SenderId);

【讨论】:

还有一个问题,如何获取一个用户的好友列表? 我需要一个 ApplicationUser 列表,它们是另一个 ApplicationUser 的朋友。所以我传入它的 ID 和查询如下 var friends = (from f in _context.Friends from u in _context.Users where f.ReceiverId == u.Id || f.SenderId == u.Id where u.Id==id其中 f.Status==true 选择新 Id=u.Id, Name=u.Name, ).ToList(); var friends = (from u in _context.Users where u.Id == id from fId in (from s in u.Senders where s.Status == true select s.SenderId) .Concat(from r in u.Receivers where r.Status == true select r.ReceiverId) join f in _context.Users on fId equals f.Id select new Id = f.Id, Name = f.Name, ) .ToList(); 谢谢,但似乎查询实际上让用户获得了自己而不是朋友

以上是关于实体框架代码优先,同一张表上的多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

多对多关系实体框架核心db优先

实体框架代码优先的多对多关系的最佳方法

实体框架代码优先多对多不使用 GUID 加载相关实体

Fluent NHibernate:如何在关系表上映射具有附加属性的多对多关系?

Laravel 定义同一张表的多对多关系

Remove() 不适用于实体框架中的多对多关系