Entity Framework con grandes sistemas: ¿cómo dividir los modelos?

50

Estoy trabajando con una base de datos de SQL Server con más de 1000 tablas, otros cientos de vistas y varios miles de procedimientos almacenados. Estamos buscando comenzar a usar Entity Framework para nuestros proyectos más nuevos, y estamos trabajando en nuestra estrategia para hacerlo. Lo que me preocupa es la mejor manera de dividir las tablas en diferentes modelos (EDMX o DbContext si vamos a codificar primero). Puedo pensar en algunas estrategias desde el principio:

  • Dividido por esquema
    Tenemos nuestras tablas divididas en probablemente una docena de esquemas. Podríamos hacer un modelo por esquema. Sin embargo, esto no es perfecto, porque dbo todavía termina siendo muy grande, con más de 500 tablas / vistas. Otro problema es que ciertas unidades de trabajo terminarán teniendo que realizar transacciones que abarcan múltiples modelos, lo que se suma a la complejidad, aunque supongo que EF lo hace bastante sencillo.
  • Dividir por intención
    En lugar de preocuparse por los esquemas, divida los modelos por intención. Por lo tanto, tendremos diferentes modelos para cada aplicación, proyecto, módulo o pantalla, dependiendo de qué tan granulares queramos obtener. El problema que veo con esto es que hay ciertas tablas que inevitablemente deben usarse en todos los casos, como User o AuditHistory. ¿Los agregamos a cada modelo (creo que viola DRY), o son aquellos en un modelo separado que es utilizado por cada proyecto?
  • No se divida en absoluto: un modelo gigante
    Esto es obviamente simple desde una perspectiva de desarrollo, pero desde mi investigación y mi intuición parece que podría funcionar terriblemente, tanto en tiempo de diseño, tiempo de compilación y posiblemente tiempo de ejecución.

¿Cuál es la mejor práctica para usar EF en una base de datos tan grande? Específicamente, ¿qué estrategias utilizan las personas para diseñar modelos contra este volumen de objetos DB? ¿Hay opciones en las que no estoy pensando que funcionen mejor que las que tengo arriba?

Además, ¿es esto un problema en otros ORM como NHibernate? Si es así, ¿han encontrado alguna solución mejor que EF?

RationalGeek
fuente
"tener que hacer transacciones que abarcan varios modelos, lo que aumenta la complejidad" Solo una nota aquí que necesitará habilitar el Coordinador de transacciones distribuidas de Microsoft. Una vez que tenga eso en funcionamiento, debería ser simple lograr lo que habla.
Tjaart
@Tjaart gracias. He usado MS DTC antes y, si bien es bastante simple, agrega complejidad más allá de un simple DB txn, por lo que quiero evitarlo siempre que sea posible.
RationalGeek
2
4 años después, ¿qué decidiste y qué recomendarías ahora?
Rory

Respuestas:

31

Personalmente, he intentado hacer un gran esquema para todas mis entidades en un proyecto bastante complejo pero pequeño (~ 300 tablas). Teníamos una base de datos extremadamente normalizada (normalización de quinta forma (lo digo de manera general)) con muchas relaciones "muchas a muchas" y una aplicación extrema de la integridad referencial.

También utilizamos una estrategia de "instancia única por solicitud" que tampoco estoy convencido de que haya ayudado.

Cuando se realizaban listas simples, razonablemente planas "explícitamente definidas", las búsquedas y los ajustes de rendimiento generalmente eran aceptables. Pero cuando comenzamos a profundizar en las relaciones profundas, el rendimiento pareció tener drásticas caídas. En comparación con un proceso almacenado en este caso, no hubo comparación (por supuesto). Estoy seguro de que podríamos haber modificado la base del código aquí y allá para mejorar el rendimiento, sin embargo, en este caso solo necesitábamos un aumento del rendimiento sin análisis debido a limitaciones de tiempo, y volvimos al proceso almacenado (aún lo mapeé a través de EF, porque EF proporcionó resultados fuertemente tipados), solo necesitábamos eso como un retroceso en algunas áreas. Cuando tuvimos que recorrer toda la base de datos para crear una colección (usando .include () sin disimulo), el rendimiento fue notablemente degradante, pero tal vez estábamos pidiendo demasiado ...

