NHibernate - пометка определенных свойств как «грязных» - PullRequest
1 голос
/ 19 ноября 2010

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

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

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

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

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

В настоящее время с помощью 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, похожий на UPDATE Persons SET Firstname = "Bob" WHERE PersonID = 1 '. Вместо этого я получаю исключение DateTime вне диапазона из-за того, что BirthDate не инициализируется. Для него не требуется дата рождения, так как он не требуется для оператора SQL. Может быть, это невозможно?

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

Заранее спасибо, John

1 Ответ

4 голосов
/ 19 ноября 2010

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

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

Помните о потенциальных проблемах, которые это может вызвать.Допустим, у вас есть некоторая логика домена, которая говорит, что FirstName или Nickname не должны быть нулевыми.(Полностью придумываю это.) Два человека обновляют Джона "Jonboy" Джонсона одновременно.Один удаляет свое имя.Поскольку динамическое обновление имеет значение true, оператор update просто обнуляет Jon, а запись теперь называется Jonboy Jonson.Другое одновременное обновление удаляет его никнейм.Цель - Джон Джонбой.Но только нулевой никнейм отправляется в базу данных.Теперь у вас есть запись без имени или псевдонима.Если бы динамическое обновление было ложным, второе обновление установило бы его на Jon Jonboy.Возможно, это не проблема в вашей ситуации, но установка 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();
}

(NB dynamic-update = "true" указано в моем отображении.)

Это приводит кследующий SQL:

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