我在 EntityFramework 和同一实体的多对多关系方面存在条目删除问题。考虑这个简单的例子:
实体:
public class UserEntity {
// ...
public virtual Collection<UserEntity> Friends { get; set; }
}
流畅的 API 配置:
modelBuilder.Entity<UserEntity>()
.HasMany(u => u.Friends)
.WithMany()
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
m.ToTable("FriendshipRelation");
});
Cascade Delete? 删除 UserEntity(例如 Foo)的最佳方法是什么?
它现在寻找我,我必须清除 Foo 的Friends 集合,然后我必须加载所有其他UserEntities,其中包含 Friends 中的 Foo,然后从每个列表中删除 Foo,然后再删除 Foo 来自用户。但这听起来太复杂了。
是否可以直接访问关系表,以便我可以删除这样的条目
// Dummy code
var query = dbCtx.Set("FriendshipRelation").Where(x => x.UserId == Foo.Id || x.FriendId == Foo.Id);
dbCtx.Set("FriendshipRelation").RemoveRange(query);
谢谢!
Update01:
我知道这个问题的最佳解决方案是在调用 SaveChanges 之前执行原始 sql 语句:
dbCtx.Database.ExecuteSqlCommand(
"delete from dbo.FriendshipRelation where UserId = @id or FriendId = @id",
new SqlParameter("id", Foo.Id));
但这样做的缺点是,如果 SaveChanges 由于某种原因失败,FriendshipRelation 已经被删除并且无法回滚。还是我错了?
最佳答案
答案很简单:
Entity Framework 在不知道哪些属性属于关系时无法定义级联删除。
此外,在多对多关系中还有第三个表,负责管理关系。此表必须至少有 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
}
现在,您必须更改 UserEntity。它不是 UserEntity 的集合,而是 UserFriendship 的集合。像这样:
public class UserEntity
{
...
public virtual ICollection<UserFriendship> Friends { get; set; }
}
让我们看看映射:
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
生成的迁移:
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);
检索所有用户的 friend :
var someUser = ctx.UserEntity
.Include(i => i.Friends.Select(x=> x.Friend))
.SingleOrDefault(i => i.UserEntityId == 1);
一切正常。但是,该映射存在问题(这也发生在您当前的映射中)。假设“我”是一个 UserEntity:
当我检索我的 Friends 属性时,它返回“John”、“Ann”,但没有返回“Richard”。为什么?因为理查德是这段关系的“缔造者”,而不是我。 Friends 属性仅绑定(bind)到关系的一侧。
好的。我该如何解决这个问题?简单!更改您的 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; }
}
更新映射:
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);
无需迁移。
检索所有用户的 friend :
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);
是的,您必须迭代集合并删除所有子对象。在这个线程中查看我的答案 Cleanly updating a hierarchy in Entity Framework
按照我的回答,只需创建一个 UserFriendship 数据库集:
public DbSet<UserFriendship> UserFriendships { get; set; }
现在您可以检索特定用户 id 的所有好友,只需一次性删除所有好友,然后删除该用户。
是的,这是可能的。您现在有一个 UserFriendship 数据库集。
希望对您有所帮助!
关于c# - Entity Framework 代码优先 : CASCADE DELETE for same table many-to-many relationship,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32655959/