Entonces, según mi experiencia, recomendaría crear un .edmx separado por intento. Solo genere lo que usará en función del alcance de esa necesidad. Es posible que tenga algunos archivos .edmx de ámbito más pequeño para tareas específicas, y luego algunos grandes donde necesite atravesar relaciones complejas para construir objetos. No estoy seguro de dónde está ese lugar mágico, pero estoy seguro de que hay uno ... jaja ...

Honestamente, sin embargo, aparte de algunos escollos que vimos venir (travesía compleja), el enorme .edmx funcionó bien desde una perspectiva "funcional". Pero tendrá que estar atento a la magia de "reparación" que hace el contexto detrás de la escena si no la deshabilita explícitamente. Además de mantener el .edmx sincronizado cuando se realizan cambios en la base de datos ... a veces era más fácil borrar toda la superficie y volver a crear las entidades, lo que tomó como 3 minutos, por lo que no fue un gran problema.

Todo esto fue con EntityFramework 4.1. También me interesaría saber sobre su elección final y su experiencia.

Y con respecto a su pregunta sobre nHibernate, esa es una pregunta de lata de gusanos, en mi opinión, ladrará a ambos lados de la cerca ... Escucho que mucha gente golpea a EF por golpear sin tener que pasar por el desafíos y comprensión de los matices exclusivos de EF en sí ... y aunque nunca he usado nHibernate en la producción, en general, si tiene que crear manual y explícitamente cosas como mapeos, obtendrá un control más finito si puedo arrastrar y soltar, generar y comenzar a CRUD y hacer consultas usando LINQ, podría dar una mierda sobre la granularidad.

Espero que esto ayude.

hanzolo
fuente
1
FYI: hay una utilidad de mapeo de NHibernate que hace que estas asignaciones sean MUY fáciles y automáticas.
deslumbra el
@ganders: ¿tiene una interfaz de usuario y cómo es la integración IDE? ¿Supongo que lo señala a una fuente de datos y respeta la integridad referencial y el recorrido del objeto y crea los objetos de mapeo?
hanzolo
1
Sí, lo hace (GUI). No he tenido ningún problema hasta ahora. Lo usé en 4 o 5 proyectos / sitios web diferentes. Nota: Lo uso con el NHibernate fluido, que hace el mapeo en código c #, no en archivos config / xml. Aquí hay un enlace: nmg.codeplex.com
gansos
13

Permítanme comenzar con una simple aclaración: no tengo experiencia con una base de datos tan grande, por lo que el resto de mi respuesta no se basa en el ejemplo del mundo real.

