EF eagerly loading Navigation Properties issue

asp.net-mvc c# eager-loading entity-framework entity-framework-6

Question

I am using EF6 with Generic Repository pattern. Recently I experienced a problem trying to delete a composite entity in a single go. Here is a simplified scenario:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("Parent")]
    public int ParentId { get; set; }

    public virtual Parent Parent { get; set; }
}

For deleting the Parent entity with related Children I am doing something like this:

public virtual T GetById(int id)
{
    return this.DBSet.Find(id);
}
public virtual void Delete(T entity)
{
    DbEntityEntry entry = this.Context.Entry(entity);

    if (entry.State != EntityState.Deleted)
    {
        entry.State = EntityState.Deleted;
    }
    else
    {
        this.DBSet.Attach(entity);
        this.DBSet.Remove(entity);
    }
}

First I find the parent object by ID and then pass it to the delete method to change it's state to deleted. The context.SaveChanges() finally commits the delete.

This worked fine. The find method only pulled up Parent object and Delete worked since I have a cascade on delete enabled on Children.

But the moment I added another property in Child class:

[ForeignKey("Gender")]
public int GenderId { get; set; }

public virtual Gender Gender { get; set; }

For some reason EF started pulling related Children on the Parent.Find() method. Because of this I get the following error:

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.

Even after reverting the changes (removing the Gender property) the problem still exists. I am not able to understand this weird behavior!!

All I want to do is Delete the Parent object along with the Children. There are some solutions around it but none really serves my purpose:

  1. Turn LazyLoading to false - this.Configuration.LazyLoadingEnabled = false; This works but in my real application I need this property to true.
  2. Iterate all children first and Delete them and then delete the Parent. This seems at best a workaround and is very verbose.
  3. Use Remove() rather than just changing the EntityState to Deleted. I need to track Changes for Auditing so EntityState helps there.

Can someone explain why EF is loading related Entities even when I am not using them?

1
4
1/18/2017 12:22:45 PM

Popular Answer

It seems that the problem was related to the life-cycle of context. I am using Unit Of Work and injecting it into my service layers using ninject.

kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();

The UnitOWork class implements IDisposable.

public bool DeleteView(int viewId)
    {
        // This is a workaround. It seems ninject is not disposing the context. 
        // Because of that all the info (navigation properties) of a newly created view is presisted in the context.
        // Hence you get a referential key error when you try to delete a composite object.
        using (var context = new ApplicationDbContext())
        {
            var repo = new GenericRepository<CustomView>(context);
            var view = repo.GetById(viewId);
            repo.Delete(view);
            context.SaveChanges();
        }
        //var model = _unitOfWork.CustomViews.GetById(viewId);
        //_unitOfWork.CustomViews.Delete(model);
        //_unitOfWork.Save();

        return true;
    }

The commented code throws and error, while the un-commented one (using block) works. A controller method before this call loads the CustomView entity (which is of a similar structure as Parent with a list of children). And a subsequent user action can be triggered to delete that view.

I believe this has something to do with the context not being disposed. Maybe this has something to do with Ninject or UnitOfWork, I haven't been able to pin-point yet. The GetById() might be pulling the whole entity from context cache or something.

But the above workaround works for me. Just putting it out there so that it might help somebody.

2
1/19/2017 5:46:03 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow