我创建了一个MVC 4并使用NHibernate来持久保存模型并使用流畅的nhibernate映射它。该实体具有“Name”属性,并以这种方式映射:
Map(x => x.Name).Not.Nullable().Length(100);
我创建了一个用于查看对象列表的表,让我编辑,查看对象详细信息并删除它们。 当我删除对象时,视图层将模型ID发回到相应的控制器,控制器通过存储库对象尝试删除对象。
[HttpPost]
public ActionResult DeleteElement(Element element)
{
Element deletedElement = repository.Delete(element);
TempData["message"] = string.Format("{0} has been deleted.",deletedElement.Name);
return RedirectToAction("Index");
}
表格视图的部分内容:
<td>
@using (Html.BeginForm("DeleteMenu", "Admin"))
{
@Html.Hidden("ID", item.ID)
<input type="submit" value="Delete"/>
}
</td>
因此视图仅将elemntID发回控制器。和元素对象只有它的ID。并且它的所有属性都是null。当尝试删除对象因为name属性为null时,存储库中的会话对象无法删除该对象,因为name字段为null。
错误讯息:
not-null属性引用null或瞬态值Element.Name
如果我只删除一个对象并拥有主键,为什么nHibernate会关心其他字段是否为空? 以及如何仅使用其ID删除对象?
public IQueryable<T> GetAll()
{
return session.Query<T>();
}
public IQueryable<T> Get(Expression<Func<T, bool>> predicate)
{
return GetAll().Where(predicate);
}
public void Delete(T entity)
{
session.Delete(entity);
}
答案 0 :(得分:2)
首先从NHibernate中检索实例,并使用 作为对象传递到Delete
。
发生的事情是你在NHibernate的范围之外创建一个对象(在MVC模型绑定中)。由于您只在HTML表单中指定了ID,因此当模型绑定器完成时,模型的属性都为null。
当你将这个对象传递给NHibernate时,它会注意到会话没有观察到它并尝试附加它,这将看到许多脏属性值(所有空值),因此尝试刷新对它的更改第一
您的行动应如下所示:
[HttpPost]
public ActionResult DeleteElement(int id)
{ var element = repository.Get(e => e.Id == id).First();
repository.Delete(element);
TempData["message"] = string.Format("{0} has been deleted.",deletedElement.Name);
return RedirectToAction("Index");
}
我建议在您的存储库中添加一个由NHibernate包装Load
的方法。 Load
非常有用,因为它使用您指定的ID创建对象的观察实例,但在访问ID以外的属性之前,实际上并不会访问数据库。它对于您知道对象存在但只需要指针(例如删除实体或在关系中添加它)的情况非常有用。
更新了存储库:
public IQueryable<T> GetAll()
{
return session.Query<T>();
}
public IQueryable<T> Get(Expression<Func<T, bool>> predicate)
{
return GetAll().Where(predicate);
}
public T DeferredGet(int id) // I like to call it DeferredGet, you can call it Load or whatever you want
{
return session.Load<T>(id);
}
public void Delete(T entity)
{
session.Delete(entity);
}
然后更新了您的操作:
[HttpPost]
public ActionResult DeleteElement(int id)
{ var element = repository.DeferredGet(id); // will not actually hit the database, saving you a query.
repository.Delete(element); // deletes the element normally.
TempData["message"] = string.Format("{0} has been deleted.",deletedElement.Name);
return RedirectToAction("Index");
}