[NHibernate] NonUniqueObjectException–> a different object with the same identifier value was already associated with the session

Problema

Si trabajas con NHibernate puede que te hayas encontrado con este error alguna vez cuando haces un Update de una entidad de tu dominio. El problema viene dado por lo que se conoce como Cross-Session operations.

En mi caso tengo un servicio REST que es el punto de entrada de mi aplicación y que se encarga de resgitrar dependencias (IoC) y crear la factoría de NHibernate y abrir la sessión y por otra parte hay un proceso asíncrono que actualiza cierta información en base a eventos que se disparan y que abre otra sesión. Esto hace que en el proceso asíncrono me encuentre con esta excepción:

NonUniqueObjectException–> a different object with the same identifier value was already associated with the session

El problema es que el método Update sí encuentra un objeto con el mismo identificador en otra sesión y la referencia es diferente (Estamos trabajando con Detached Entity) lanzará una excepción del tipo NonUniqueObjectException.

Podéis echar un vistazo al blog de Ayende que habla acerca de esto

Solución

  1. La mejor opciónd esde mi punto de vista es recuperar de la sesión la entidad grupo (No usar las Detached Entities) y llamar al método Update:
    public GroupDTO ModifyGroup(GroupDTO group){    

     

        using (var scope = new TransactionScope())    

        {        

            var editGroup = _groupRepository.Get(group.Id);

            editGroup = _groupRepository.Modify(editGroup); //Hace uso de Update  

            scope.Complete();    

        }     

     

        return Mapper.Map<Group, GroupDTO>(editGroup);

    }

  2. Pero si queremos trabajar con Detached Entities (Son entidades que no está en la sesión de NHibernate, como en este caso editGroup y de aquí el error del que hablabamos) podemos resolver el problema haciendo uso del método Merge que se encargará de resolver este conflicto.
    public GroupDTO ModifyGroup(GroupDTO group)

    {

        var editGroup = Mapper.Map<GroupDTO, Group>(group);

     

        using (var scope = new TransactionScope())

        {

            editGroup = _groupRepository.Modify(editGroup); //Hace uso de Merge

            scope.Complete();

        }

     

        return Mapper.Map<Group, GroupDTO>(editGroup);

    }

Un saludo.

8 comentarios en “[NHibernate] NonUniqueObjectException–> a different object with the same identifier value was already associated with the session”

  1. Si ejecutas “Get(id)” estás realizando una petición innecesaria a la base de datos. Probaría a cargar el objeto mediante el método “Load(id)”.

  2. Hola Soren,

    Muchas gracia Soren por tu comentario 🙂

    Estoy usando second level caché, es decir, en el primer Get va a base de datos y en el segundo tira de caché 😉 Puedes hacer la prueba con NHibernate Profiler http://nhprof.com/Download 😉

    Un saludo y gracias por comentar.

  3. Un último apunte. Creo que merece mucho la pena utilizar Spring.NET con NHibernate. Spring.NET te gestiona automáticamente las transacciones y te evitas el engorro de hacerlo a través de NHibernate.

  4. Gracias por el apunto Soren, no he trabajado nunca con Spring.NET así que le echaré un ojo.

    ¿Alguna recomendación, libro, doc… para empezar?

    Un saludo

  5. La verdad es que no puedo recomendar ningún libro de Spring.Net. En mi caso aprendí directamente de la documentación y con los ejemplos que ofrecen.

    Desde mi punto de vista las grandes ventajas de Spring.Net frente a los clásicos contenedores para la inyección de dependencias son:

    1. Sencillez a la hora de gestionar las transacciones.

    [Transaction(ReadOnly = false)]
    public void UpdateCustomers(…)
    {

    }

    El atributo “[Transaction]” ofrece varias propiedades para personalizar la transacción, por ejemplo: Isolation.

    También se agradece lo bien que gestiona el anidamiento de transacciones, por ejemplo en el típico caso de servicio de aplicación que utiliza diversos servicios de dominio.

    2. Durante las pruebas de integración Spring.Net gestiona los “rollbacks” y te despreocupas de este tema.

    Mi recomendación para empezar con Spring.Net es descargar el fichero completo con todos los ejemplos y la documentación:

    http://www.springsource.org/download/community

    El mejor ejemplo si utilizas NHibernate es el proyecto “Spring.Data.NHibernate.Northwind”. Muestra perfectamente cómo integrar NHibernate y Spring.Net en una aplicación de N-Capas y perfectamente descoplado, con las correspondientes pruebas unitarias y de integración.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *