EntityFramework y relaciones muchos a muchos. Cómo tratarlo sin morir en el intento.

Hoy vengo con un post de programación avanzada. Es un caso concreto que me he ido encontrando en los últimos proyectos .Net que he hecho. Voy a explicar algunos conceptos y truquillos de cómo trata el Entity Framework las relaciones too many – too many entre tablas.

El Entity Framework nos facilita enormemente las funcionalidades estándars de consulta, inserción y modificación de una base de datos, pero hay que conocer bien como maneja las relaciones para que no nos suponga una desventaja en lugar de una ventaja, y por mi experiencia las relaciones muchos a muchos en EF, a veces no son fáciles de tratar.

Empecemos haciendo una breve guía de cómo funciona.

Imaginemos que tenemos una base de datos donde estamos guardando información de cómics y sus autores. Sabemos que un cómic puede tener más de un autor y que un autor puede haber escrito o dibujado más de un cómic, por lo que un posible diagrama reducido de BBDD quedaría así.

DiagramaBBDDAhora bien,… el EF al mapear estas tablas genera el siguiente diagrama.

EF

Cómo vemos, el EF detecta que es una relación muchos a muchos y en lugar de generar la tabla explosionada intermedia incorpora una colección en cada una de las clases haciendo referencia a la otra clase. El código generado sería el siguiente:

autores comics

Insertar datos

Para insertar datos en la tabla intermedia podemos hacer lo siguiente: incorporar en las vistas una select multiple con el listado de autores o cómics (dependiendo de la vista en la que estemos) y enviarlo al controlador para que lo incorpore en la colección correspondiente de la clase. De esta forma añadiendo el nuevo autor o cómic, directamente se añadirán las relaciones. Veámos como sería con código:

Suponemos que queremos dar de alta un cómic y asignarle sus autores que previamente ya existen en la base de datos:

  1. En el Get de Create del controlador de cómics, incluiremos una lista con todos los autores de la base de datosGetCreateAutores
  2. En la vista Create de cómics añadiremos una select multiple que recogerá todos los autores que hemos pasado a través del ViewBag para que seleccionemos los que queremos relacionar con el nuevo cómic. (Lógicamente si existen muchos autores optaremos por otro componente o bien utilizaremos librerías o ajax para que nos facilite la búsqueda entre ellos)
    vistaCreateAutores Visualmente podríamos tener algo de este estilo:CreateComics
  3. Una vez seleccionado los autores que queremos añadirle al comic, los enviamos como una lista al controlador, donde los recogeremos, añadiremos a la colección de Autores de la clase Comics, haciendo de esta forma que cuando llamemos a su método Add, añadirá no sólo la información en la tabla de Comics, sino que también añadirá la relación con sus autores en la tabla ComicAutor:
  4. postCreatecomicEn base de datos obtendremos este resultado: comicAutorCreate

Bueno,… hasta ahora parece que todo bien y no es complicado,… funciona perfecto y nos ahorra tener que llamar explícitamente al método Add de la tabla ComicAutor.

Vamos a ver cómo funciona ahora el modificar con el EF

Modificar datos