Entonces tiene una base de datos GRANDE y quiere usarla con ORM / EF. Yo iría con la segunda opción. Aquí está mi explicación simple por qué:

  • El mapeo agrega complejidad. No es necesario agregar complejidad con las entidades que su aplicación / proyecto / módulo actual nunca necesita, pero no haga que la granularidad sea de un nivel demasiado bajo. Tener un mapeo separado por pantalla no te ayudará también.
  • Desea lograr la unidad de trabajo. Debería poder especificar qué módulo de tablas necesita en la mayoría de los casos (no es necesario en todos los casos). Si coloca estas tablas en un conjunto de mapeo único, podrá manejar la lectura y la modificación de datos por instancia de contexto único, que es lo que debería ser su objetivo final.
  • No estoy seguro de qué quiere decir exactamente con modelo, pero incluso con diferentes conjuntos de mapeo puede compartir clases entre conjuntos de mapeo utilizando los mismos tipos de entidad. Entonces, si usa la tabla de usuario en dos módulos, no necesita dos clases de usuario para representar lo mismo. Todavía puede usar una sola tabla y, en el caso de la asignación de código (también conocido como código primero), incluso puede definir la asignación una vez y cargarla en múltiples conjuntos de asignación para que el principio DRY no se viole pero el enfoque de código primero tiene más limitaciones cuando se trata a vistas y procedimientos almacenados. EDMX hace esto más difícil. Todavía puede reutilizar clases, pero reutilizar el mapeo es imposible.
  • ¿Qué pasa con las consultas de módulos cruzados? Estas consultas pueden ocurrir, pero para ser sincero, no todo debe ser manejado por EF. Puede aprovechar EF para casos comunes para simplificar el acceso regular a los datos, pero si tiene alguna necesidad de una consulta especial que combine tablas que pertenecen a 5 módulos diferentes, simplemente puede ejecutarla directamente o envolverla en un procedimiento almacenado. El reemplazo al 100% del acceso a datos nativos puede ser difícil, complejo y contraproducente.
  • El último punto es simplemente práctico: no creo que las herramientas VS estén listas para trabajar con un conjunto de objetos tan grande, ni en el diseñador, ni siquiera con la herramienta de importación. Solía ​​trabajar en bases de datos muy grandes con acceso a datos tradicionales y proyecto de base de datos SQL en VS2008: la experiencia del usuario con un proyecto complejo fue muy mala. Debe mantener baja la cantidad de tablas usadas: el límite para el diseñador debe estar entre 100 y 200, pero incluso 100 tablas manejadas por contexto único (conjunto de mapeo) suena como demasiada responsabilidad para una clase (suponga que tendrá 100 propiedades de conjunto expuesto en el contexto, no parece un buen diseño).
Ladislav Mrnka
fuente
4

Diría que no puede decidir este tipo de preguntas desde una perspectiva técnica. Le recomendaría que construya su arquitectura en función de sus casos de uso (historias de usuarios, etc.). Primero encuentre sus objetos comerciales. Un objeto de entidad no es por defecto un objeto comercial. Típicamente tendrá un objeto comercial frente a los objetos de la entidad. Luego, puede decidir de forma incremental lo que realmente necesita, en función de los requisitos del usuario.

"Un buen arquitecto maximiza el número de decisiones que no se toman". Robert C. Martin

http://cleancoder.posterous.com/architecture-deference

ollins
fuente
3

Utilizo un enfoque híbrido: EF maneja las cosas de OLTP, mientras que las operaciones pesadas, como las inserciones por lotes, las actualizaciones masivas, las consultas de informes, etc., son manejadas por Stored Procs. También facilita la ruta de migración si no está haciendo una reescritura completa de su capa de datos de una sola vez.

Nik
fuente
Esto parece una buena estrategia, pero en realidad no aborda la cuestión de cómo dividir entidades en diferentes modelos de EF. ¿Tiene todas las entidades en un modelo o divide y vence de alguna manera?
RationalGeek
1
Si el rendimiento de OLTP es suficiente con el enfoque de modelo completo, vaya con eso. Siempre puede dividirlo más tarde si es necesario, pero la forma más rápida y ágil es cargar todo. Es posible que nunca necesite las ganancias de rendimiento que obtiene al dividirlo, por lo que desperdiciará tiempo y hará que su sistema sea más complicado sin ninguna razón. Luego está la cuestión de a qué modelo pegaría una nueva tabla / entidad cuando decida expandir. Y qué sucede cuando necesita ejecutar una actualización en varios modelos. Ahórrate el dolor de cabeza a menos que realmente no tengas una alternativa.
Nik
Olvidé mencionar que siempre puedes modificar tu rendimiento al acceder a tus datos. Observe las opciones de carga lenta / ansiosa y las entidades secundarias que está trayendo. No veo ninguna razón por la cual un modelo completo se comportaría peor que uno más pequeño si no está cargando árboles de objetos masivos.
Nik
yo diría árboles de objetos masivos y una estructura de datos normalizado van mano a mano cuando se trata de grandes del esquema
hanzolo
Usted controla cuán poco o cuánto desea saturar el gráfico del objeto.
Nik