2010-10-13 21 views
11

Tenemos un sitio ASP.NET MVC que utiliza abstracciones de Entity Framework con los patrones Repository y UnitOfWork. Lo que me pregunto es cómo otros han implementado la navegación de gráficos de objetos complejos con estos patrones. Déjeme darle un ejemplo de uno de nuestros controladores:Patrón para recuperar gráficos de objetos complejos con Patrón de repositorio con Entity Framework

var model = new EligibilityViewModel 
    { 
     Country = person.Pathway.Country.Name, 
     Pathway = person.Pathway.Name, 
     Answers = person.Answers.ToList(), 
     ScoreResult = new ScoreResult(person.Score.Value), 
     DpaText = person.Pathway.Country.Legal.DPA.Description, 
     DpaQuestions = person.Pathway.Country.Legal.DPA.Questions, 
     Terms = person.Pathway.Country.Legal.Terms, 
     HowHearAboutUsOptions = person.Pathway.Referrers 
    }; 

Es un proceso de registro y prácticamente todo lo que cuelga de la clase Persona POCO. En este caso, almacenamos en caché a la persona a través del proceso de registro. Ahora comencé a implementar la última parte del proceso de registro, que requiere acceso a datos más profundos en el gráfico de objetos. Específicamente datos de DPA que cuelgan dentro de País.

El código anterior está simplemente mapeando la información del modelo en un formato más simple para ViewModel. Mi pregunta es: ¿Consideras que esta navegación bastante profunda de las buenas prácticas de los gráficos o abstractas la recuperación de los objetos que se encuentran más abajo en los repositorios?

Respuesta

14

En mi opinión, la pregunta importante aquí es - ¿Ha desactivado LazyLoading?

Si no ha hecho nada, está activado por defecto.

Así que cuando haga Person.Pathway.Country, invocará otra llamada al servidor de la base de datos (a menos que esté haciendo una carga ansiosa, de la que hablaré en un momento). Dado que estás usando el patrón Repositorio, este es un gran no-no. Los controladores no deben causar llamadas directas al servidor de la base de datos.

Una vez que un C ontroller ha recibido la información de la Odel M, debe estar listo para hacer proyección (si es necesario), y pasar a la V IEW, no va volver a la M odel.

Por eso, en nuestra aplicación (también utilizamos repositorio, EF4, y la unidad de trabajo), que deshabilitar la carga perezosa, y permitir el paso a través de las propiedades de navegación a través de nuestra capa de servicio (una serie de "Incluir "declaraciones, más dulces por enumeraciones y métodos de extensión).

A continuación, eager-load estas propiedades como los controladores lo requieren. Pero lo importante es que el Controlador debe solicitarlos explícitamente.

Lo que básicamente le dice a la interfaz de usuario: "Oye, solo recibes información básica sobre esta entidad. Si quieres algo más, pregúntalo".

También tenemos Service Layer que media entre los controladores y el repositorio (nuestros repositorios devuelven IQueryable<T>). Esto permite que el repositorio salga del negocio de manejar asociaciones complejas. La carga ansiosa se realiza en la capa de servicio (y cosas como paginación).

El beneficio de la capa de servicio es simple: más acoplamiento libre. El Repositorio maneja solo Agregar, Eliminar, Buscar (que devuelve IQueryable), la Unidad de trabajo maneja el "newing" de CD y el Compromiso de cambios, la Capa de servicio maneja la materialización de entidades en colecciones concretas.

Es un buen enfoque 1-1 de pila:

personService.FindSingle(1, "Addresses") // Controller calls service 
| 
--- Person FindSingle(int id, string[] includes) // Service Interface 
     | 
     --- return personRepository.Find().WithIncludes(includes).WithId(id); // Service calls Repository, adds on "filter" extension methods 
      | 
      --- IQueryable<T> Find() // Repository 
       | 
       -- return db.Persons; // return's IQueryable of Persons (deferred exec) 

