在 Nhibernate 中,我是否必须在 SaveOrUpdate 之前加载/获取实体?
在我的 ASP.NET MVC 应用程序中,我将域模型与视图模型分开。
我在视图模型对象中转换我的实体,以便我可以仅使用所需的数据“提供”我的视图(为此目的我使用了 valueinjecter)。
在保存过程中,我的控制器取回视图模型对象,将其转换为域模型实体,并尝试使用 SaveOrUpdate 保存它。 我注意到,如果我尝试更新现有记录,Nhibernate 会将其视为新对象并强制执行 INSERT,即使我的实体具有正确的 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);
}
我担心以下事实:我必须多次转换我的视图模型/实体。现在,当我尝试保存新用户时,我收到此消息:行已被另一个事务更新或删除(或未保存值映射不正确)
也许这在某些方面与达林告诉我的内容有关。
有没有更好的方法来做我想做的事情?
更新
我注意到错误“行已更新或删除...”是由于我为映射定义了版本而导致的。我能理解的是我需要有与我想要更新的实体匹配的 Id 和 版本。
我想了解其他人如何使用 DDD + ASP.NET MVC + NHibernate 做这些事情???
解决方案
我的所有实体都定义了一个版本(抱歉,我忘了说):
<version name="Version" type="System.Int32" unsaved-value="0">
<column name="Version" not-null="true" />
</version>
正如 Ayende 所解释的 此处,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 异常,则意味着其他人已经更新了数据库行,而我'将为用户提供一些反馈。
in my ASP.NET MVC application I've separated the domain model from the view model.
I transform my entity in a viewmodel object so I can "feed" my views with only the data needed (I used valueinjecter for this purpose).
During the save process my controller gets back the viewmodel object, transforms it into a domain model entity and try to persist it with SaveOrUpdate.
I've noticed that if I try to update an existing record, Nhibernate considers it as a new object and forces an INSERT, even if my entity has a proper ID.
I haven't loaded (Get/Load) the entity before cause I would like to avoid to re-map all the fields again.
Is there anything I am doing wrong? What's the best way to achieve that?
***** - UPDATE - ***
My controller receives a User (ViewModel), validates it and tries to save it through a service layer as an entity:
public ActionResult Edit(Guid id, Models.User User)
{
...
var user = new Domain.User();
user.InjectFrom(User);
user.SetId(id);
user = this._SecurityService.SaveUser(user);
}
This is the service:
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);
}
I've got a concern about the fact that I have to transform my viewmodel/entity so many times. Now when I try to save a new User I got this message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
Probably this is in some ways related to what Darin was telling me.
Are there any better ways to do what I am trying to do?
UPDATE
I've noticed that the error "Row was updated or deleted..." is caused by the fact that I have a version defined for my mappings. What I can understand is I need to have the Id and the version matching the entity I want to update.
I would like to understand how other people do these things with DDD + ASP.NET MVC + NHibernate ???
SOLUTION
All my entities have a version defined (I forgot to say, sorry):
<version name="Version" type="System.Int32" unsaved-value="0">
<column name="Version" not-null="true" />
</version>
As Ayende explained here, Nhibernate tries to update my entity with a query like this:
UPDATE Table SET field1 = 'bla', Version = (y+1) WHERE id = x AND Version = y
where y should be the version of my entity. Since I wasn't populating my entity with a version, it would generate an exception StaleObjectException.
I've added a version field to my viewmodel. I save it in a hidden field of my view together with the id.
My controller now receives a viewmodel with an Id and a version.
I inject these fields in my domain entity and let the repository save it (I don't reload my entity):
User = this._UserRepository.Update(user);
If I get a StaleObjectException exception it means that someone else has updated the DB row and I'll provide the user with some feedback.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为了使
SaveOrUpdate
按预期工作,您需要在
映射上显式指定unsaved-value
属性。例如,您将:然后对模型发出
SaveOrUpdate
调用,该模型的 Id 属性分配的值不同于0
并且它将更新
而不是插入
。因此,要回答您的问题,不,您不需要在UPDATE
之前手动发出SELECT
。SaveOrUpdate
应该在幕后执行此操作。For
SaveOrUpdate
to work as expected you need to explicitly specify theunsaved-value
attribute on your<id>
mapping. So for example you would have:and then issue a
SaveOrUpdate
call on a model which has the Id property assigned to a value different than0
and it is going toUPDATE
instead ofINSERT
. So to answer your question, no, you don't need to manually issue aSELECT
beforeUPDATE
.SaveOrUpdate
should do this behind the scenes.由于您已经有了 Id,因此知道这是更新,因此请使用
session.Update
而不是SaveOrUpdate
。Since you already have an Id and, therefore, know this is an update, use
session.Update
instead ofSaveOrUpdate
.