c# - 如何处理更新实体。 NHibernate + ASP.NET MVC

标签 c# asp.net-mvc nhibernate exception orm

我无法更新之前创建的实体。我收到一个 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。我该如何处理它,MergeReattach

最佳答案

根据您的代码模式,您可能会遇到两种情况。

  1. 您可以使用 ISession.Get() 从数据库中检索对象,然后可以对检索到的对象进行更改/更新。要使此更改生效,您需要做的就是刷新 session 或提交事务,因为 Nhibernate 会自动为您跟踪所有更改。

  2. 您有一个临时实例,一个与上下文中的 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/

相关文章:

c# - 是否应该在每个使用 MVC 的 View 中使用 ViewModel?

NHibernate 多个数据库提供者

c# - 速记访问器和修改器

c# - 在 C# 中,我如何评估一个类的任何成员是否包含 switch case 或 if else 构造?

c# - 在 C# 中将 DateTime 日期和字符串时间组合成单个 DateTime

asp.net-mvc - MvcIntegrationTestFramework 或针对 ASP.NET MVC 3 更新的替代方案

c# - C++中的委托(delegate)

c# - 用于存储所有全局数据的 asp.net session 变量

nhibernate - NHibernate。按功能排序

NHibernate QueryOver Multiple Order By with Aliases