NHibernate — пометка определенных свойств как «грязных»

Я работаю над проектом NHibernate, и у меня есть вопрос об обновлении временных сущностей.

В основном рабочий процесс выглядит следующим образом:

  1. Создайте DTO (проекцию) и отправьте клиенту по сети. Это имеет небольшое подмножество свойств сущности.
  2. Клиент отправляет обратно измененный DTO
  3. Сопоставьте свойства DTO обратно с соответствующей сущностью, чтобы оператор UPDATE мог быть сгенерирован и выполнен NH.
  4. Сохраните объект

Пункт 4, где у меня есть проблема. В настоящее время я могу добиться этого обновления с помощью метода session.Merge(), однако перед обновлением он должен сначала загрузить объект из базы данных (предполагается, что нет 2LC). Таким образом, запускаются как оператор выбора, так и оператор обновления.

Что я хотел бы сделать, так это создать временный экземпляр объекта, сопоставить новые значения из DTO, а затем заставить NH сгенерировать оператор SQL, используя только те свойства, которые я изменил. В дополнительном выборе не должно быть необходимости, поскольку у меня уже есть идентификатор объекта и значения, необходимые для предложения SET. Возможно ли это в НГ?

В настоящее время при использовании session.Update() все свойства будут включены в оператор обновления, и возникнет исключение из-за неинициализированных свойств, которые не являются частью DTO.

По сути, мне нужен способ указать, какие свойства объекта являются грязными, чтобы только они были включены в обновление.

== ИЗМЕНИТЬ ==

Например...

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();
}

Я надеялся сгенерировать оператор SQL, похожий на «ОБНОВЛЕНИЕ Persons SET Firstname = «Bob» WHERE PersonID = 1». Вместо этого я получаю исключение DateTime вне диапазона из-за того, что BirthDate не инициализирован. Для него не требуется BirthDate, поскольку он не требуется для оператора SQL. Может это не возможно?

== /РЕДАКТИРОВАТЬ ==

Заранее спасибо, Джон


person John    schedule 19.11.2010    source источник


Ответы (1)


Динамическое обновление — это то, что вам нужно. В вашем файле сопоставления (hbm.xml):

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

Помните о потенциальных проблемах, которые это может вызвать. Допустим, у вас есть некоторая логика домена, которая говорит, что либо Имя, либо Ник не должны быть нулевыми. (Это полностью выдумка.) Два человека обновляют информацию о Джоне «Джонбое» Джонсоне одновременно. Один удаляет свое Имя. Поскольку динамическое обновление истинно, оператор обновления просто обнуляет Джона, и запись теперь «Джонбой» Джонсон. Другое одновременное обновление удаляет его псевдоним. Целью является Джон Джонбой. Но в базу данных отправляется только обнуление псевдонима. Теперь у вас есть запись без имени или псевдонима. Если бы динамическое обновление было ложным, второе обновление установило бы его на Джона Джонбоя. Возможно, это не проблема в вашей ситуации, но установка dynamic-update="true" имеет последствия, и вам следует подумать о последствиях.

ОБНОВЛЕНИЕ: Спасибо за код. Это помогло. Основная проблема в том, что у NHibernate недостаточно информации. Когда вы говорите session.Update(p), NHibernate должен связать отключенный объект с текущим сеансом. Он имеет ПК не по умолчанию. Итак, NHibernate знает, что это обновление, а не вставка. Когда вы говорите session.Update(p), NHibernate считает весь объект грязным и отправляет его в базу данных. (Если вы используете session.Merge(obj), NHibernate выбирает сущность из базы данных и объединяет с ней obj.) Это не то, что вы на самом деле имеете в виду. Вы хотите связать свой объект с текущим сеансом, но пометить его как чистый. API несколько не интуитивно понятен. Вы используете session.Lock(obj, LockMode.None), как показано ниже.

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" указано в моем сопоставлении.)

В результате получается следующий SQL:

UPDATE Person
SET    Firstname = 'Bob' /* @p0_0 */
WHERE  PersonId = 1 /* @p1_0 */
person James Kovacs    schedule 19.11.2010
comment
Это также влияет на производительность, я считаю, что использование статических обновлений (в целом) более производительно из-за того, как nhibernate подготавливает операторы обновления при сборке sessionfactory. - person DanP; 19.11.2010
comment
Привет, Джеймс, спасибо за ответ. У меня есть набор DynamicUpdate (текущее сопоставление) для объекта. Возможно, пример проиллюстрирует проблему более ясно. См. отредактированный раздел выше. - person John; 22.11.2010
comment
Я должен согласиться, что в этом случае API не интуитивно понятен;) Я тестировал использование session.Lock() в своем «реальном» приложении, и он возвращает именно тот SQL, который мне нужен. Большое спасибо за вашу помощь с этим Джеймсом. - person John; 23.11.2010