我正在使用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。 有一些解决方案,但没有一个真正符合我的目的:
有人可以解释为什么即使我不使用EF,也会加载相关的实体吗?
答案 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()可能会从上下文缓存或其他东西中拉出整个实体。
但上述解决方法对我有用。只是把它放在那里,以便它可以帮助某人。