我无法更新之前创建的实体。我收到一个 StaleObjectException
异常消息:
行已被另一个事务更新或删除(或未保存值映射不正确):[Project.DomainLayer.Entities.Employee#00000000-0000-0000-0000-000000000000]
我不会与任何人分享更新过程。有什么问题?
数据访问/DI
public class DataAccessModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
this.Bind<ISessionFactory>()
.ToMethod(c => new Configuration().Configure().BuildSessionFactory())
.InSingletonScope();
this.Bind<ISession>()
.ToMethod(ctx => ctx.Kernel.TryGet<ISessionFactory>().OpenSession())
.InRequestScope();
this.Bind(typeof(IRepository<>)).To(typeof(Repository<>))
.InRequestScope();
}
}
数据访问/映射
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Project.DomainLayer" namespace="Project.DomainLayer.Entities">
<class name="Employee" optimistic-lock="version">
<id name="ID" column="EmployeeID" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<version name="Version" type="Int32" column="Version" />
<!-- properties -->
<property name="EmployeeNumber" />
<!-- ... -->
<property name="PassportRegistredOn" not-null="true" />
<!-- sets -->
<set name="AttachedInformation" cascade="all">
<key column="EmployeeID" />
<element column="Attachment" />
</set>
<set name="TravelVouchers" cascade="all">
<key column="EmployeeID" />
<one-to-many class="TravelVoucher" />
</set>
</class>
</hibernate-mapping>
数据访问/存储库
public class Repository<T> : IRepository<T> where T : AbstractEntity<T>, IAggregateRoot
{
private ISession session;
public Repository(ISession session)
{
this.session = session;
}
// other methods are omitted
public void Update(T entity)
{
using(var transaction = this.session.BeginTransaction())
{
this.session.Update(entity);
transaction.Commit();
}
}
public void Update(Guid id)
{
using(var transaction = this.session.BeginTransaction())
{
this.session.Update(this.session.Load<T>(id));
transaction.Commit();
}
}
}
Controller 内部
public class EmployeeController : Controller
{
private IRepository<Employee> repository;
public EmployeeController(IRepository<Employee> repository)
{
this.repository = repository;
}
public ActionResult Edit(Guid id)
{
var e = repository.Load(id);
return View(e);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Employee employee)
{
if(ModelState.IsValid)
{
repository.Update(employee);
return RedirectToAction("Deatils", "Employee", new { id = employee.ID });
}
else
{
return View(employee);
}
}
}
如何更新我的实体? 谢谢!
编辑
所以我将 unsaved-value="{Guid.Empty goes here}"
添加到我的标记中。此外,我尝试做下一件事:
public void Update(T entity)
{
using(var transaction = this.session.BeginTransaction())
{
try
{
this.session.Update(entity);
transaction.Commit();
}
catch(StaleObjectStateException ex)
{
try
{
session.Merge(entity);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
这给了我同样的效果。我的意思是 transaction.Commit();
在 Merge
之后给出了同样的异常。
另外我想知道我是否应该使用隐藏输入在 Edit
View 中公开实体 ID
?
编辑
所以实体真的分离了。当它传递给 Controller 时,ID
等于 Guid.Empty
。我该如何处理它,Merge
或 Reattach
?
最佳答案
根据您的代码模式,您可能会遇到两种情况。
您可以使用
ISession.Get()
从数据库中检索对象,然后可以对检索到的对象进行更改/更新。要使此更改生效,您需要做的就是刷新 session 或提交事务,因为 Nhibernate 会自动为您跟踪所有更改。您有一个临时实例,一个与上下文中的
ISession
无关的对象,您希望从中更新。在这种情况下,根据我的经验,最佳做法是ISession.Get()
对象并对您刚刚检索的对象进行相应的更改。 (通常你的 View 模型也不同于你的域模型,不要混合两者)这种模式如下所示。它一直有效。确保您还使用了ISession.SaveOrUpdate()
。
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Employee employee)
{
if(ModelState.IsValid)
{
var persistentEmployee = repository.Get(employee.Id);
if( persistentEmployee == null){
throw new Exception(String.Format("Employee with Id: {0} does not exist.", employee.Id));
}
persistentEmployee.Name = employee.Name;
persistentEmployee.PhoneNumber = employee.PhoneNumber;
//and so on
repository.Update(persistentEmployee);
return RedirectToAction("Deatils", "Employee", new { id = employee.ID });
}
else
{
return View(employee);
}
}
另外,请注意您的 Controller 可能是根据每个请求实例化的,因此,您的 ISession
的生命周期不会跨越对 Controller 中不同方法的多次调用。换句话说,每个方法几乎总是在新的 ISession
(工作单元)的上下文中工作。
关于c# - 如何处理更新实体。 NHibernate + ASP.NET MVC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9701433/