6

tengo IQueryable cuyo marco 4 objetos Entidad quisiera proyectar a sus equivalentes de DTO. Uno de esos objetos 'Persona' es una clase EF4, y el POCO PersonP correspondiente es una clase que he definido. Estoy usando Automapper para mapear entre ellos. Sin embargo, cuando intento el siguiente código:IQueryable Lambda proyección Sintaxis

IQueryable<Person> originalModel = _repo.QueryAll(); 
IQueryable<PersonP> projection = originalModel.Select(e => Mapper.Map<Person, PersonP>(e)); 

La proyección genera este error en tiempo de ejecución:

LINQ to Entities does not recognize the method 'TestSite.Models.PersonP Map[Person,PersonP](TestSite.DataLayer.Model.Person)' method, and this method cannot be translated into a store expression. 

¿Cuál es la sintaxis adecuada para crear una proyección IQueryable<PersonP> usando AutoMapper? Gracias.

P.S. AutoMapper está configurado correctamente - lo uso en otros lugares para convertir de ida y vuelta entre la persona y PersonP, es decir Mapper.Map<Person, PersonP>(myPersonObject) correctamente devuelve un objeto PersonP.

EDITAR (más código):

estoy usando esto para una función auxiliar para unir EF4 Entidad POCOs (PersonP) a una red de Telerik - que no serializar las propias entidades adecuadamente ya que contienen circular referencias (es decir, propiedades de navegación). Mi código es el siguiente:

public static GridModel GetGridModel<TEntity, TPoco>(IRepository<TEntity> repo, GridState gridState) where TEntity : EntityObject 
{ 
var originalModel = repo.QueryAll().ToGridModel(gridState); 
var projection = originalModel.Select(e => Mapper.Map<TEntity, TPoco>(e)); 


return projection.ToGridModel(gridState); // applies filters, sorts, pages, etc... 
} 

el método .ToGridModel es un método de extensión en IQueryable y devuelve un objeto complejo que no puedo analizar con fiabilidad - por lo que este me lleva a creer que tenga que realizar el filtrado después de haber hecho la proyección a POCOs.

ACTUALIZACIÓN 2:

Tratando de simplificar las cosas, me hizo un método no genérico como esto:

public static GridModel GetGridModel2(IRepository<Client> repo, GridState gridState) 
{ 
IQueryable<Client> originalModel = repo.QueryAll(); 
IQueryable<ClientP> projection = originalModel.Select(c => ClientToClientP(c)); 

return projection.ToGridModel(gridState); 
} 

private static ClientP ClientToClientP(Client c) 
{ 
return new ClientP { Id = c.Id, FirstName = c.FirstName }; 
} 

Este código también falla al crear la proyección. Noto que IQueryable.Select() tiene múltiples sobrecargas: Expression> es uno de ellos. ¿Podría representar esta función/delegar llamada usando una de estas sobrecargas?

Respuesta

5

¿Cuál es la sintaxis adecuada para crear una proyección IQueryable utilizando Automapper?

No hay ninguna. Automapper doesn't do this. Es la herramienta incorrecta para este trabajo.

Se podría crear una herramienta AutoMapper similar a hacer una cosa similar para las proyecciones de la consulta. Lo he considerado en el pasado, pero siempre concluí que el código que lo usa sería menos legible que la proyección. No quiero optimizar el tiempo de escritura del código sobre el tiempo de lectura del código.

Su código actualizado no funciona porque no es una expresión. Si lo hace:

private static Expression<Func<Client, ClientP>> ClientP ClientToClientP() 
{ 
    return c => new ClientP { Id = c.Id, FirstName = c.FirstName }; 
} 

...y luego:

IQueryable<Client> originalModel = repo.QueryAll(); 
Expression<Func<Client, ClientP>> exp = ClientToClientP(); 
IQueryable<ClientP> projection = originalModel.Select(exp); 

... entonces funcionará.

+0

Esto falla incluso con un método regular en lugar de AutoMapper. Por favor, mire UPDATED2 en mi publicación. – Harper

+0

Ver la actualización. Las no expresiones no se pueden traducir a SQL. –

+0

Gracias por su ayuda. Su código funciona (error tipográfico corregido en el método sig) pero como mencionó, AutoMapper no es adecuado para este propósito y no funciona. ¿Podría explicar la razón técnica por la que no puedo sustituir el nuevo ClientP {Id = ..} por un mapeo de AutoMapper, ya que ambos devuelven un objeto ClientP? – Harper

2

Si agrega .ToList() antes del Seleccionar, puede forzar el mapeo a pasar del lado del cliente (Linq a Objetos) en lugar de al lado del servidor (SQL). Solo asegúrate de haber hecho tu filtrado primero para que no traigas toda la mesa.

+0

Bueno, en realidad estoy usando un método de extensión Telerik para realizar el filtrado/agrupamiento/clasificación/etc. y no puedo aplicar esto al EF Queryable porque el Grid no puede serializar dependencias circulares en esas Entidades. He agregado más código. – Harper

+0

Puede mapear las entidades utilizando tipos anónimos: http://msdn.microsoft.com/en-us/library/bb738512.aspx, pero eso le resta importancia a sus entidades Poco. Parece que el verdadero problema aquí es la llamada al método monolítico 'ToGridModel'. Eso realmente necesita dividirse para que pueda filtrar y clasificar, luego proyectar, y luego hacer todo lo que necesite hacer. ¿Puede aplicar los filtros por separado, luego cruzar el límite, luego proyectar y ensamblar el GridModel? –

+0

Gracias por sus sugerencias. Investigaré si puedo eludir el método ToGridModel(). – Harper

11

En realidad, esto ha sido resuelto ahora por el autor de AutoMapper aquí: http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/

Se proporciona una implementación que tiene una proyección deseada la consulta y consulta la base de datos para sólo los campos necesarios para el mapeo de proyección.

Además, consulte el follow-on article de Paul Hiles para obtener algunas mejoras de almacenamiento en caché.

Espero que esto ayude.

+0

Muy útil, gracias. –

+0

Documentos oficiales de AutoMapper sobre esto: https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions –