C# 如何处理更新实体。NHibernate+;ASP.NETMVC

C# 如何处理更新实体。NHibernate+;ASP.NETMVC,c#,asp.net-mvc,nhibernate,exception,orm,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

我无法更新以前创建的实体。我收到一个
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();
    }
}
这给了我同样的效果。。我的意思是
transaction.Commit()Merge
之后的code>给出了相同的异常

我还想知道是否应该使用隐藏输入,在
Edit
视图中公开实体
ID

编辑

所以实体是分离的。当它传递给控制器时,
ID
等于
Guid.Empty
。如何处理它,
合并
重新附加

缺少“未保存的值”。因此NH认为Guid.Empty是一个有效的id

<id name="ID" column="EmployeeID" unsaved-value="0000000-0000-0000-0000-000000000000">

如果要更新某些实体的字段,则不需要使用session.update(), 在关闭事务之前使用session.Flush()


session.Update()->使用给定临时实例的标识符更新持久实例。

我相信您的Employee对象已成为NHibernate所称的编辑操作方法的GET和POST之间的“分离”。有关更多详细信息和一些解决方案,请参阅有关此主题的。事实上,该链接描述了您似乎正在使用的确切的GET-POST场景

您可能需要重新附加Employee对象和/或按照Firo的建议指定“未保存的值”,以便NHibernate知道ID为Guid的员工。Empty尚未保留到数据库中。否则,正如Firo所建议的,NHibernate将Guid.Empty视为有效ID,并认为该对象已保存到数据库中,但检索该对象的会话已被丢弃(因此,该对象变得“分离”)


希望这有帮助。

您的逻辑不太好,因为您使用的域模型(如Employee)是ViewModel。最佳实践是使用CreateEmployeeeviewModel和EditEmployeeViewModel,并分离域逻辑和视图模型逻辑。 例如:

public class Employee 
 {
        public virtual int Id { get; set; }

        public virtual string FirstName { get; set; }

        public virtual string LastName { get; set; }

        public virtual string MiddleName { get; set; }
 }

public class CreateEmployeeViewModel 
 {
        public virtual string FirstName { get; set; }

        public virtual string LastName { get; set; }

        public virtual string MiddleName { get; set; }
 }

public class EditEmployeeViewModel : CreateEmployeeViewModel  
 {
        public virtual int Id { get; set; }
 }
要将Employee转换为ViewModel,我更喜欢使用

因此,控制器动作看起来像:

[HttpGet]
    public virtual ActionResult Edit(int id)
    {
        Employee entity = GetEntityById(id);
        EmployeeEditViewModel model = new EmployeeEditViewModel();

        Mapper.Map(source, destination);            

        return View("Edit", model);
    }

    [HttpPost]
    public virtual ActionResult Edit(EmployeeEditViewModel model)
    { 
        if (ModelState.IsValid)
        {
            Employee entity = GetEntityById(model.Id);

            entity = Mapper.Map(model, entity);               
            EntitiesRepository.Save(entity);

            return GetIndexViewActionFromEdit(model);
        }           

        return View("Edit", model);
    }
在这种情况下,NHibernate知道您更新了Employee,并且您无法删除视图中不存在的某些属性

你问

我还想知道是否应该使用隐藏输入在编辑视图中公开实体ID

是的,你应该。您还应该在隐藏输入中公开版本,因为它的业务是帮助防止对同一实体进行并发编辑。StaleObjectException提示您已启用版本控制,在这种情况下,仅当您发送回的版本值(Int32)与数据库中的版本值相同时,更新才会起作用

您总是可以通过重新加载实体并映射它来绕过它,确保版本值可能匹配,但这似乎破坏了它的用途

嗯,我把实体ID和版本放在一个隐藏的输入中,在回发时,重新加载实体并映射数据。这样,就像上面建议的那样,您就不必携带视图中不需要的属性。您还可以在控制器级别处理过时问题,并添加验证错误,而不是让NHibernate告诉您对象过时


概述处理实体简单编辑的标准过程。他的答案唯一的问题是它没有处理Version属性。总之,数据库不应该进行版本控制,或者版本属性应该很重要。

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

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

  • 您有一个临时实例,它是一个和上下文中的
    ISession
    不关联的对象,您希望从中进行更新。在这种情况下,根据我的经验,最佳做法是
    ISession.Get()
    对象,并对刚检索到的对象进行相应的更改。(通常视图模型也不同于域模型,不要两者混合)此模式如下所示。它一直在工作。确保还使用了ISession.SaveOrUpdate()


  • 另外,请注意,您的控制器可能是基于每个请求进行实例化的,因此,
    ISession
    的生命周期不会跨越对控制器中不同方法的多次调用。换句话说,每种方法几乎总是在一个新的
    会话
    (工作单元)的上下文中工作。

    如果您是我们中的一员,这里的答案没有帮助,请尝试查找您的实体中的“ID”发送的内容

    我也有同样的问题,但最后,我看到我正在将ID更改为另一个数字(在NHibernate中,ID将自动生成,如果您这样设置的话!

    所以,在最底层,检查您发送的数据的结构和值是否与您期望发送的数据相匹配


    希望我能帮助任何人!:)

    您可以发布一些生成的SQL吗?另外,您是否验证了员工实体在回邮时有id?确定。那么,在编辑/更新时,将
    Automapper
    与相应的视图模型一起使用不会导致实体分离吗?使用另一种3d软件使应用程序更加复杂。我想知道的是如何在不分离实体的情况下使用nhibernate进行编辑/更新
    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;
                }
            }
    
        }
    }
    
    <id name="ID" column="EmployeeID" unsaved-value="0000000-0000-0000-0000-000000000000">
    
    public class Employee 
     {
            public virtual int Id { get; set; }
    
            public virtual string FirstName { get; set; }
    
            public virtual string LastName { get; set; }
    
            public virtual string MiddleName { get; set; }
     }
    
    public class CreateEmployeeViewModel 
     {
            public virtual string FirstName { get; set; }
    
            public virtual string LastName { get; set; }
    
            public virtual string MiddleName { get; set; }
     }
    
    public class EditEmployeeViewModel : CreateEmployeeViewModel  
     {
            public virtual int Id { get; set; }
     }
    
    [HttpGet]
        public virtual ActionResult Edit(int id)
        {
            Employee entity = GetEntityById(id);
            EmployeeEditViewModel model = new EmployeeEditViewModel();
    
            Mapper.Map(source, destination);            
    
            return View("Edit", model);
        }
    
        [HttpPost]
        public virtual ActionResult Edit(EmployeeEditViewModel model)
        { 
            if (ModelState.IsValid)
            {
                Employee entity = GetEntityById(model.Id);
    
                entity = Mapper.Map(model, entity);               
                EntitiesRepository.Save(entity);
    
                return GetIndexViewActionFromEdit(model);
            }           
    
            return View("Edit", model);
        }
    
    [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);
        }
    }