2012-03-27 21 views
9

Creé una pequeña API agradable con ASP.NET Web API, pero supongo que no es correcto devolver las entidades de mi contexto (entidad marco) AsQueryable, así que estoy mapeando todo a los objetos DTO.ASP.NET Web API devuelve DTO cuestionables?

Lo que no entiendo del todo sin embargo: ¿cómo puedo mantener mi contexto consultable, pero aún así solo devuelvo DTO en lugar de entidades? O esto no es posible?

Este es mi código:

public IQueryable<ItemDto> Get() 
{ 
    using (EfContext context = new EfContext()) 
    { 
     Mapper.CreateMap<Item, ItemDto>() 
      .ForMember(itemDto => itemDto.Category, mce => mce.MapFrom(item => item.Category.Name)); 

     IEnumerable<ItemDto> data = Mapper.Map<IEnumerable<Item>, IEnumerable<ItemDto>>(context.Items 
      .OrderByDescending(x => x.PubDate) 
      .Take(20)); 

     return data.AsQueryable(); 
    } 
} 

Como se puede ver que cargar los datos, y hacer que poco consultable colección IEnumerable. El problema es que la consulta que se genera para este fragmento de código probablemente sea bastante ineficiente porque primero carga todos los elementos (o al menos los 20 primeros elementos) y luego filtra el resultado.

Espero haber descrito mi problema lo mejor posible, es un poco difícil de explicar. No pude encontrar nada al respecto en Google.

+0

No exponer los puntos finales de la API web como IQueryable en absoluto ... Si realmente los necesita, vaya a Web API OData. De lo contrario, basta con quedarse con los viejos puntos finales REST y exponer cualquier tipo de filtrado posible como parámetros en las acciones de su controlador. – mare

Respuesta

7

No seleccione todo primero en la memoria. Hacer algo como esto:

public IQueryable<ItemDto> Get() 
{ 
    using (EfContext context = new EfContext()) 
    { 
     var query = from item in context.Items 
        select Mapper.Map<Item, ItemDto>(item) 

     return query.OrderByDescending(x => x.PubDate).Take(20)); 
    } 
} 

BTW El código siguiente es algo que desea hacer una vez, por ejemplo, en un constructor estático o en el archivo WebApiConfig.cs.

Mapper.CreateMap<Item, ItemDto>() 
    .ForMember(itemDto => itemDto.Category, mce => mce.MapFrom(item => item.Category.Name)); 
+0

¿Eso funciona en EF4? Como mejor recuerdo, EF no te permitió mapear a tipos que no estaban definidos en EF. –

+0

Entonces, ¿solo tengo que definir mi asignación una vez en el inicio de la aplicación? No lo sabía Gracias por señalarlo :) –

+0

@Ryan. En la consulta EF está recuperando entidades EF, solo las que se cargan se transforman en DTO usando AutoMapper. Pero de esta forma se obtiene una carga diferida, por lo que la orden/filtro se realiza en la base de datos y solo se mapean los 20 registros máximos de entidades EF a DTO. – Maurice

3

Si la única consulta tiene lugar en el código que vemos (es decir, ordenar y Tomar) su código está bien. Solo asignará el resultado (máximo 20). Sin embargo, dado que está devolviendo IQueryable, supongo que desea consultar el resultado. ¿Pueden ser parámetros de estilo OData?

Con un máximo de 20 elementos, es mejor que no escriba ningún código. El resto de las consultas se realizarán como consultas de objetos. Sin embargo, si decide eliminar esa restricción (máx. 20) o la pone después de que se realicen más consultas, esta forma será ineficaz.

Básicamente, debe mover la asignación al final de la cadena de consulta si desea que todas sus consultas se ejecuten en la base de datos de EF.

Lo que puede hacer es realmente volver a la entidad real de objetos

public IQueryable<ItemDto> Get() 
    { 
     using (EfContext context = new EfContext()) 
     { 
      return context.items 
         .OrderByDescending(x => x.PubDate) 
         .Take(20)); 
     } 
    } 

y decirle a MVC cómo serializar esto en un MediaTypeFormatter. Aquí puedes usar la AutoMapper. .