2011-03-22 31 views
5

Quiero usar AutoMapper para construir un ViewModel (aplanamiento - proyección de datos) para usar en una aplicación MVC de ASP.net.Proyección de datos en Entity Framework y Automapper

var tmp = from x in db.Mailings select Mapper.Map<Mailing, MailingViewModel>(x); 
return View(tmp.ToList()); 

Por supuesto, cuando trato de la muestra anterior, me sale el error EF "LINQ a Entidades no reconoce el método ... método, y este método no se puede traducir en una expresión tienda."

sé que es posible mover el .ToList() antes de la AutoMapper hace su magia, pero luego voy a buscar todos los campos de la BD (y sólo necesitan 3 de 20 campos)

¿Es posible usa eso de una manera limpia. Limpiar = No se obtienen todos los campos de la base de datos, pero solo los campos necesarios para ViewModel. ¿Es posible en Automapper? ¿O tal vez una otra biblioteca? (Sin hacerlo de forma manual;))

+0

Es bastante "peligrosa" para usar AutoMapper en las proyecciones, porque si usted tiene una operación de aplanamiento en el mapa ('target.Prop1 = source.Ref1.Prop1') que MIG ht crea un escenario N + 1. –

Respuesta

9

Sí, esto es muy posible. Ver aquí http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code

Editar: Recientemente he encontrado que la base de esto ya existe en AutoMapper. agregue una instrucción using para AutoMapper.QueryableExtensions y se le proporciona una extensión IQueryable llamada Project <>()

+0

¡Buena solución! Por lo general, es mejor hacer que su respuesta sea un poco más explicativa, especialmente para situaciones en las que el enlace provisto dejará de funcionar. –

+0

Ayer vi la publicación del blog también. ¡Solo lo que estaba buscando! –

+0

Muy bien, como yo entiendo, no enumera. Si es así, eso es realmente lo que necesito. –

4

Usted sólo tiene que llamar:

var tmp = from x in db.Mailings 
      select new MailingViewModel 
      { 
       FirstName = x.FirstName, 
       LastName = x.LastName, 
       Address = x.Address 
      }; 

que no es necesario para la proyección AutoMapper sencilla si accede a EF directamente en el controlador.

No puede involucrar a AutoMapper en la consulta de linq-to-entities - de ninguna manera. Debe devolver la entidad (u otro objeto proyectado) y asignarla por AutoMapper o usar proyección simple sin AutoMapper.

+0

Sí, pero soy flojo;) No, en serio; Tengo curiosidad si se puede hacer automáticamente ... (este ejemplo solo tiene un par de campos, pero hay situaciones con +20 campos donde los campos provienen de varias combinaciones, etc ...) –

+0

Actualicé mi respuesta. –

+0

Eso no es técnicamente correcto, puedes hacer esto. Consulte http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code – brentmckendrick

2

Esto se debe a la forma en que linq interactúa con IQueryableProviders (creo que esta es la interfaz).

Entonces, lo que está sucediendo es que Linq se compila en un árbol de expresiones que el proveedor de linq subyacente lee e intenta convertir a sql. El proveedor de linq no tiene idea de cómo traducir Mapper.Map<> en SQL, de ahí el error.

Para un buen vídeo sobre cómo LINQ proveedores de trabajo salida: http://channel9.msdn.com/Shows/Going+Deep/Erik-Meijer-and-Bart-De-Smet-LINQ-to-Anything

2

Usted debe ser capaz de hacer esto utilizando AutoMapper de DynamicMap. Creo que algo como el siguiente umbral resolverá tu problema si realmente quieres usar AutoMapper, aunque en este caso particular estoy de acuerdo con Ladislav Mrnka.

var tmp = from x in db.Mailings 
      select new 
      { 
       FirstName = x.FirstName, 
       LastName = x.LastName, 
       Address = x.Address 
      }; 

return View(tmp.ToList().Select(item => Mapper.Map<MailingViewModel>(item))); 

Por desgracia, si desea limitar las columnas que estés regresan de la base de datos, tiene que especificar cuáles desea, lo que hace frustrar el propósito de AutoMapper en este escenario. Sin embargo, esta sería una extensión AutoMapper realmente ordenada, para tomar el tipo de destino y crear dinámicamente una expresión de selección basada en las propiedades del tipo.

+0

La extensión que sugiere de hecho sería la solución que estoy buscando. Lástima que mi conocimiento del árbol de expresiones sea bastante limitado :) –

+0

En realidad, he creado el método de extensión al que se refiere y funciona ... hasta cierto punto. Si intenta llenar una entidad que tiene una ICollection/List/array, etc. como una propiedad (y también desea materializar una proyección de esas entidades), entonces se rompe porque el proveedor de SQL no le permite llamar a ToList () dentro de una consulta. He escrito sobre este tema aquí http://refactorthis.wordpress.com/2012/07/20/entity-framework-tolist-in-nested-type-linq-to-entities-does-not-recognize-the- método/Si simplemente solucionan este problema, todos podemos escribir proyecciones de una línea aprovechando automapper – brentmckendrick

1

desde AutoMapper no funciona directamente a la base de datos (necesidad de transformar al objeto en memoria antes de dirigirse a ella), he escrito propia clase simple para copiar propiedades idénticas:

código inicial:

model.Sales = _dbcontext.Sales.Where(o => o.PartnerId == PartnerId && (o.SaleDate > model.BeginDate || model.BeginDate == null) && (o.SaleDate <= model.EndDate || model.EndDate == null)).Select(o => new SaleViewModel 
     { 
      NumberIn1S = o.NumberIn1S, 
      Total = o.Total, 
      SaleDate = o.SaleDate, 
      Comments = o.Comments, 
      Driver = o.Driver, 
      GuidIn1S = o.GuidIn1S 
     }).OrderByDescending(o => o.SaleDate).ToList(); </p> 

MyMapper:

model.Sales = _dbcontext.Sales.Where(o => o.PartnerId == PartnerId && (o.SaleDate > model.BeginDate || model.BeginDate == null) && (o.SaleDate <= model.EndDate || model.EndDate == null)).OrderByDescending(o => o.SaleDate).ToArray().Select(p => <b>MyMapper<SaleViewModel>.CopyObjProperties(p, "NumberIn1S,Total,SaleDate,Comments,Driver,GuidIn1S")</b>).ToList(); </p> 
Cuestiones relacionadas