EntityFramework CodeFirst:CASCADE DELETE用于同一个表的多对多关系

ten*_*its 10 c# entity-framework code-first ef-code-first entity-framework-6

我有EntityFramework的条目删除问题和同一实体的多对多关系.考虑这个简单的例子:

实体:

public class UserEntity {
    // ...
    public virtual Collection<UserEntity> Friends { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

流畅的API配置:

modelBuilder.Entity<UserEntity>()
    .HasMany(u => u.Friends)
    .WithMany()
    .Map(m =>
    {
        m.MapLeftKey("UserId");
        m.MapRightKey("FriendId");
        m.ToTable("FriendshipRelation");
    });
Run Code Online (Sandbox Code Playgroud)
  1. 我是否正确,无法Cascade Delete在Fluent API中定义?
  2. UserEntity例如,删除a的最佳方法是什么Foo

    现在找我,我一定要ClearFooFriends集合,然后我需要加载的所有其他UserEntities,其中包含FooFriends,然后取出Foo从每个列表,之前我删除FooUsers.但这听起来太复杂了.

  3. 是否可以直接访问关系表,以便我可以删除这样的条目

    // Dummy code
    var query = dbCtx.Set("FriendshipRelation").Where(x => x.UserId == Foo.Id || x.FriendId == Foo.Id);
    dbCtx.Set("FriendshipRelation").RemoveRange(query);
    
    Run Code Online (Sandbox Code Playgroud)

谢谢!

Update01:

  1. 我知道这个问题的最佳解决方案是在调用之前执行原始sql语句SaveChanges:

    dbCtx.Database.ExecuteSqlCommand(
        "delete from dbo.FriendshipRelation where UserId = @id or FriendId = @id",
        new SqlParameter("id", Foo.Id));
    
    Run Code Online (Sandbox Code Playgroud)

    但这样做的缺点是,如果SaveChanges由于某种原因导致失败,FriendshipRelation则已经被删除而无法回滚.还是我错了?

Fab*_*Luz 12

问题1

答案很简单:

当实体框架不知道哪些属性属于该关系时,它无法定义级联删除.

此外,在许多:很多关系中,有第三个表,负责管理关系.此表必须至少有2个FK.您应该为每个FK配置级联删除,而不是为"整个表".

解决方案是创建FriendshipRelation实体.像这样:

public class UserFriendship
{
    public int UserEntityId { get; set; } // the "maker" of the friendship

    public int FriendEntityId { get; set;  }´ // the "target" of the friendship

    public UserEntity User { get; set; } // the "maker" of the friendship

    public UserEntity Friend { get; set; } // the "target" of the friendship
}
Run Code Online (Sandbox Code Playgroud)

现在,你必须改变UserEntity.UserEntity它有一个集合而不是集合UserFriendship.像这样:

public class UserEntity
{
    ...

    public virtual ICollection<UserFriendship> Friends { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我们来看看映射:

modelBuilder.Entity<UserFriendship>()
    .HasKey(i => new { i.UserEntityId, i.FriendEntityId });

modelBuilder.Entity<UserFriendship>()
    .HasRequired(i => i.User)
    .WithMany(i => i.Friends)
    .HasForeignKey(i => i.UserEntityId)
    .WillCascadeOnDelete(true); //the one

modelBuilder.Entity<UserFriendship>()
    .HasRequired(i => i.Friend)
    .WithMany()
    .HasForeignKey(i => i.FriendEntityId)
    .WillCascadeOnDelete(true); //the one
Run Code Online (Sandbox Code Playgroud)

生成的迁移:

CreateTable(
    "dbo.UserFriendships",
    c => new
        {
            UserEntityId = c.Int(nullable: false),
            FriendEntityId = c.Int(nullable: false),
        })
    .PrimaryKey(t => new { t.UserEntityId, t.FriendEntityId })
    .ForeignKey("dbo.UserEntities", t => t.FriendEntityId, true)
    .ForeignKey("dbo.UserEntities", t => t.UserEntityId, true)
    .Index(t => t.UserEntityId)
    .Index(t => t.FriendEntityId);
Run Code Online (Sandbox Code Playgroud)

要检索所有用户的朋友:

var someUser = ctx.UserEntity
    .Include(i => i.Friends.Select(x=> x.Friend))
    .SingleOrDefault(i => i.UserEntityId == 1);
Run Code Online (Sandbox Code Playgroud)

所有这一切都很好.但是,映射存在问题(在当前映射中也会发生).假设"我"是UserEntity:

  • 我向约翰做了一个朋友请求 - 约翰接受了
  • 我向Ann - Ann发了一个朋友请求加入
  • 理查德向我发了一个朋友请求 - 我接受了

当我检索我的Friends财产时,它返回"John","Ann",但不是"Richard".为什么?因为理查德是关系的"制造者"而不是我.该Friends属性仅限于该关系的一方.

好.我怎么解决这个问题?简单!改变你的UserEntity课程:

public class UserEntity
{

    //...

    //friend request that I made
    public virtual ICollection<UserFriendship> FriendRequestsMade { get; set; }

    //friend request that I accepted
    public virtual ICollection<UserFriendship> FriendRequestsAccepted { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

更新映射:

modelBuilder.Entity<UserFriendship>()
    .HasRequired(i => i.User)
    .WithMany(i => i.FriendRequestsMade)
    .HasForeignKey(i => i.UserEntityId)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<UserFriendship>()
    .HasRequired(i => i.Friend)
    .WithMany(i => i.FriendRequestsAccepted)
    .HasForeignKey(i => i.FriendEntityId)
    .WillCascadeOnDelete(false);
Run Code Online (Sandbox Code Playgroud)

不需要迁移.

要检索所有用户的朋友:

var someUser = ctx.UserEntity
    .Include(i => i.FriendRequestsMade.Select(x=> x.Friend))
    .Include(i => i.FriendRequestsAccepted.Select(x => x.User))
    .SingleOrDefault(i => i.UserEntityId == 1);
Run Code Online (Sandbox Code Playgroud)

问题2

是的,您必须迭代集合并删除所有子对象.请参阅此线程中的答案干净地更新实体框架中的层次结构

根据我的回答,只需创建一个UserFriendshipdbset:

public DbSet<UserFriendship> UserFriendships { get; set; }
Run Code Online (Sandbox Code Playgroud)

现在,您可以检索特定用户ID的所有朋友,只需一次删除所有朋友,然后删除该用户.

问题3

对的,这是可能的.你UserFriendship现在有一个dbset.

希望能帮助到你!