【问题标题】:How can I update a row and insert a new one automatically in NHibernate with one call to Save?如何通过一次调用 Save 在 NHibernate 中自动更新一行并插入一个新行?
【发布时间】:2010-03-20 07:15:19
【问题描述】:

假设我有一个 II 型 SCD 数据库,基本上只是追加。我正在使用 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,基本上就好像它是一个被持久化到数据库中的全新对象。

【问题讨论】:

    标签: c# nhibernate versioning


    【解决方案1】:

    这可以通过custom SQL for update 完成。

    【讨论】:

      【解决方案2】:

      我最终创建了自己的 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;
          }
      }
      

      【讨论】:

      • ISession.SaveOrUpdate() 方法没有完成这项工作吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 2014-02-10
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 2013-09-05
      • 1970-01-01
      相关资源
      最近更新 更多