删除嵌套集合中的条目

Posted

技术标签:

【中文标题】删除嵌套集合中的条目【英文标题】:Deleting entites in nested collections 【发布时间】:2020-03-10 20:04:54 【问题描述】:

在更新实体时,在实体方法中删除了集合的多个子项时,我无法保存对数据库的更改。 我从HighLvlEntity 实体调用RemoveSpecificMiddleLvlEntities 方法。一些孩子被删除。从 EfRepository 通过 Update 方法更新时,会引发以下错误。 我是否必须通过我的 Delete 方法手动处理集合中的所有级别的子项? 我想我并没有真正了解基础实体框架 6 概念之一?想要让我的逻辑在我的实体中删除哪些实体,而不是在任何存储库或服务中。

提前谢谢你。

错误:System.InvalidOperationException:“操作失败: 关系不能改变,因为其中一项或多项 外键属性不可为空。当对某项进行更改时 关系,相关的外键属性设置为空值。 如果外键不支持空值,一个新的关系 必须定义,外键属性必须分配另一个 非空值,否则必须删除不相关的对象。”

我的简化实体:

public class Base

    public Guid Id  get; set; 

    public Base()
    
        Id = Guid.NewGuid();
    

public class HighLvlEntity : Base

    public HighLvlEntity(string name)
    
        Name = name;
    
    public string Name  get; set; 
    public ICollection<MiddleLvlEntity> middleLvlEntities  get; set;  = new List<MiddleLvlEntity>();

    public void RemoveSpecificMiddleLvlEntities(ICollection<MiddleLvlEntity> middleLvlEntitiesOfCriteria)
    
        foreach (var MiddleLvlEntity in middleLvlEntitiesOfCriteria)
        
            var foundmiddleLvlEntity = middleLvlEntities.Where(p => p.Testcriteria == MiddleLvlEntity.Testcriteria).First();
            middleLvlEntities.Remove(foundmiddleLvlEntity);
            
            
        
    

public class MiddleLvlEntity : Base

    public MiddleLvlEntity(string testcriteria)
    
        Testcriteria = testcriteria;
    
    public ICollection<LowLvlTwo> LowLvlTwos  get; set;  = new List<LowLvlTwo>();
    public ICollection<LowLvlOne> LowLvlOnes  get; set;  = new List<LowLvlOne>();
    public string Testcriteria  get; set; 
    public HighLvlEntity HighLvlEntity  get; set; 
    public Guid HighlvlEntityId  get; set; 

public class LowLvlOne : Base

    public LowLvlOne(DateTime date)
    
        Date = date;
    
    public DateTime Date  get; set; 
    public Guid middleLvlEntityId  get; set; 
    public MiddleLvlEntity MiddleLvlEntity  get; set; 

public class LowLvlTwo : Base

    public LowLvlTwo(DateTime date)
    
        Date = date;
    
    public DateTime Date  get; set; 
    public Guid middleLvlEntityId  get; set; 
    public MiddleLvlEntity MiddleLvlEntity  get; set; 

EfRepository:

public class EfRepository<T> : IRepository<T> where T : Base

    protected readonly Context _dbContext;

    public EfRepository(Context dbContext)
    
        _dbContext = dbContext;
    

    public void Update(T entity)
    
        _dbContext.Entry(entity).State = EntitieState.Modified;

        _dbContext.SaveChanges();
    

    public void Delete(T entity)
    
        _dbContext.Set<T>().Remove(entity);
        _dbContext.SaveChanges();
    

更新: This seems to be same problem。 然后我的问题是,我到底要如何构建我的应用程序才能工作。我有一个包含实体、接口和服务的核心项目。具有实现和连接到实体框架的基础设施项目。所以我在核心项目中的实体不知道 dbcontext 和从子集合中删除项目。我是否必须从在实体本身中实现业务逻辑的想法转变为在核心项目中使用服务接口和在基础设施项目中使用服务实现?

【问题讨论】:

这能回答你的问题吗? Entity Framework .Remove() vs. .DeleteObject() @EugenePodskal 不,因为我知道。在我的架构方法中(参见更新),我的实体不知道 dbcontext。 【参考方案1】:

不幸的是,当人们想要正确处理所谓的孤立记录时,似乎(大多数情况下)不可能完全不了解实体。

How to remove child one to many related records in EF code first database?

那么,有哪些替代方案?

咬紧牙关,让实体感知持久性(最低限度)。

显然不是你想要的,但如果我们

    以更类似于DDD 的方式将我们的实体改造为更大的操作/面向任务的聚合体,而不是普通实体 和明智的抽象持久性

它可能不会像现在看起来那么糟糕

在 SaveChanges(或等)上添加一些巧妙的自定义孤儿处理

类似https://***.com/a/42048505/3745022

或者尝试从 identifying relationships 中获得最大的收益 - 对他们来说孤儿删除工作。

不要删除实体

除非有实际删除这些实体的要求(域限制、数据库大小/性能、GDPR...),否则您可以将它们标记为已删除或将它们添加到某些 DeletedParent

好吧,我知道这可能不可行,并且会使系统的某些方面更加复杂和容易出错,但在许多情况下,不删除实体可能是非常有利的(能够回滚操作、生成历史数据等等)

使用 EF Core (2.2?)

EF Core 似乎支持正确的orphan removal。

虽然您无法使用 .NET Framework use EF Core 3.0,但您仍然可以尝试 EF Core 2.2,因为它也应该支持孤儿删除。

是的,EF 和 EF Core 之间存在一些差异,但对于一个看似处于早期阶段的项目来说,它们并非不可克服。

【讨论】:

感谢您的详细解答和努力。我熟悉 EF Core,正如你所说,那里从来没有遇到过这个问题。环境导致我使用 .NET Framework。我选择不删除和标记。

以上是关于删除嵌套集合中的条目的主要内容,如果未能解决你的问题,请参考以下文章

从嵌套for循环中的指针集中删除项目

选择和修改嵌套向量中的条目的最佳实践

MongoDB - 仅当嵌套数组中的所有条目存在时才更新它们

在 Firestore 中使用 Angular 删除集合中的文档

mongodb中的嵌套条目,仅在子字段的最大值上求和和平均值

R子集嵌套列表,选择多个条目