Cómo separar las clases de Entity Framework en un proyecto separado?

En este post vamos a seguir viendo algunas de las funcionalidades que tenemos disponibles en Entity Framework, en este caso cómo separar las clases que generadas por EF en un proyecto separado.

Generalmente cuando trabajamos con una arquitectura en capas tenemos un proyecto para nuestro acceso a datos separado de lo que es la parte de interfaz de usuario. A su vez por cuestiones de ordenamiento o por necesidad de compartir estas clases con otros proyectos (o directamente exportarlos en una assembly separada para compartirla con otra solución diferente) puede que necesitemos que las clases generadas por EF estén separadas de lo que es en sí el proyecto de acceso a datos.

Para empezar con este ejemplo vamos a plantear nuestro caso inicial. Tenemos nuestra aplicación ASP.NET MVC con dos proyectos, uno de la App web en sí y otro para el acceso a datos con Entity Framework Database First:

MoverClasesEF - ConfiguracionInicial

Lo que vamos a hacer a continuación es agregar un nuevo proyecto para nuestras clases compartidas, en este caso DemoEF.Entities. Luego de crearlo habrá que agregar al proyecto de datos la referencia al mismo.

Ahora empezaremos a trabajar en la separación de las clases en sí. Lo primero que debemos hacer es crear sobre el proyecto Entities un generador de DbContext de Entity Framework como vamos a ver en la siguiente imagen:

MoverClasesEF - CrearGenerator

Una vez que se hayan creado correctamente los archivos asociados, debemos abrir el archivo EditorialDataContext.tt que acabamos de crear en nuestro proyecto Entities y editar la variable inputFile por la ruta del archivo edmx de Entity Framework que tenemos en nuestro proyecto de datos:

MoverClasesEF - EstablecerReferencia

Cuando guardemos los cambios, podremos ver que efectivamente se agregaron las clases de Entity Framework en nuestro proyecto Entities:

MoverClasesEF - ClasesCreadas

Con esto finalizamos la primer parte. Ahora lo que debemos hacer es enlazar la parte de acceso a datos con las clases de nuestro proyecto Entities. Para ello vamos a las propiedades del archivo EditorialDataContext.Context.tt de la parte de datos y en la opción “Espacio de nombres de la herramienta personalizada” definimos el nombre del proyecto donde están definidas nuestras entidades, en este caso DemoEF.Entities. Esto permitirá que Entity Framework efectivamente utilice las clases del proyecto de entidades en vez de las de datos.

MoverClasesEF - RelacionEntreProyectos

Solo nos queda quitar aquellas cosas que no utilizaremos en cada proyecto. En el de datos deberemos sacar el archivo EditorialDataContext.tt y en el de entidades EditorialDataContext.Context.tt, ya que son los archivos sobrantes de los pares que estamos separando en dos proyectos:

MoverClasesEF - EliminarArchivosInnecesarios

Y listo, separación de clases realizada. Lo que deberemos hacer posteriormente cuando modifiquemos el archivo del DataContext porque hubo una modificación en el esquema de datos, es hacer click derecho en el archivo EditorialDataContext.tt y seleccionar la opción “Ejecutar herramienta personalizada“. Recién en este punto se hará efectiva la edición de nuestro esquema de datos en las clases asociadas.

Espero que les sea útil para aplicar en sus proyectos (especialmente a Matías, que a raíz de su comentario sobre esta situación hizo que escriba el post).

Gracias por leer!!

Anuncios

