1

I have a problem to save changes to database when updating entity where multiple children of collection are removed in method of entity. I call RemoveSpecificMiddleLvlEntities method from HighLvlEntity entity. Some Children are deleted. When updating through Update method from EfRepository following error is thrown. Do i have to manually all levels of children in the collections through my Delete method? I think i don't really get one of the basisc Entity Framework 6 concepts? Wanted to have my logic which entities are deleated in my Entity not in any repository or service.

Thank you in advance.

Error: System.InvalidOperationException: "The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted."

My simplified entities:

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();
    }
}

Update: This seems to be same problem. Then my question is, how the hell i can architecture my application that it works. I have a Core project with entities, interfaces and services. An Infrastructure project with implementation and connection to Entity Framework. So my entities in Core project don't know about dbcontext and removing items from childcollections. Do i have to move from the idea to implement business logic in the entity itself to use service-interfaces in Core project and service implementations in Infrastructure project?

D3ac0n
  • 15
  • 1
  • 5
  • Does this answer your question? [Entity Framework .Remove() vs. .DeleteObject()](https://stackoverflow.com/questions/17723626/entity-framework-remove-vs-deleteobject) – Eugene Podskal Mar 10 '20 at 20:58
  • @EugenePodskal No, because i know that. In my architectural approach (see update) my entities don't know about dbcontext. – D3ac0n Mar 10 '20 at 21:13

1 Answers1

0

Unfortunately, it seems that with EF6 it is (mostly) impossible to have full persistence ignorance with entities when one wants to correctly handle so-called orphaned records.

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

So, what are the alternatives?

Bite the bullet and make entities persistence aware (minimally).

Obviously not what you want, but if we

  1. remodel our entities in a more DDD-like way as larger operation/task-oriented aggregates instead of plain entities
  2. and judiciously abstract persistence

it may not end up being as bad as it might look now

Add some clever custom orphan handling on SaveChanges(or etc.)

Something like https://stackoverflow.com/a/42048505/3745022

Or try to get the best out of the identifying relationships - for them orphan deletion works.

Do not delete entities

Unless there is a requirement (domain restriction, DB size/performance, GDPR...) to actually delete those entities, you can just mark them as deleted or add them to some DeletedParent.

Well, I understand that it may not be feasible and it will make some aspects of the system more complex and error-prone, but in many cases not deleting entities can be quite advantageous (ability to roll operations back, produce historical data and etc.)

Use EF Core (2.2?)

EF Core seems to support proper orphan removal.

And while you won't be able to use EF Core 3.0 with .NET Framework, you can still try EF Core 2.2, as it should support orphan removal as well.

Yes, there are some differences between EF and EF Core, but for a project seemingly at its early phases, they are not insurmountable.

Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • Thank you for your detailed answer and your effort. I'm familiar with EF Core and as you said, never had this problem there. Circumstances have led me to use .NET Framework. I chose to not delete and mark. – D3ac0n Mar 12 '20 at 21:03