NHibernate-将特定属性标记为“脏" [英] NHibernate - flagging specific properties as 'dirty'

查看:95
本文介绍了NHibernate-将特定属性标记为“脏"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从事NHibernate项目,并且对更新瞬态实体有疑问.

I am working on an NHibernate project and have a question regarding updating transient entities.

工作流程基本上如下:

  1. 创建一个DTO(投影)并通过电线发送给客户端.它具有实体的一小部分属性.
  2. 客户端发回更改后的DTO
  3. 将DTO属性映射回适当的实体,以便NH可以生成并执行UPDATE语句.
  4. 保存实体

第4点是我遇到的问题.目前,我可以使用session.Merge()方法来完成此更新,但是在更新之前,它必须首先从db中加载实体(假定为2LC).因此,同时触发了select和update语句.

Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.

我想做的是创建实体的瞬态实例,从DTO映射新值,然后让NH仅使用我更改的属性生成SQL语句.我已经有了实体ID和SET子句所需的值,因此不需要额外的选择.在NH中可能吗?

What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?

当前使用session.Update(),所有属性都将包含在update语句中,并且由于未初始化的属性(不属于DTO的一部分)而引发了异常.

Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.

基本上,我需要一种方法来指定哪些实体属性是脏的,以便仅将这些属性包括在更新中.

Essentially I need a way to specify which entity properties are dirty so only these are included in the update.

==编辑==

例如...

public class Person
{
    public virtual int PersonId { get; set; }
    public virtual string Firstname { get; set; }   
    public virtual string Nickname { get; set; }    
    public virtual string Surname { get; set; } 
    public virtual DateTime BirthDate { get; set; }     
}

还有测试用例.

// Create the transient entity
Person p = new Person()
p.id = 1;

using (ISession session = factory.OpenSession())
{
    session.Update(p);

    // Update the entity – now attached to session    
    p.Firstname = "Bob";

    session.Flush();
}

我希望生成一条类似于"UPDATE Persons SET Firstname ='Bob'WHERE PersonID = 1'"的SQL语句.相反,由于未初始化BirthDate,我得到了DateTime超出范围的异常.它不需要BirthDate,因为SQL语句不需要.也许这不可能吗?

I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?

==/EDIT ==

预先感谢, 约翰

推荐答案

动态更新是您所需要的.在您的映射文件(hbm.xml)中:

Dynamic-update is what you're looking for. In your mapping file (hbm.xml):

<class name="Foo" dynamic-update="true">
   <!-- remainder of your class map -->

请注意这可能引起的潜在问题.假设您有一些域逻辑说FirstName或Nickname不能为null. (完全弥补了这一点.)两个人同时更新Jon"Jonboy" Jonson.一个人删除他的名字.因为dynamic-update是正确的,所以update语句只会使Jon无效,并且记录现在是"Jonboy" Jonson.其他同时更新将删除他的昵称.目的是乔恩·琼博(Jon Jonboy).但是,只有昵称的空值被发送到数据库.您现在有了一个没有名字或昵称的记录.如果dynamic-update为false,则第二次更新会将其设置为Jon Jonboy.也许这不是您遇到的问题,但是设置dynamic-update ="true"会带来后果,您应该仔细考虑其含义.

Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.

更新:谢谢您的代码.那有帮助.基本问题是NHibernate没有足够的信息.当您说session.Update(p)时,NHibernate必须将一个断开连接的实体与当前会话相关联.它具有非默认的PK.因此,NHibernate知道这是更新而不是插入.当您说session.Update(p)时,NHibernate会将整个实体视为脏实体,并将其发送到数据库. (如果使用session.Merge(obj),则NHibernate从数据库中选择实体,然后将obj与之合并.)这并不是您的意思.您想将您的对象与当前会话相关联,但将其标记为干净.该API有点不直观.您可以按如下方式使用session.Lock(obj,LockMode.None).

UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.

using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
    var p = new Person {PersonId = 1};
    session.Lock(p, LockMode.None); // <-- This is the secret sauce!
    p.Firstname = "Bob";
    // No need to call session.Update(p) since p is already associated with the session.
    tx.Commit();
}

(在我的映射中指定了N.B. dynamic-update ="true".)

(N.B. dynamic-update="true" is specified in my mapping.)

这将导致以下SQL:

UPDATE Person
SET    Firstname = 'Bob' /* @p0_0 */
WHERE  PersonId = 1 /* @p1_0 */

这篇关于NHibernate-将特定属性标记为“脏"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