Entity Framework Core:删除关系但不删除实体

chr*_*ark 5 c# entity-framework-core asp.net-core

假设您有两个实体,但没有“关系”实体,您将如何删除关系?

假设以下实体...

模型类:

public class DisplayGroup
{
        [Key]
        public int GroupId { get; set; }
        public string Description { get; set; }
        public string Name { get; set; }
        public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}

public class DisplayItem
{
        [Key]
        public int ItemId { get; set; }
        public string Description { get; set; }
        public string FileType { get; set; }
        public string FileName { get; set; }
        public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}

public class LookUpGroupItem
{
        public int ItemId { get; set; }
        public DisplayItem DisplayItem { get; set; }
        public int GroupId { get; set; }
        public DisplayGroup DisplayGroup { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是删除关系的代码。注意:我不想删除这些实体,它们只是不再共享关系。

public void RemoveLink(DisplayGroup g, DisplayItem d)
{
    _dataContext.Remove(g.LookUpGroupItems.Single(x => x.ItemId == d.ItemId));
}
Run Code Online (Sandbox Code Playgroud)

上面的方法会报错:

发生 System.ArgumentNullException
Message=Value 不能为 null。

看起来情况确实如此,因为LookUpGroupItemsis null,但这些是从数据库调用的。我同意,每当我从数据库中获取时,我不想加载所有实体关系对象,但是,最有效的方法是什么?

附加注意:这个问题与参数空异常无关。它明确说明了如何删除 Entity Framework Core 中的关系。

Iva*_*oev 4

以下不是有效方法,但却是最可靠的方法:

public void RemoveLink(DisplayGroup g, DisplayItem d)
{
    var link = _dataContext.Find<LookUpGroupItem>(g.GroupId, d.ItemId); // or (d.ItemId, g.GroupId) depending of how the composite PK is defined
    if (link != null)
        _dataContext.Remove(link);
}
Run Code Online (Sandbox Code Playgroud)

它简单明了。Find方法用于在本地缓存中定位实体或从数据库加载它。如果找到,该Remove方法用于将其标记为删除(这将在您调用时应用SaveChanges)。

它不是最有效的,因为当实体不包含在本地缓存中时会进行数据库往返。

最有效的是使用“存根”实体(仅填充 FK 属性):

var link = new LookUpGroupItem { GroupId = g.GroupId, ItemId = d.ItemId };
_dataContext.Remove(link);
Run Code Online (Sandbox Code Playgroud)

这只会在调用DELETE时发出 SQL 命令。ApplyChanges但它有以下缺点:

(1) 如果_dataContext已经包含(正在跟踪)LookUpGroupItem具有相同 PK 的实体,则Remove调用将抛出InvalidOperationException类似“无法跟踪实体类型 'LookUpGroupItem' 的实例,因为另一个实例具有键值 'GroupId:1, ItemId: 1' 已被跟踪。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。”

(2) 如果数据库表不包含具有指定复合PK的记录,则会SaveChanges抛出“数据库操作预计影响 1 行,但实际影响 0 行。数据可能已被修改或删除,因为实体已加载。有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=527962 。 ”(这种行为实际上被包括我在内的许多人认为是一个错误,但事实就是如此是)。DbUpdateConcurrencyException

Shorty,只有当您仅针对该操作使用短期新建DbContext并且您绝对确定数据库中存在具有此类 PK 的记录时,您才能使用优化方法。在所有其他情况下(一般情况下),您应该使用第一种方法。