Para el proceso de modificar datos, hay muchas formas de hacerlo (como en todo proceso de programación) pero la que suelo usar más a menudo sería similar al ya explicado para el create, que sería añadir en el controlador (en el Get Edit) una lista con todos los autores y pasarla a la vista para rellenar una select multiple. En el caso del Edit, además debemos pasar los autores que ya estan dados de alta en la tabla intermedia para poder hacer que en la vista salgan seleccionados. Para ello suelo añadir un Include que me devuelva la colección de los Autores en la propia clase de Comics que quiero editar. El código sería este:

  1. En el Get de Edit del controlador del cómic, incluiremos una lista con todos los autores de la base de datos y añadiremos en la obtención del comic el Include para que traiga los valores de la tabla ComicAutorgetEdit
  2. En la vista Edit de cómics añadiremos una select multiple con la lista de todos los autores y una variable que añadirá el texto “selected” a los autores que ya están relacionados con el cómic para que aparezcan seleccionados.viewEditVisualmente quedaría así
  3. vistaEditComicImaginemos ahora que queremos hacer cambios en los autores tal y como se muestra en la imagen siguiente:
  4. cambioAutoresEn este caso nos encontramos con 3 situaciones diferentes: Hay que quitar un autor (Ino Asano), añadir otro (Tsugumi Oba) y mantener otro (Takeshi Obata).
    En nuestra ingenuidad, y creyendo que la programación es lógica y casi automática podríamos pensar que una solución como la del Create bastaría para hacer esta modificación, y que el propio EF se encargaría de detectar qué es lo que tiene que quitar, añadir o mantener,… pero no, lo siento,… no es tan adivino.
    La solución reflejada en la imagen siguiente no funciona, lo único que hace es modificar los cambios que realizamos en los datos de la clase Comics pero no modifica las relaciones con Autores, ya que sólo cambia el estado a modificado de la entidad Comic.noeditcomicY entonces os preguntareis,… ¿cómo puedo cambiar el estado de los registros de la entidad ComicAutor si el EF no me ha creado una clase para ella?. Pues efectivamente éste es el quid de la cuestión y después de muchos batacazos y probar diferentes formas de hacerlo, lo encontré,… por eso decidí escribir este post. No os impacientéis que os lo cuento enseguida,…
  5. El truco está en primero guardar los datos de la clase Comics y después volver a recuperarla con el Include de la colección de autores para tratar la colección y hacer las operaciones pertinentes para borrar, añadir o mantener los datos de ComicAutor.
    Una de las formas sería comparar en el controlador la lista de autores antiguos, con la de los autores seleccionados en la vista y hacer operaciones entre ellas: los datos que sean comunes, mantenerlos,… si hay datos en la lista antigua que no se haya seleccionado en la nueva, quitarlos,… y si hay datos en la lista nueva que no estén en la antigua añadirlos.
    Pero la verdad, es que yo soy de la ley del mínimo esfuerzo y me parece un poco engorroso el comparar listas en este caso, así que yo prefiero, borrar todas las relaciones que había anteriormente en la colección de Autores para ese cómic y añadir las nuevas que vienen seleccionadas desde la vista, tal y como muestro en la siguiente imagen:editComicAutores

OJO!! CASO ESPECIAL CON DISTINTO DBCONTEXT

En el ejemplo que estoy haciendo estoy trabajando con el dbContext en el propio controlador, si tenemos una arquitectura de software dividia en proyectos donde pasamos toda la lógica de acceso a datos a otro proyecto con otro dbContext, es posible que tengamos que hacer primero un Detach de cada uno de los Autores antes de poder borrar las relaciones con Comic.  En la siguiente imagen muestro como hacer el Detach antes de borrar:dbcontext

Y AQUÍ VIENE LO BUENO: TRUCO PARA ENGAÑAR AL ENTITY

Si te pasa como a mi  que eres de la vieja escuela de programadores y te gusta tener el control de todas las tablas y no dejarle la gestión al Entity Framework, hay un truco para “engañarle” y que te cree la clase de la tabla intermedia, con lo que serás tú quien hagas cuando tú quieras y cómo quieras los inserts, updates y deletes.

El truco es tan fácil cómo añadirle una columna más a la tabla ComicAutor que no esté relacionada con ninguna otra tabla. En mi caso podría ser añadir un campo Función donde se pudiera poner que es lo que hace ese Autor en ese Cómic, por ejemplo si hace el guión, el dibujo, entintado…

También en muchos casos se podría añadir columnas de auditorías con el usuario que modifica y el timestamp de la modificación o creación,… o si no sabes que añadir, incluye simplemente una columna Dummy que no utilices para nada,… el caso es añadir algo,..

Con esta columna añadida lo que conseguimos es que el EF mapee las tablas con el siguiente diagrama:

AutoresComics

Y cree las clases siguientes:

ClassAutores

classComic

classComicAutor

Y de esta forma tendrás acceso a todos los métodos de la clase ComicAutor para añadir, modificar, borrar y manejar los datos como quieras,….

metodosComicAutorYo casi lloré con este descubrimiento,…. TT,… gracias a él ya no le tengo tanta manía a las relaciones muchos a muchos con EF.

Bueno, al final me ha salido un poco largo y denso este POST, pero espero que te ayude a entender más sobre este tema y a encontrar truquillos para manejarlos mejor.

Nos leemos en el siguiente

Technology is coming!!!

5 opiniones en “EntityFramework y relaciones muchos a muchos. Cómo tratarlo sin morir en el intento.”

  1. hola, excelente post muy bien explicado. esto era lo esta buscando, yo estaba como tu partiendome el coco jejejeje.
    saludos desde Venezuela

  2. Me habría mucho gustado q pongas el ejemplo de hacer la eliminación por el camino largo.
    Cómo garantizas consistencia en caso q se corte comunicación con la base si ya enviaste a realizar el Delete??

Deja un comentario

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