2010-09-08 28 views
6

Tengo una pregunta semi complicada sobre Entity Framework4, expresiones Lambda y Data Transfer Objects (DTO).EF4, Lambda, patrón de repositorio y DTOs

Así que tengo un pequeño proyecto EF4, y siguiendo los principios OO establecidos, tengo un DTO para proporcionar una capa de abstracción entre los consumidores de datos (GUI) y el modelo de datos.

  • VideoDTO = DTO con getters/setters, utilizado por la GUI
  • VideoEntity = Entidad generada por EF4

Mi pregunta gira en torno al uso de la DTO por la interfaz gráfica de usuario (y no tener la La GUI usa la Entidad en absoluto), combinada con la necesidad de pasar una lambda a la capa de datos. Mi capa de datos es un patrón básico de repositorio con Agregar. Cambiar, borrar, Get, GetList, etc. Tratando de poner en práctica un método de búsqueda con una firma de este modo:

public IEnumerable<VideoDTO> Find(Expression<Func<VideoEntity, bool>> exp) 
... 
_dataModel.Videos.Where(exp).ToList<Video>() 
--- 

Mi problema/preocupación es la de "CAD" necesidad de ser del tipo VideoEntity en lugar de VideoDTO. Quiero preservar la separación de las preocupaciones para que la GUI no conozca los objetos de la Entidad. Pero si trato de pasar

Func<VideoDTO, bool> 

No puedo hacer un LINQ Donde en esa expresión usando el modelo de datos real.

¿Hay una manera de convertir un Func<VideoDTO,bool> a un Func<VideoEntity, bool>

Lo ideal sería que mi firma método aceptaría Func<VideoDTO, bool> y de esa manera la interfaz gráfica de usuario no tendría ninguna referencia a la entidad de datos subyacente.

¿Es esto lo suficientemente claro? Gracias por su ayuda


Gracias por las respuestas a los dos.

Voy a probar la idea de definir los criterios de búsqueda en un objeto y usar eso en la expresión LINQ. Empezando con EF4 y L2S, usando esto como un proyecto de aprendizaje.

¡Gracias nuevamente!

Respuesta

0

Quizás su objetivo de diseño sea evitar la propagación de las entidades del modelo de datos al nivel del cliente en lugar de evitar una dependencia entre la capa de presentación y el modelo de datos. Si se ve de esa manera, no habría nada de malo en que la consulta se formara de la forma en que usted indica.

Para ir más lejos, puede exponer los campos de búsqueda de VideoEntity a través de una interfaz (IVideoEntityQueryFields) y usar eso como el tipo en la expresión.

Si no desea agregar una interfaz a sus entidades a continuación, la opción más complicado es el uso de un objeto VideoEntityQuery y algo que se traduce un Expression<Func<VideoEntityQuery,bool>> a un Expression<Func<VideoEntity,bool>>.

1

En arquitecturas como CQRS no hay necesidad de tal conversión, en todo caso, lea & los lados de escritura de la aplicación están separados.

Pero en su caso, no puede escaparse de la traducción.

Antes que nada: debe ser más específico cuando defina repositorios. La firma del repositorio es algo que desea mantener explícito en lugar de genérico.

Ejemplo común para mostrar esta idea: ¿Puede decir qué índices necesita en Su base de datos cuando mira la firma de su repositorio (tal vez mirando la implementación del repositorio, pero ciertamente sin mirar el código del cliente)? No puedes. Porque es demasiado genérico y el lado del cliente puede buscar por cualquier cosa.

En su ejemplo, es un poco mejor expresión genérica de la expresión está vinculada con dto en lugar de entidad.

Esto es lo que hago (usando NHibernate.Linq, pero la idea sigue siendo)

public class Application{ 
    public Project Project {get;set;}  
} 

public class ApplicationRepository{ 
public IEnumerable<Application> Search(SearchCriteria inp){ 
     var c=Session.Linq<Application>(); 
     var q=c.AsQueryable(); 
     if(!string.IsNullOrEmpty(inp.Acronym)) 
     q=q.Where(a=>a.Project.Acronym.Contains(inp.Acronym)); 
     /*~20 lines of similar code snipped*/ 
     return q.AsQueryable(); 
} 
} 

//used by client 
public class SearchCriteria{ 
public string Acronym{get;set;} 
/*some more fields that defines how we can search Applications*/ 
} 

Si Usted quiere mantener sus expresiones, una forma sería definir manualmente el diccionario de la siguiente manera:

var d=new Dictionary<Expression<Func<VideoDTO,object>>, 
        Expression<Func<VideoEntity,object>>{ 
    {x=>x.DtoPropNumberOne,x=>x.EntityPropNumberOne} /*, {2}, {3}, etc.*/ 
}; 

y utilizarla más tarde:

//can You spot it? 
//client does not know explicitly what expressions dictionary contains 
_dataModel.Videos.Where(d[exp]).ToList<Video>(); 
//and I'm not 100% sure checking expression equality would actually work 

Si no desea escribir manualmente el diccionario de mapeo, Y Necesitarás algunas técnicas avanzadas. Una idea sería traducir la expresión dto a cadena y luego volver a la expresión de la entidad. Here son algunas ideas (aunque relacionadas) que pueden ser útiles. Las expresiones son bestias bastante complicadas.

De todos modos, como dije, deberías evitar esto. De lo contrario, producirá un código realmente frágil.