No hemos conseguido hasta la capa MVC todavía (que estamos haciendo TDD), pero una capa de servicio podría ser otro lugar podrías hidratar las entidades centrales en ViewModels. Y de nuevo, correspondería al controlador decidir cuánta información desea.

De nuevo, se trata de acoplamiento suelto. Sus controladores deben ser lo más simple posible, y no tener que preocuparse por asociaciones complejas.

En términos de cuántos repositorios, este es un tema muy debatido. A algunos les gusta tener uno por entidad (sobretodo si me preguntas), algunos prefieren agrupar según la funcionalidad (tiene sentido en términos de funcionalidad, es más fácil trabajar con ellos); sin embargo, tenemos uno por raíz agregada.

Solo puedo adivinar en su Modelo que "Persona" debería ser la única raíz agregada que pueda ver.

Por lo tanto, no tiene mucho sentido tener otro repositorio para manejar "Caminos", cuando un camino siempre está asociado con una "Persona" en particular. El repositorio de personas debe manejar esto.

Otra vez - tal vez si protegió su EDMX, podríamos darle más consejos.

Esta respuesta puede extenderse un poco demasiado en función del alcance de la pregunta, pero pensé que daría una respuesta en profundidad, ya que estamos lidiando con este escenario exacto en este momento.

HTH.

+0

Gracias, su respuesta ciertamente ha aclarado mi pensamiento. Las respuestas hasta ahora me han ayudado a darme cuenta de que cuando miro mi código me preocupa lo que EF está haciendo detrás de escena para realizar estos gráficos de objetos. Sé que puedo utilizar SQL Profiler para ver qué está sucediendo, pero mi pensamiento inicial es que extraerlo del controlador me dará un mejor control en el futuro si el rendimiento o el requerimiento de entregar estos datos a fuentes remotas se convierte en un requisito. Su sugerencia de una capa de servicio entre los controladores y repositorios es algo que voy a analizar. –

+0

@Daz Lewis - definitivamente. Tenemos una API para nuestro sitio web. Es por eso que tenemos la capa de servicio. Tanto nuestro sitio web como API son "clientes". Esto permite un modelo de programación agradable, fluido (pero ajustado).Por cierto, no TIENES que usar el Analizador de SQL. Hay un método en ObjectQuery llamado .ToTraceString. Usamos esto con el registro, todos nuestros métodos de repositorio depuran esta línea (nivel de información), para que podamos ver lo que se está ejecutando. De todos modos, definitivamente mira en la capa de servicio. Nuestros repositorios son muy simples, eche un vistazo a algunas de mis preguntas para ver cómo lo hice. – RPM1984

+0

"Los controladores no deben causar llamadas directas al servidor de la base de datos", luego de esto su controlador no debe llamar a un repositorio para causar llamadas directas al servidor de la base de datos. Cuando el acceso a una propiedad que se va a cargar de forma diferida no provoca una llamada directa al servidor de la base de datos de la misma forma que su repositorio está causando una llamada directa al servidor de la base de datos. Sí, seguro que causa una llamada al servidor de la base de datos, pero eso es indirectamente a través de su infraestructura usando una herramienta ORM. – adriaanp

3

Depende de la cantidad de información que está utilizando en un momento dado.

Por ejemplo, si solo desea obtener el nombre de país de una persona (persona.Pathway.Country.Name), ¿de qué sirve hidratar todos los demás objetos de la base de datos?

Cuando solo necesito una pequeña parte de los datos tiendo a sacar lo que voy a utilizar. En otras palabras, proyectaré en un tipo anónimo (o un tipo concreto especialmente hecho si tengo debe tener uno).

No es una buena idea sacar un objeto completo y todo lo relacionado con ese objeto cada vez que quiera acceder a algunas propiedades. ¿Qué sucede si estás haciendo esto una vez cada postback, o incluso varias veces? Al hacer esto, es posible que le haga la vida más fácil a corto plazo a costa de hacer que su aplicación sea menos escalable a largo plazo.

Como dije al principio, no hay una regla única para esto, pero diría que es raro que necesite hidratar tanta información.

Cuestiones relacionadas