15 comentarios en “Cómo separar las clases de Entity Framework en un proyecto separado?

  1. Hola Diego, gracias por tu respuesta y elaboración del post para aclarar mi duda! Ya lo voy a poner a probar!
    Te hago otra pregunta, en base a tu experiencia…consideras mas oportuno para un proyecto grande (cientos de tablas) utilizar Database First o Code First? Es una respuesta muy relacionada a la experiencia, considero que si estas mas familiarizado con UML y el paradigma POO lo será CF, y si lo tuyo es SQL o alguna herramienta para el modelado de BD me dirás DBF, pero mi pregunta va mas por el lado del rendimiento…razon por la cual, tampoco he descartado abstenerme del uso de EF y optar por codigo puro en un proyecto de 3 capas y entidades, combinado con procedimientos almacenados y disparadores, siendo razonable que esto ultimo demanda mucho mas tiempo de codificacion..

    Me gusta

  2. Como siempre en estos casos depende de la práctica y comodidad del equipo de desarrollo con cada herramienta. Aunque hay justificativos que tienden a usar cada una de las alternativas dependiendo del caso:
    – Si estamos hablando de bases de datos con muchas tablas (y de un sistema heredado), la mejor alternativa me parece que es Database First. Esto lo veo independiente entre la preferencia entre POO y SQL, personalmente me inclino totalmente por POO, pero en la parte de acceso a datos de grandes sistemas con muchas tablas, y donde hay muchas personas trabajando, termina siendo mucho más ordenado y productivo Database First.
    – Code First lo usaría (y lo usé de esta forma) solo para aquellos casos en los que estás comenzando un proyecto de cero, en el cual no tenes nada “heredado”. A su vez sería ideal mantenerlo solo durante un tiempo en el cual se lo está haciendo crecer en la parte de desarrollo inicial, una vez que se implemente en producción o sean muchas las personas trabajando se haría el cambio al esquema Database First.

    Pasando en limpio, me parece que lo mejor sería hacerlo con Database First, dentro de las alternativas que ofrece Entity Framework. Sobre la otra alternativa que propones, yo aprovecharía a dejar la mayor cantidad de cosas de lógica del lado de la aplicación, y si utilizar SP’s para aquellos casos en los que consideres que el rendimiento tiene que se algo clave (como algún proceso batch que al escribir SQL lo podes ajustar y optimizar a gusto).

    Bueno, si algo no queda en claro o tenes otra duda no dudes en consultar.
    Saludos!!

    Me gusta

    • Gracias nuevamente! No se me habia ocurrido lo de pasar de Code First a Database First como mencionas cuando el proyecto se encuentre maduro o sean muchas las personas…tiene mucho sentido. Yo hasta ahora siempre habia comenzado por la BD, dado mi desconocimiento sobre los ORM, pero desde que me puse a analizar un poco esto de EF, descubrí que pensar en un desarrollo orienteado a objetos es mucho mas optimo, sobre todo a la hora de trabajar con herencia, algo que en el mundo relacional no existe y se hace un poco engorroso a la hora de implementarla; me parece que este es un tema que podrias considerar a futuro, las tres formas de mapear una estructura de Herencia a tablas: TPC, TPH y TPT.

      Un abrazo!

      Me gusta

  3. Despues de implementar esto, como se actualiza posteriormente ya que al agregar una nueva columna no me actualiza el proyecto de entitites, es necesario lo agregue de manera manual?

    Me gusta

    • Hola!
      Disculpa por la demora en la respuesta.
      La idea es que al modificar las tablas no debas modificar nada de forma manual, ya que el comportamiento debería ser el mismo que si lo tenes dentro del mismo proyecto. Cuando ejecutas la opción “Ejecutar herramienta personalizada” del archivo .tt de tu proyecto de datos no te realiza ningún cambio sobre las clases correspondientes?
      Saludos!

      Me gusta

    • Hola Fer.
      El principal uso está asociado a la reutilización de dichas clases bajo distintos esquemas como pueden ser:
      * Arquitectura N-capas, compartiendo estas entidades entre distinta capas de la aplicación.
      * Exportar una DLL con todas tus entidades de tipo POCO para compartir con otra aplicación.

      Básicamente será en los casos donde necesites reutilización de código, utilizando estas clases desde distintos proyectos los cuales no tienen nada que ver (y por ende no deben conocer) la capa de acceso a datos y sus particularidades (el ejemplo más claro sería que compartirías el DataContext con todos, lo cual no corresponde).

      Saludos!

      Me gusta

      • Buena explicación.

        te comento estoy haciendo un app en .net con linq to sql que me genera el DataContext y luego creo clases parciales de las entidades que son las tablas de sql y le agrego los metodos como por ejemplo: agregar a la bd o modificar y tambien lo que ya es logica de negocio.

        mi intención ahora es organizar mejor el proyecto, la idea es que en la solución tenga 4 proyectos, uno seria de acceso a los datos que ahi pondria DataContext y clases con consultas linq to sql no?.
        Luego en la capa de entidades separo unicamente lo que tengo generado en el DataContext.
        En el proyecto de negocio colocaria los calculos referidos a cada clase y la transformacion de los metodos que hay en la capa de acceso a datos no?
        y por ultimo en el proyecto de presentacion, los formularios, que por lo general me queda mucho codigo cuando en teoria deberia crear objetos y metodos unicamente no?

        Desde ya muchas gracias y como veras no tengo experiencia con la arquitectura en N capas pero quisiera aprender.

        Abrazo y saludos desde Corrientes!

        Me gusta

  4. Hola Diego, en el modelo Database first, los Dataannotantions, por ejemplo de [Required], donde irían?… ahí que separar la entidades del Datacontext? Y a estás entidades agregarles las dataannotantions? O crear un 3er proyecto para mapear a DTO’s y poder agregar ahí las Dataannotantions, estoy confundido, que sería lo mejor?

    Me gusta

    • Hola Jorge!
      La recomendación es siempre separar las capas, por lo que no deberían estar en la parte de acceso a datos.
      Dependiendo de las capas que tenga tu aplicación, una alternativa es aplicarla sobre una clase en la capa web (en Models por ejemplo). Allí tendrás todas las validaciones asociadas a ese modelo (tanto DataAnnotations como validaciones de negocio propias).
      Si ya tenes una arquitectura n-capas podrías hacerlo en la capa de Entidades o Contratos, entonces tenes definido cómo aplicar la lógica de validación para una determinada acción, y de forma independiente a lo que es tu capa Web.
      Ambas son válidas, siempre y cuando tengamos separadas las responsabilidades .

      Cualquier otra duda me avisas, saludos!

      Me gusta

      • Gracias Diego por responder!, ok, entiendo que por mejores practicas es recomendable, separar las entidades del acceso a datos, esto se convertiria en Datos en una capa(Proyecto), y Entidades en otra capa(Proyecto), ahora si DataContext que hereda de DbContext, sigue permaneciendo en Datos, cuando yo quiero crear mi controlador, indico que mi Modelo, esta en entidades, y mi DataContext esta en Datos, pero para esto debo seguir dejando la referencia a Datos para el DataContext, y agregar una nueva referencia a Entidades. En este especifico caso, cual sería la ventaja de separar la entidades de datos, y otro pregunta, para los Dataanotations, si los agrego a entidades, cada que actualize mi edmx, se perderian las Dataannotations?, o como bien dices, en la carpeta Models, de mi apliación, crear una clase por ejemplo: Cliente.cs que herede de mi clase Cliente que esta en Entidades, y ahi agregar los Dataannotations?, estoy con la incertidumbre de como debería trabajar, que me aconsejas?

        Me gusta

        • Ahí hay un par de cosas a aclarar. La primera es que si vas por una arquitectura en capas (en mi opinión usaría este esquema, pero nada es absoluto) tendrías los siguientes proyectos:
          – Datos
          – Servicios
          – Contratos (o Entidades)
          – Web

          Donde:
          – Servicios tiene la referencia de Datos y Entidades
          – Web tiene la referencia de Servicios y Entidades

          De esta forma, en tu lógica de controlador no estás referenciando a al DataContext sino que a un método de la capa de servicios (el cual recibe como parámetro o retorna una entidad del proyecto de Contratos). De esta forma los controladores se encargan de ser un nexo en el intercambio de información, pero no tienen la lógica de negocio de tu aplicación. Esto permitiría que a futuro puedas cambiar el proyecto ejecutable (ej.: WebApi, app de escritorio, etc.) y la lógica clave ya estará desarrollada y funcionando.

          Igualmente, suponiendo que no vas por este esquema y solo tienes una o dos capas (Web y Datos) las validaciones tampoco irían sobre el modelo de datos sino sobre clases de modelos que tengan a lógica propia de la validación, y tengan las propiedades de lo necesario para interactuar con el usuario (tanto recibir información de formularios como mostrar información). Tampoco haría herencia, ya que estaría acoplando mucho las capas y no es lo que en general se recomienda.

          Como conclusión/resumen, te diría que lo importante es mantener las capas separadas. Si te parece que lo primero es mucho para arrancar, usa un esquema con menos capas pero separando responsabilidades: una clase Cliente.cs de Models para bindear en el Post de un formulario y validar según las reglas de negocio y por separado la clase Cliente.cs mapeada de la BD. Luego deberás interactuar con la conversión de una a otra para el guardado/recuperación de la información.

          Saludos!

          Me gusta

          • Excelente Diego, muchas gracias por regalarme un poco de tu tiempo para resolver mis inquietudes me ha sido de gran ayuda, ahora la conversión entre clases de las que estan en Models contra la mapeadas de BD, donde debería hacerla?, en el Servicio?, en el controlador?, esto lo harias por medio de linq?, o quizas con el automapper?, que me aconsejas?, Saludos y te felicito por tu trabajo!

            Me gusta

            • Gracias a vos!
              Si tenes la capa de servicios podes hacerlo allí mismo, sino en el controlador. Igual tene en cuenta para donde hacerlo las capas visibles en cada momento (desde el servicio no vas a ver los Models de Web, y desde el controlador las clases mapeadas de la BD, esto si tenes todas las capas).

              Podes usar alguna librería de Mapper automático si lo necesitas, o hacerte algunos métodos para tener allí esa lógica. Depende del tamaño de tu aplicación y lo complejo de las entidades.

              Saludos!

              Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s