C# EF急切地加载导航属性问题
我将EF6与通用存储库模式一起使用。最近,我在尝试一次性删除复合实体时遇到了一个问题。下面是一个简化的场景:C# EF急切地加载导航属性问题,c#,asp.net-mvc,entity-framework,entity-framework-6,eager-loading,C#,Asp.net Mvc,Entity Framework,Entity Framework 6,Eager Loading,我将EF6与通用存储库模式一起使用。最近,我在尝试一次性删除复合实体时遇到了一个问题。下面是一个简化的场景: 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
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; }
}
首先,我通过ID找到父对象,然后将其传递给delete方法,将其状态更改为deleted。context.SaveChanges()最终提交删除操作
这很有效。find方法只拉起父对象,Delete工作,因为我在子对象上启用了cascadeondelete
但当我在子类中添加另一个属性时:
[ForeignKey("Gender")]
public int GenderId { get; set; }
public virtual Gender Gender { get; set; }
出于某种原因,EF开始在Parent.Find()方法上提取相关的子级。因此,我得到以下错误:
操作失败:无法更改关系,因为一个或多个外键属性不可为null。对关系进行更改时,相关外键属性设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象
即使在恢复更改(删除性别属性)后,问题仍然存在。我无法理解这种奇怪的行为
我只想删除父对象和子对象。
有一些解决方案,但没有一个真正符合我的目的:
有人能解释为什么EF在我不使用相关实体的情况下仍在加载它们吗?似乎问题与上下文的生命周期有关。我正在使用工作单元,并使用ninject将其注入到我的服务层中
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind().To().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;
}
public bool DeleteView(int-viewId)
{
//这是一个解决方法。ninject似乎没有处理上下文。
//因此,新创建的视图的所有信息(导航属性)都显示在上下文中。
//因此,在尝试删除复合对象时,会出现引用键错误。
使用(var context=new ApplicationDbContext())
{
var repo=新的一般报告(上下文);
var view=repo.GetById(viewId);
回购。删除(查看);
SaveChanges();
}
//var model=_unitOfWork.CustomViews.GetById(viewId);
//_unitOfWork.CustomView.Delete(模型);
//_unitOfWork.Save();
返回true;
}
注释后的代码抛出错误,而未注释的代码(使用block)有效。此调用之前的控制器方法加载CustomView实体(该实体的结构类似于父实体,具有子实体列表)。并且可以触发后续的用户操作来删除该视图
我认为这与未处理的上下文有关。也许这和Ninject或UnitOfWork有关,我还没能确定这一点。GetById()可能正在从上下文缓存或其他地方提取整个实体
但是上面的解决方法对我很有效。只是把它放在那里,这样可以帮助别人。当你恢复时,你也恢复了数据库吗?你的问题可能和DB有关,是的。我删除了数据库并重新创建了它。是否需要先加载实体?您可以通过创建一个新实例、设置id、将其附加到上下文,然后调用
delete
来删除它。恢复更改的事实让我产生怀疑。您确定这不是由于调试器检查子项从而加载行而导致的吗?@Rob非常感谢。我觉得自己像个白痴。实际上是调试器在示例应用程序中加载属性,但在我的实际应用程序中,问题不同。你的评论给了我一个新的调查方向。谢谢在我的真实应用程序中,请求之间的上下文似乎没有被破坏。在以前的请求中,有问题的实体已在上下文中完全加载,而删除请求似乎实际上是从缓存中提取的。我将添加一个详细解释的答案。使用Autofac-针对生命周期、范围、处理和工作单元(根据请求)的更成熟、更容易理解的解决方案。换句话说,整个帖子是不正确的,与EF没有任何共同点,但模式、注射等。我认为一旦赏金到期,这个问题应该删除,因为它只是误导,我看不出它如何能帮助别人。
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;
}