【问题标题】:Do I have to Load/Get an entity before SaveOrUpdate in Nhibernate?我必须在 Nhibernate 中的 SaveOrUpdate 之前加载/获取实体吗?
【发布时间】:2011-06-15 20:09:11
【问题描述】:

在我的 ASP.NET MVC 应用程序中,我已将域模型与视图模型分开。
我在视图模型对象中转换我的实体,这样我就可以只用所需的数据“提供”我的视图(为此我使用了 valueinjecter)。
在保存过程中,我的控制器取回 viewmodel 对象,将其转换为域模型实体并尝试使用 SaveOrUpdate 将其持久化。 我注意到,如果我尝试更新现有记录,Nhibernate 会将其视为新对象并强制插入,即使我的实体具有正确的 ID。
我之前没有加载(获取/加载)实体,因为我想避免再次重新映射所有字段。
有什么我做错了吗?实现这一目标的最佳方法是什么?

***** - 更新 - ***

我的控制器接收到一个用户 (ViewModel),对其进行验证并尝试通过服务层将其保存为实体:

public ActionResult Edit(Guid id, Models.User User)
{
...
var user = new Domain.User();
user.InjectFrom(User);
user.SetId(id);

user = this._SecurityService.SaveUser(user);

}

这是服务:

public Domain.User SaveUser(Domain.User User)
        {
            bool Result = false;

            if (this._ValidationEngine.IsValid(User))
            {
                using (_UnitOfWork)
                {
                    if (User.Code != Guid.Empty)
                    {
                        var user = this._UserRepository.Load(User.Code);
                        user.InjectFrom(User);
                        User = this._UserRepository.Update(user);
                    }
                    else {
                        User = this._UserRepository.Save(User);
                    }
                    Result = _UnitOfWork.Commit();
                }
            }
            return (User);
        }

我担心我必须多次转换我的视图模型/实体。现在,当我尝试保存新用户时,我收到以下消息:行已被另一个事务更新或删除(或未保存的值映射不正确)
可能这在某些方面与达林告诉我的有关。
有没有更好的方法来做我想做的事情?

更新

我注意到错误 “Row was updated or deleted...” 是因为我为我的映射定义了一个版本。我能理解的是我需要 Idversion 匹配我要更新的实体。
我想了解其他人如何使用 DDD + ASP.NET MVC + NHibernate 来做这些事情???

解决方案

我所有的实体都定义了一个版本(我忘了说,抱歉):

<version name="Version" type="System.Int32" unsaved-value="0">
   <column name="Version" not-null="true" />
</version>

正如 Ayende 解释的 here,Nhibernate 尝试使用如下查询更新我的实体:

UPDATE Table SET field1 = 'bla', Version = (y+1) WHERE id = x AND Version = y

y 应该是我的实体的版本。由于我没有使用版本填充我的实体,因此它会生成异常 StaleObjectException。
我在我的视图模型中添加了一个版本字段。我将它与 id 一起保存在视图的隐藏字段中。
我的控制器现在收到一个带有 Id 和版本的视图模型。 我在我的域实体中注入这些字段并让存储库保存它(我不重新加载我的实体):

User = this._UserRepository.Update(user);

如果我收到 StaleObjectException 异常,则表示其他人更新了数据库行,我会向用户提供一些反馈。

【问题讨论】:

  • 尝试加载实体并在重新映射后(我知道你不希望这样),试试这个并告诉我们它是否可以这样工作
  • 我想我的问题是版本。我有 在我的映射中定义。我想我必须找到更好的方法来做到这一点。

标签: asp.net-mvc nhibernate asp.net-mvc-2 viewmodel valueinjecter


【解决方案1】:

由于您已经有一个 ID,因此知道这是一个更新,请使用 session.Update 而不是 SaveOrUpdate

【讨论】:

  • 恐怕我不能这样做,因为如果我尝试保存我的实体,我会收到此消息:具有相同标识符值的不同对象已与会话关联:caedb8a4-abce-4c6f -b171-9e7200cc7a37,实体:BpReminders.Domain.User
  • 表示对象已经加载完毕。那你为什么不更新已经加载的对象呢?
  • 抱歉打错了。我现在正在做的是从存储库中加载一个实体并将其与从控制器获得的实体合并。当我尝试插入时,我仍然收到错误“行已被另一个事务更新或删除(或未保存的值映射不正确)”。我会用更多信息更新我原来的问题。
【解决方案2】:

要使SaveOrUpdate 按预期工作,您需要在&lt;id&gt; 映射上明确指定unsaved-value 属性。因此,例如,您将拥有:

<id unsaved-value="0" ...

然后在模型上发出 SaveOrUpdate 调用,该模型的 Id 属性分配给与 0 不同的值,它将转到 UPDATE 而不是 INSERT。所以要回答你的问题,不,你不需要在UPDATE 之前手动发出SELECTSaveOrUpdate 应该在幕后执行此操作。

【讨论】:

  • 谢谢大胆。如果我的 id 是 Guid 类型,我想我必须使用一个空的 Guid (00000000-0000-0000-0000-000000000000),对吧?
  • @vandalo,试试这个:unsaved-value="00000000-0000-0000-0000-000000000000"(未经测试)。
  • 再次感谢。无论如何,你会怎么做?我是否应该从数据库加载实体并用新实体填充每个字段,以防我尝试更新?有没有最佳实践?再次感谢您的帮助。
猜你喜欢
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 2015-11-24
  • 2011-01-05
  • 2021-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多