Как отключить поведение обновления NHibernate (грязная проверка)?
Я только что обнаружил, что если я получу объект из сеанса NHibernate и измените свойство на объекте, NHibernate автоматически обновит объект при совершении без вызова Session.Update(myObj)
!
Я вижу, как это может быть полезно, , но по умолчанию поведение кажется сумасшедшим!
Обновление: Теперь я понимаю упорство невежества, так что теперь это явно предпочтительный вариант. Я оставлю здесь этот смущающий вопрос, надеюсь, помочь другим профанам.
Как я могу остановить это? Является ли это поведением NHibernate по умолчанию или что-то из Fluent NHibernate AutoPersistenceModel?
Если нет способа остановить это, что мне делать? Если я не пропущу точку, это поведение создает правильный беспорядок.
Я использую NHibernate 2.0.1.4 и Fluent NHibernate build от 18/3/2009
Этот парень прав с его ответ?
Я также прочитал, что переопределение прослушивателя событий может быть решением этого. Однако IDirtyCheckEventListener.OnDirtyCheck
не вызывается в этой ситуации. Кто-нибудь знает, какой слушатель мне нужно переопределить?
Ответы
Ответ 1
Вы можете установить Session.FlushMode
на FlushMode.Never
. Это сделает ваши операции явными
т.е.: на tx.Commit()
или session.Flush()
. Конечно, это все равно будет обновлять базу данных при фиксации/сбросе. Если вы не хотите этого поведения, вызовите session.Evict(yourObj)
, и он станет временным, и NHibernate не выдаст для него никаких команд db.
Ответ на ваше редактирование: Да, этот парень дает вам больше опций о том, как его контролировать.
Ответ 2
Мое решение:
- В вашем первоначальном создании ISession (где-то внутри ваших регистраций фреймворка) установите DefaultReadOnly в true.
- В вашей реализации IRepository, которая обертывает NHibernate и управляет ISession и т.д., в методах Insert, Update, InsertUpdate и Delete (или подобных), которые вызывают ISession.Save, Update, SaveUpdate и т.д., вызовите SetReadOnly для объекта и флаг установлен на false.
Ответ 3
Вызов SaveOrUpdate() или Save() делает объект постоянным. Если вы извлекли его с помощью ISession или из ссылки на постоянный объект, тогда объект будет постоянным, и очистка сеанса сохранит изменения. Вы можете предотвратить это поведение, вызывая Evict() на объекте, который делает его переходным.
Отредактировано для добавления: Обычно я рассматриваю ISession как единицу работы. Это легко реализовать в веб-приложении. используя сеанс-за-запрос, но требует большего контроля в WinForms.
Ответ 4
Мы сделали это с помощью Слушателей событий с NH (это не моя работа, но я не могу найти ссылку, где я это сделал).
У нас есть EventListener для чтения в данных, чтобы установить его как ReadOnly, а затем один для Save (и SaveOrUpdate), чтобы установить их как загруженные, чтобы объект сохранялся, когда мы вручную вызываем Save()
на нем.
Это - или вы можете использовать IStatelessSession, у которого нет состояния /ChangeTracking.
Это устанавливает сущность/элемент как ReadOnly сразу при загрузке.
Я включил только один прослушиватель событий Insertion, но мой код конфигурации ссылается на все из них.
/// <summary>
/// A listener that once an object is loaded will change it status to ReadOnly so that
/// it will not be automatically saved by NH
/// </summary>
/// <remarks>
/// For this object to then be saved, the SaveUpdateEventListener is to be used.
/// </remarks>
public class PostLoadEventListener : IPostLoadEventListener
{
public void OnPostLoad(PostLoadEvent @event)
{
EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);
entry.BackSetStatus(Status.ReadOnly);
}
}
При сохранении объекта мы вызываем это, чтобы установить этот объект на Loaded (что означает, что он будет сохраняться)
public class SaveUpdateEventListener : ISaveOrUpdateEventListener
{
public static readonly CascadingAction ResetReadOnly = new ResetReadOnlyCascadeAction();
/// <summary>
/// Changes the status of any loaded item to ReadOnly.
/// </summary>
/// <remarks>
/// Changes the status of all loaded entities, so that NH will no longer TrackChanges on them.
/// </remarks>
public void OnSaveOrUpdate(SaveOrUpdateEvent @event)
{
var session = @event.Session;
EntityEntry entry = session.PersistenceContext.GetEntry(@event.Entity);
if (entry != null && entry.Persister.IsMutable && entry.Status == Status.ReadOnly)
{
entry.BackSetStatus(Status.Loaded);
CascadeOnUpdate(@event, entry.Persister, @event.Entry);
}
}
private static void CascadeOnUpdate(SaveOrUpdateEvent @event, IEntityPersister entityPersister,
object entityEntry)
{
IEventSource source = @event.Session;
source.PersistenceContext.IncrementCascadeLevel();
try
{
new Cascade(ResetReadOnly, CascadePoint.BeforeFlush, source).CascadeOn(entityPersister, entityEntry);
}
finally
{
source.PersistenceContext.DecrementCascadeLevel();
}
}
}
И мы реализуем его в NH таким образом:
public static ISessionFactory CreateSessionFactory(IPersistenceConfigurer dbConfig, Action<MappingConfiguration> mappingConfig, bool enabledChangeTracking,bool enabledAuditing, int queryTimeout)
{
return Fluently.Configure()
.Database(dbConfig)
.Mappings(mappingConfig)
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<__AuditEntity>())
.ExposeConfiguration(x => Configure(x, enabledChangeTracking, enabledAuditing,queryTimeout))
.BuildSessionFactory();
}
/// <summary>
/// Configures the specified config.
/// </summary>
/// <param name="config">The config.</param>
/// <param name="enableChangeTracking">if set to <c>true</c> [enable change tracking].</param>
/// <param name="queryTimeOut">The query time out in minutes.</param>
private static void Configure(NHibernate.Cfg.Configuration config, bool enableChangeTracking, bool enableAuditing, int queryTimeOut)
{
config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, "none");
if (queryTimeOut > 0)
{
config.SetProperty("command_timeout", (TimeSpan.FromMinutes(queryTimeOut).TotalSeconds).ToString());
}
if (!enableChangeTracking)
{
config.AppendListeners(NHibernate.Event.ListenerType.PostLoad, new[] { new Enact.Core.DB.NHib.Listeners.PostLoadEventListener() });
config.AppendListeners(NHibernate.Event.ListenerType.SaveUpdate, new[] { new Enact.Core.DB.NHib.Listeners.SaveUpdateEventListener() });
config.AppendListeners(NHibernate.Event.ListenerType.PostUpdate, new[] { new Enact.Core.DB.NHib.Listeners.PostUpdateEventListener() });
config.AppendListeners(NHibernate.Event.ListenerType.PostInsert, new[] { new Enact.Core.DB.NHib.Listeners.PostInsertEventListener() });
}
}