EF急切地加载导航属性问题

时间:2017-01-15 07:55:18

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

我正在使用EF6和Generic Repository模式。最近我在尝试一次性删除复合实体时遇到了问题。这是一个简化的场景:

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

为了删除带有相关子女的父实体,我正在做这样的事情:

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

首先,我通过ID找到父对象,然后将其传递给delete方法,将其状态更改为已删除。 context.SaveChanges()最终提交删除。

这很好用。 find方法只提取了Parent对象并且Delete工作,因为我在Children上启用了删除级联。

但是我在Child class中添加了另一个属性的那一刻:

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

public virtual Gender Gender { get; set; }

出于某种原因,EF开始在Parent.Find()方法中提取相关的Children。因此,我收到以下错误:

  

操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

即使在恢复更改(删除Gender属性)后,问题仍然存在。我无法理解这种奇怪的行为!!

我想要做的就是删除Parent对象和Children。 有一些解决方案,但没有一个真正符合我的目的:

  1. 将LazyLoading转为false - this.Configuration.LazyLoadingEnabled = false;这有效,但在我的实际应用程序中,我需要将此属性设置为true。
  2. 首先迭代所有孩子并删除它们然后删除父级。这似乎是一种解决方法,而且非常详细。
  3. 使用Remove()而不仅仅是将EntityState更改为Deleted。我需要跟踪审计更改,以便EntityState帮助那里。
  4. 有人可以解释为什么即使我不使用EF,也会加载相关的实体吗?

1 个答案:

答案 0 :(得分:2)

似乎问题与背景的生命周期有关。我正在使用工作单元并使用ninject将其注入我的服务层。

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

UnitOWork类实现了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;
    }

注释代码抛出并出错,而未注释的代码(使用块)有效。此调用之前的控制器方法加载CustomView实体(其结构与Parent类似,具有子项列表)。并且可以触发后续用户操作以删除该视图。

我认为这与未被处置的背景有关。也许这与Ninject或UnitOfWork有关,我还没有能够指出。 GetById()可能会从上下文缓存或其他东西中拉出整个实体。

但上述解决方法对我有用。只是把它放在那里,以便它可以帮助某人。