2009-03-23 6 views
27

Acabo de descubrir que si obtengo un objeto de una sesión de NHibernate y cambio una propiedad en objeto, NHibernate actualizará automáticamente el objeto en commit sin que yo llame al Session.Update(myObj).¿Cómo desactivar el comportamiento de actualización automática (comprobación incorrecta) de NHibernate?

Puedo ver cómo esto podría ser útil, pero como comportamiento predeterminado parece loco!

Actualización: Ahora entiendo la ignorancia de persistencia, por lo que este comportamiento ahora es claramente la opción preferida. Dejaré esta pregunta ahora embarazosa aquí para ayudar a otros usuarios profanos.

¿Cómo puedo evitar que esto suceda? ¿Es este comportamiento predeterminado de NHibernate o algo proveniente del AutoPersistenceModel de NHibernate de Fluidez?

Si no hay forma de detener esto, ¿qué debo hacer? A menos que me pierda el punto, este comportamiento parece crear un desorden correcto.

estoy usando NHibernate NHibernate 2.0.1.4 y una estructura a partir del 18/3/2009 Fluido

¿Es esta persona correcta con his answer?

También he leído que anular un Oyente de eventos podría ser una solución a esto. Sin embargo, IDirtyCheckEventListener.OnDirtyCheck no se llama en esta situación. ¿Alguien sabe qué oyente necesito anular?

Respuesta

13

Puede establecer Session.FlushMode en FlushMode.Never. Esto hará que sus operaciones sean explícitas

es decir, en tx.Commit() o session.Flush(). Por supuesto, esto aún actualizará la base de datos al confirmar/vaciar. Si no desea este comportamiento, llame al session.Evict(yourObj) y se volverá transitorio y NHibernate no emitirá ningún comando db para él.

Responder a su edición: Sí, ese tipo le da más opciones sobre cómo controlarlo.

+1

Sí, pero aún sucederá cuando lo haga. –

+0

ahh, luego haga que el objeto sea transitorio haciendo Session.Evict() –

+0

Me da la sensación de que nhibernate no quiere que yo controle las actualizaciones de forma manual, todas estas soluciones parecen piratear. ¿Por qué es esto? –

1

Llamar a SaveOrUpdate() o Guardar() hace que un objeto sea persistente. Si lo ha recuperado utilizando una ISession o una referencia a un objeto persistente, entonces el objeto es persistente y el lavado de la sesión guardará los cambios. Puede evitar este comportamiento llamando a Desalojar() en el objeto que lo hace transitorio.

Editado para agregar: en general considero que una ISession es una unidad de trabajo. Esto se implementa fácilmente en una aplicación web. usando session-per-request pero requiere más control en WinForms.

+0

Después de desalojar, ¿puedo hacer una actualización manual del objeto? ¿Es todo este enfoque una buena idea? ¿Hay alguna manera de hacer que este comportamiento predeterminado para todos los objetos o tengo que expulsar a cada uno? –

+0

Después de Desalojar, el objeto es transitorio. Para hacerlo persistente de nuevo, puede llamar SaveOrUpdate en él. –

+0

Creo que estás intentando pelear la sesión como una unidad de trabajo. Hay una manera de volver a conectar, pero probablemente solo deberías confiar en ciclos de vida de sesión más ajustados. En una aplicación web, esto sería por solicitud, en un cliente inteligente sería por unidad de trabajo. –

3

Mi solución:

  1. En su creación inicial ISession, (en algún lugar dentro de sus registros marco de inyección) establecer DefaultReadOnly true.
  2. En su implementación de IRepository que envuelve NHibernate y administra la ISession y tal, en los métodos Insertar, Actualizar, InsertarActualizar y Eliminar (o similares) que invocan ISession.Save, Update, SaveUpdate, etc., llame a SetReadOnly para la entidad y bandera establecida en falso.
+0

¡Gracias, me salvaste el día! –

0

Lo hicimos mediante el uso de los oyentes del evento con NH (Este no es mi trabajo, pero no puedo encontrar el enlace donde lo hice ...).

Tenemos una EventListener para la lectura de los datos, para establecerlo como de sólo lectura - y luego una para guardar (y saveOrUpdate) para establecer como carga, por lo que objeto persistirá cuando llamamos manualmente Save() en él .

Eso - o podría usar una sesión de IStateless que no tenga State/ChangeTracking.

Esto establece la entidad/elemento como ReadOnly inmediatamente al cargar.

Solo he incluido un detector de eventos de inserción, pero mi código de configuración hace referencia a todos ellos.

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

Al guardar el objeto, llamamos a esto para establecer ese objeto para Loaded (lo que significa que ahora persistirá)

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

Y incorporación del mismo a NH por consiguiente, tan:

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

¿Podría decirnos por qué ha decidido hacer esto? ¿Notaste algún aumento en el rendimiento? – yeska

+0

Algunas veces estábamos actualizando el modelo de datos, pero no estábamos seguros de si queríamos guardarlos (creo que esto fue hace 2 años). Posiblemente sea un mal arco, ya que solo debería cambiar realmente lo que quiere guardar. Desactivar el seguimiento * debería * mejorar teóricamente el rendimiento, pero luego debe guardar todo manualmente. –

Cuestiones relacionadas