2009-03-23 17 views
27

Właśnie odkryłem, że jeśli otrzymam obiekt z sesji NHibernate i zmieniam właściwość obiektu, NHibernate automatycznie zaktualizuje obiekt przy zatwierdzaniu bez wywoływania Session.Update(myObj)!Jak wyłączyć automatyczne aktualizowanie NHibernate (sprawdzanie nieczytelności)?

Widzę, jak to może być pomocne, , ale jako zachowanie domyślne wydaje się szalone!

Aktualizacja: Teraz rozumiem uporczywą ignorancję, więc to zachowanie jest teraz wyraźnie preferowaną opcją. Zostawię to teraz żenujące pytanie, aby pomóc innym, wulgarnym użytkownikom.

Jak mogę to zatrzymać? Czy jest to domyślne zachowanie NHibernate lub coś pochodzącego z AutoPersistenceModel Fluent NHibernate?

Jeśli nie można tego zatrzymać, co mam zrobić? O ile nie brakuje mi punktu, to zachowanie wydaje się tworzyć właściwy bałagan.

Używam NHibernate 2.0.1.4 i Fluent NHibernate budować od 18/3/2009

Czy ten facet rację z his answer?

Czytałem również, że przesłonięcie odbiornika zdarzeń może być rozwiązaniem tego problemu. Jednak w tej sytuacji nie jest wywoływana IDirtyCheckEventListener.OnDirtyCheck. Czy ktoś wie, którego słuchacza muszę zastąpić?

Odpowiedz

13

Można ustawić Session.FlushMode na FlushMode.Never. Spowoduje to, że operacje użytkownika będą jawnie oznaczone jako:

tj .: na tx.Commit() lub session.Flush(). Oczywiście nadal będzie aktualizować bazę danych po zatwierdzeniu/flush. Jeśli nie chcesz tego zachowania, zadzwoń pod numer session.Evict(yourObj), a stanie się on przejściowy, a NHibernate nie wyda dla niego żadnych komend db.

Odpowiedź na twoją edycję: tak, ten facet daje ci więcej opcji, jak to kontrolować.

+1

tak, ale to się stanie, gdy się spiorunuję. –

+0

ahh, a następnie uczynić obiekt przejściowy, wykonując Session.Evict() –

+0

Mam poczucie Nhibernate nie chce mnie kontrolować aktualizacje ręcznie, wszystkie te rozwiązania wydają się hack. dlaczego to? –

1

Wywołanie SaveOrUpdate() lub Save() powoduje, że obiekt jest trwały. Jeśli pobierzesz go za pomocą ISession lub z odniesienia do trwałego obiektu, to obiekt jest trwały i opróżnianie sesji spowoduje zapisanie zmian. Możesz zapobiec temu zjawisku, wywołując Evict() na obiekcie, który sprawia, że ​​jest przejściowy.

Edytowane w celu dodania: Ogólnie uważam, że ISIS jest jednostką pracy. Jest to łatwo zaimplementowane w aplikacji internetowej. za pomocą session-per-request, ale wymaga większej kontroli w WinForm.

+0

Po I Evict, czy mogę ręcznie zaktualizować obiekt? Czy to całe podejście jest dobrym pomysłem? Czy jest jakiś sposób na zachowanie tego domyślnego dla wszystkich obiektów lub czy muszę je eksmitować? –

+0

Po eksmisji obiekt jest przejściowy. Aby uczynić go trwałym ponownie, możesz wywołać SaveOrUpdate na nim. –

+0

Myślę, że próbujesz walczyć z sesją jako jednostką pracy. Istnieje sposób, aby ponownie dołączyć, ale prawdopodobnie powinieneś polegać tylko na krótszych cyklach życia sesji. W aplikacji internetowej byłoby to na żądanie, w inteligentnym kliencie byłaby to jednostka pracy. –

3

Moje rozwiązanie:

  1. W początkowej tworzenia ISession (gdzieś wewnątrz swoich rejestracjach ramowych wtryskowych) ustawić DefaultReadOnly true.
  2. W implementacji IRepository, która otacza NHibernate i zarządza ISession i innymi, w metodach Insert, Update, InsertUpdate i Delete (lub podobnych), które wywołują ISession.Save, Update, SaveUpdate itd., Wywołaj SetReadOnly dla jednostki i flaga ustawiona na false.
+0

Dzięki, oszczędzasz mój dzień! –

0

Zrobiliśmy to, używając Event Listeners z NH (to nie jest moja praca - ale nie mogę znaleźć linka do tego, gdzie to zrobiłem ...).

Mamy EventListener dla podczas czytania w danych, aby ustawić go jako ReadOnly - a następnie po jednym dla Save (i SaveOrUpdate), aby ustawić je jako załadowany, więc że obiekt będzie się utrzymywać, gdy ręcznie wywołać Save() na nim .

To - lub możesz użyć IStatelessSession, która nie ma stanu/ChangeTracking.

Ustawia jednostkę/przedmiot jako Odczytuj natychmiast po załadowaniu.

Dodałem tylko jeden detektor zdarzeń wstawiania, ale mój kod konfiguracji odwołuje się do nich wszystkich.

/// <summary> 
/// A listener that once an object is loaded will change it's 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); 
    } 
} 

Na zapisywania, nazywamy to, aby ustawić ten obiekt do Loaded (czyli będzie to teraz utrzymują)

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

I wdrożyć go w NH więc tak:

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

Czy możesz nam powiedzieć, dlaczego zdecydowałeś się to zrobić? Czy zauważyłeś pewien wzrost wydajności? – yeska

+0

Czasami aktualizowaliśmy model danych, ale nie byliśmy pewni, czy chcemy je zapisać (myślę, że to było 2 lata temu). Prawdopodobnie zły łuk, ponieważ powinieneś tylko zmienić to, co chcesz uratować. Wyłączenie śledzenia * powinno * teoretycznie poprawić wydajność - ale musisz ręcznie zapisać wszystko. –

Powiązane problemy