nhibernate fluent-nhibernate optimistic-locking staleobjectstate

¿Cómo se combinan con gracia gráficos de objetos después de NHibernate StaleObjectStateException?



fluent-nhibernate optimistic-locking (1)

Estamos intentando combinar objetos después de lanzar una StaleObjectStateException para guardar una copia combinada.

Aquí está nuestra situación ambiental:

  • Artículo de lista
  • Sistema multiusuario
  • Aplicación de escritorio WPF, base de datos de SQL Server 2008
  • NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712
  • Sesiones globales de NHibernate de larga duración [por el momento. Entendemos que la sesión por presentador es el patrón recomendado, pero no tenemos tiempo para convertir en el cronograma de nuestro proyecto en este momento.]
  • De arriba hacia abajo guarda y navegación de la propiedad (es decir, guardamos el objeto de nivel superior (en este documento llamado Parent) en nuestro gráfico de dominio)
  • .Cascade.AllDeleteOrphan () utilizado en la mayoría de los casos.
  • Los usuarios poseen exclusivamente algunos objetos en el gráfico de dominio, pero comparten la propiedad de Parent.
  • Las propiedades de navegación en los objetos para niños no existen.
  • Todas las clases tienen campos numéricos de Id. Y Versión numérica.

Caso de uso:

  • El usuario 1 inicia la aplicación y abre Parent.
  • El usuario 2 inicia la aplicación y abre Parent.
  • El usuario 2 agrega un niño (en este documento C2).
  • El usuario 2 guarda Parent.
  • El usuario 1 agrega un niño (en este caso C1).
  • El usuario 1 guarda Parent.
  • El usuario 1 recibe una StaleObjectStateException (y con razón)

Queremos manejar con gracia la excepción. Dado que los usuarios comparten la propiedad del elemento primario, el usuario 1 debería poder guardarlo correctamente y guardar al padre con su hijo nuevo y el hijo del usuario 2.

Cuando se arroja SOSE, según Ayende ( http://msdn.microsoft.com/en-us/magazine/ee819139.aspx ):

su sesión y sus entidades cargadas son pan comido, porque con NHibernate, una excepción lanzada desde una sesión mueve esa sesión a un estado indefinido. Ya no puedes usar esa sesión o cualquier entidad cargada

A C1 ya se le ha asignado una identificación y un número de versión en la sesión que ahora no es útil. (Desearía que no hubiera sido así)

¿Cómo combinamos el uso de ISession.Merge () e ISession.Refresh () para obtener un Parent recientemente guardado que tenga C1 y C2?

Hemos probado varias permutaciones arcanas, ninguna de las cuales funciona completamente. Por lo general, una "transacción fue actualizada o eliminada por otra transacción (o la asignación de valores no guardados era incorrecta" o una colisión de identificación real en el nivel ODBC).

Nuestra teoría, por el momento:

  1. Restablezca los números de versión en C1 (para evitar que "la asignación de valores no guardados sea incorrecta")
  2. Obtener una nueva sesión
  3. newSession.Refresh (C1);
  4. newParent = newSession.QueryOver [...]
  5. newParent.Add (C1);
  6. newSession.SaveOrUpdate (newParent)

Sin embargo, toda la documentación sugiere que se supone que newSession.Merge es suficiente.

Otros mensajes utilizados como investigación:
Nuevo usuario de NHibernate: Fila fue actualizada o eliminada por otra transacción
¿Existe una alternativa a ISession.Merge () que no arroja cuando se usa el bloqueo optimista?
La fila StaleObjectstateException fue actualizada o eliminada por
Cómo puedo decirle a NHibernate que solo guarde las propiedades modificadas
Hibernate (JPA): cómo manejar StaleObjectStateException cuando varios objetos han sido modificados y comprometidos (java, pero relevante, creo)


Dado que los usuarios comparten la propiedad del elemento primario, el usuario 1 debería poder guardarlo correctamente y guardar al padre con su hijo nuevo y el hijo del usuario 2.

¿Por qué no simplemente deshabilita el bloqueo optimista en la colección secundaria? Entonces, cualquiera puede agregar hijos y no aumentará la versión del padre.

De lo contrario, esta es la solución que mi proyecto actual utiliza para todas las excepciones recuperables que una sesión podría arrojar (por ejemplo, conexión a DB perdido, clave externa violada, ...):

  1. Antes de llamar a session.Flush() la sesión se serializa en un MemoryStream .
  2. Si session.Flush() o transaction.Commit() arroja una excepción que es recuperable, la sesión original se elimina y la guardada se deserializa.
  3. La pantalla de llamada obtiene la información de que la sesión se recuperó después de una excepción y vuelve a llamar a las mismas consultas a las que se llamó cuando se abrió la pantalla la primera vez. Y debido a que todas las entidades modificadas todavía están en la sesión recuperada, el usuario ahora tiene el estado justo antes de presionar guardar.