El sessionFactory.GetCurrentSession lo unico que hace es pedirsela al
context... practicamente no la genera.
Lo que deberias estudiar es uno de los patrones
Session-per-Conversation y Unit-of-Work.
Te doy mi receta...
Que el front-end sea WEB o WIN no debería importar mucho (hay cosas
para la cuales si importa pero no me concentro en eso ahora).
Tu front-end debería apoyarse en "algo" que representa el UseCase.
Ejemplo:
EmisionFacturaUseCase
CRUDClienteUseCase
EstadidisticoVentasUseCase
Tus clases de UseCase brindan al front-end todo lo que necesita
exactamente como lo necesita. Es decir que si tenes combos, filtro
pre-fabricados, etc. el UseCase lo debería tener ya disponibles para
ser usados as-is.
El UseCase es quien se encarga de manejar la conversation y brindará
servicios del estilo:
IsDirty
Save
Cancel
.... what ever.....
El IsDirty es bastante simple ya que se puede redireccionar
directamente al IsDirty de la session de NH.
Para el Cancel lo que se hará es un RallBack de la transaction de NH y
un close de la session.
Para el Save se hará el Save o el Update de las entidades, cuando no
fue hecho en cada una de ella mientras la conversación estaba activa,
y luego se hace el commit de la transaction de NH y el close de la
session.
Para el prj WEB tendrás que tener cuidado a lo que pasa cuando el
usuario abandona la sessionWeb mientras que para el prj Win es mas
facil porque, por lo general, coincide con el close de un form.
En todo el chiste quien mantiene e maneja una session de NH es el UseCase.
Es posible que se me escape algo... de ser así alguien mas me vas a
correguir o te volveremos a leer por estos pagos.
Bye.
Fabio Maulo
Hola Gustavo.
Tal vez fue un mal entendido pero yo no hablé de tener abierta la
session en el form. Quien se ocupa de la session es la clase de
UseCase.
La referencia al form fue porque, en muchas aplicacciones win, un
UseCase se representa mas sencillamente en un form. No siempre es así.
Hay casos en lo cuales el UseCase se representa en una serie de forms
al estilo wizard, en esos casos el UseCase queda vivo para todos los
forms.
En tu caso me parece que hay que cambiar un poco el patrón y usar el
Disconnect-Reconnect, de la session, el Refresh(object) (todavia no
porté el Merge() porque tengo ante que cambiar los cascades) y mas de
una transaction (Begin-Commit/Rollback) en la misma long conversation.
Bye.
Fabio Maulo
Luego de una exception la session va siempre destruida porque queda en
un estado incosistente.
Ojo que en tu caso es fundamental que tus entidades implementen
Equals,GetHashCode y sobre todo que sean versionadas. El Merge()
seguramente sería lo mejor para prevenir exceptions debidos a accesos
concurrentes pero bueno.... aún no está disponible.
Bye.
Fabio Maulo
Justo en el post anterior estaba pensando en consultar por lo que sigue pero no
quise molestar, ahora si me animo.
Una de las features que mas me gusta de Neo (http://neo.sourceforge.net/, (Erik
Doernenburg de ThoughtWorks y Cia.)) eran los contexts (o sesiones en NH)
anidadas.
El concepto es el siguiente (en terminos de NH ´para facilitar la lectura):
puede iniciarse una sesion en determinado lugar de tu codigo y comenzas a operar
contra ella. Luego, en otro lugar necesitas seguir operando pero no sabes si hay
o no hay sesion iniciada (tranquilos, ya se que esto se puede saber, sigan
leyendo), entonces la inicias de nuevo y haces tus cosas. Asi podria hacerse en
varios niveles. Esa sesion puede abrirse contra el store final (base de datos) o
contra la sesion anterior abierta, una suerte de sesion anidada.
Cuando hacias un commit/flush sobre la sesion anidada, en vez de ir a la base de
datos, empujaba (push) los datos a la sesion padre (esto es el Merge, la palabra
magica). Solo el commit/flush de la sesion abierta contra el data store grababa
los cambios en la base de datos.
La ventaja de esto es que si tenias una falla en la sesion anidada, la
descartabas y listo. No era necesario descartar la sesion externa. Hoy en NH,
cuando tenes una excepcion grabando tenes que descartar la sesion porque el
estado de los objetos (las propiedades) ya es invalido comparado contra lo que
tiene registrado NH.
En Neo esto debe haber sido relativamente facil de implementar ya que el soporte
para las propiedades de los objetos estaba implementado en un DataRow de ADO.
Neo recomendaba iniciar contexts para cada pantalla winforms (por ejemplo), yo
hice algunos helpes para dar soporte a lo que acabo de decir.
Esta idea, que a mi me parecio tan buena en esa epoca, es realmente buena o es
una garantia de problemas, que piensan?
Carlos Peix
Cual fue la complicación?
Gracias.
Fabio Maulo
Aaaaah!!
Me pareció raro que te habia dado problemas lo que yo escribí en los
mails anteriores.
En lo que dije yo no hay dos UseCase usando la misma session.
Dos casos de uso significa dos NH.Session abiertas.
Bye.
Fabio Maulo
De todas formas muchas gracias por tu contribución y seguí
desarrollando el ejemplo que seguramente sirve.
Es muy importante que empezemos a compartir soluciones que hayamos
encontrado, no importa que sean perfecta o super-reutilizables, lo
importante es compartirla.
Bye.
Fabio Maulo
Ahora me tengo que ir.... despues leo todo el post.
Si vas a investigarlo vas a descubrir que UseCase no debería derivar
de tu Repository.
No sigas quien dice que un BusinessObject representa un UseCase; eso
es verdad solo y exclusivamente para UseCase que representan CRUD.
Un UseCase no tiene "GetAll" y menos tiene "GetById"... "GetAll" que cosa ?
si adentro tiene, por lo general, n BO que se relecionan con otros...
como decir..... un UseCase representa un "comunidad" de
BusinessObjects así que hacerle implementar la interface de un
Repository<T> me parece muy limitado.
Esta noche voy a leer todo tu post.
Bye.
Fabio Maulo
Yo estoy en la misma busqueda que vos (NH+WinForms), asi que a algo util vamos a
llegar seguramente, incluso Gustavo menciona un tema voy a responder mas tarde.
Me gustaria debatir un poco sobre algunas de las cosas que planteas, aunque aun
no tengo respuesta para algunos temas.
En primer lugar quiero dejar clara MI interpretacion de algunos conceptos,
basada casi exclusivamente en DDD:
Repository: un objeto que me permite acceder a una entidad o aggregate. Su
interfaz tiene una semantica de colección, la implemento mediante una interface
definida en el modelo y una implementacion definida en otro assembly, por
ejemplo, usando DAOs NH. Si decidis usar Criterias, te puede quedar una interfaz
pequeña, si no usas, la interfaz es mas amplia.
DAO: Objetos de acceso a datos, dependen de la tecnologia de acceso a datos
elegida. Quedan ocultos en la implementacion del repository.
UseCase Controller: no suelo usarlos, estas clases, tal como dice Fabio, no
deberian heredar del Repositorio, mas bien usan repositorios, incluso varios. Yo
prefiero definir servicios.
Veo en tus comentarios que le das un ciclo de vida a los repositories, eso me
llama la atencion. Yo creo y descarto las instancias de repository cada vez que
los uso. El ciclo de vida la manejo con la sesion de NH y la inyecto en el
repository.
Avanzando mas en este concepto, la sesion de NH y el repository tienen una vida
bastante ortogonal. La sesion vive durante la interaccion puntual con la base
de datos (el UoW), el repository no importa cuando vivam no tiene estado propio,
es una suerte de capa sobre la sesion de NH.
Por supuesto, esto solo vale para las implementaciones de repositories basadas
en NH, los implementaciones en memoria son bien distintas.
Sigamos debatiendo...
Carlos Peix
> -----Original Message-----
> From: NHibernat...@googlegroups.com
> [mailto:NHibernat...@googlegroups.com] On Behalf Of
> tala...@hotmail.com
> Sent: Jueves, 03 de Enero de 2008 12:18 p.m.
> To: NHibernate-Hispano
> Subject: [NHibernate-Hispano] Re: NHibernate & GetCurrentSession
>
>
El 3/01/08, tala...@hotmail.com <tala...@hotmail.com> escribió:
>
> Duda #1 : Relacion entre UseCase y DAO
> -------------------------------------------
> Algunos UseCases, como EmisionFacturaUseCase, estan claros pero no me
> es facil ver cual el limite entre el UseCase y los DAO.
>
Como me pareció que ya escribió Carlos, si queres comparar UseCase con
algo es mejor que lo compares con el "C" de MVC.
>
> Imaginate que si necesito hacer un Insert de un nuevo cliente dentro
> del NuevaFacturaUseCase se complicarian las cosas. Si cada CRUD tiene
> su UseCase tambien es complicado enmarcar 2 UseCases juntos: ejemplo
> agregar 2 root objects no relacionados entre si (usando misma session
> + misma transaction). Como solucionas este tema?
Normalmente, o por lo menos hasta ahora, si de la emision de un
documento necesito cargar un nuevo cliente yo suelo llamar el UseCase
de CRUDCliente en modalidad Create.
Intento explicar (hice tambien aplicaciones de cero... no solo framework):
Mis clientes que me llamaron a participar de un prj desde su
nacimiento (estos hasta el momento fueron solo prj Windows) tienen
todos un framework de plug-ins. El FW de plug-ins lo hice yo sobre la
experiencia de otro FW que me hice para una aplicación Delphi mia.
Practicamente los UseCase son clasificados por "Context" y "Type". En
el caso de CRUDCustomer el context="Customer" y
Type="Create,Show,Update,Delete" (en realidad sono dos UseCase pero no
la complico). Desde Factura le pido al UseCaseFactory de darme una
instancia del UseCase que se ocupa del Context="Customer" en modalidad
Type="Create"... bueno y lo que sigue (todo eso no es tanto así pero
tengo que describirlo en menos de 200 chars)...
Si lo que el cliente necesita es cargar un su Customer directamente de
adentro de una factura el UseCaseFactura deberá prever eso (fijate que
es muy probable que la cantidad de informaciones del Customer en el
caso de InsideInvoice es probable que sean menores).
> Duda #2: No todo debe/puede estar pre-pensado.
> -----------------------------------------------
> Eso da mas flexibilidad a los consumidores de los servicios ( en este
> caso al diseño del UI ) y simplica el API de los servicios. En caso de
> tener todo pre-determinado no existe flexibilidad alguna en el UI.
Y hace que quien diseña la UI se ocupe de definir temas de negocio.
He esperado tanto tiempo.... ya pasaron 20 años de programación... al
fin quien construye IDE llegó a entender que hay algunos programadores
que están cansado de ser Einstein y Picasso. Parece que con WPF
tendremos clases de programadores que son mas Picasso (y no necesitan
conocer absolutamente nada de negocio) y otros que serán mas Einstein.
>
> NOTA: Obviamente teniendo en cuenta que "esta" decision hace que
> tengamos que incluir una referencia a Nhibernate.dll
> (creo haber leido que se iba a proponer "mover" ICriteria a una DLL
> separada para permitir solo linkear el assembly de ICriteria....)
Escuchaste mal!! Si no queres involucrar la UI con NH (cosa buena y
justa) create tus Criterias. ICriteria de NH está estrictamente
vinculado a otros componentes de NH.
De todos modos eso de no tener la referencia a NH.dll en la UI, aunque
separando ICriteria, seria solo fictizio ya que tendrias una
referencia a NHCriteria.DLL.
>
> OK, o sea para WinForm, cuando el "root form del use case" cierra
> basicamente haces un MyUseCase.Dispose().
>
Si, el UseCase implementa IDisposable ya que deberá hacer Dispose de
session y transaction.
En estas charlas estamos charlando de otras cosas mas que de NH...
pero bueno entiendo que a la hora de usar NH un gran escollo es
tambien el manejo de session.
Bye.
Fabio Maulo