Как я могу обновить строку и автоматически вставить новую в NHibernate с помощью одного вызова Save?

Скажем, у меня есть база данных SCD типа II, которая в основном только добавляется. Я использую NHibernate для сохранения объектов в моей базе данных. У меня есть такой объект:

Pony
|- int Id
|- Guid EntityId
|- string PonyName
|- string PonyColor
|- int RevisionValidFrom
|- int RevisionValidTo

Вот типичный сценарий:

Pony myLittlePony = myStable.GetLatestPonyByGuid("0f1ac08a-3328-43db-b278-77c272e4fea3");
myLittlePony.PonyColor = "Fish";
myNHSession.Save(myLittlePony);

Я хочу иметь возможность вызвать Session.Save(myLittlePony) и заставить NHibernate ОБНОВИТЬ RevisionValidTo старого объекта до того, что я укажу, а затем ВСТАВИТЬ измененный Pony как новую строку с новым Id, как если бы это был новый объект, сохраняемый в БД.


person snicker    schedule 10.03.2010    source источник


Ответы (2)


Это можно сделать с помощью пользовательского SQL для обновления.

person Lachlan Roche    schedule 11.03.2010

В итоге я создал свой собственный SaveOrUpdateEventListener и зарегистрировал его в конфигурации для NHibernate. Затем я запускаю необработанную команду SQL.

Для конфигурации NHibernate:

var listener = new PonySaveOrUpdateEventListener();
config.SetListener(NHibernate.Event.ListenerType.SaveUpdate, listener);
config.SetListener(NHibernate.Event.ListenerType.Save, listener);

Класс:

public class PonySaveOrUpdateEventListener : DefaultSaveOrUpdateEventListener
{
    protected override object EntityIsPersistent(SaveOrUpdateEvent @event)
    {
        Pony ent = @event.Entity as Pony;

        // I don't care if it's not a pony or if the entity doesn't need updating, call base!
        if (ent == null || !IsDirty(@event))
            return base.EntityIsPersistent(@event);

        // Do nasty dialect-specific raw SQL because using NH to update this row throws us into an infinite loop
        string tablename = ((ILockable)@event.Entry.Persister).RootTableName.ToLower();
        System.Data.IDbCommand command = ((ISession)@event.Session).Connection.CreateCommand();
        command.CommandText = String.Format("update {0} set RevisionValidTo = {1} where Id = '{2}'", tablename, CurrentRevision.Id, ent.Id);
        command.ExecuteNonQuery();

        // Make the event look like it was never persistent and force a transient insert of the entity
        ent.Id = Guid.Empty;
        @event.Entry = null;
        return EntityIsTransient(@event);
    }

    protected override object EntityIsTransient(SaveOrUpdateEvent @event)
    {
        Pony ent = @event.Entity as Pony;
        if (ent == null)
            return base.EntityIsTransient(@event);

        ent.RevisionValidFrom = Host.NextRevision;
        ent.RevisionValidTo = null;

        return base.EntityIsTransient(@event);
    }

    private static bool IsDirty(SaveOrUpdateEvent @event)
    {
        IEntityPersister persister = @event.Entry.Persister;
        object[] oldState = @event.Entry.LoadedState;
        object[] currentState = persister.GetPropertyValues(@event.Entity, @event.Session.EntityMode);
        Int32[] dirtyProps = persister.FindDirty(currentState, oldState, @event.Entity, @event.Session);
        return dirtyProps != null;
    }
}
person snicker    schedule 15.03.2010
comment
Разве метод ISession.SaveOrUpdate () не справился со своей задачей? - person Will Marcouiller; 15.03.2010