route query attribute c# asp.net-web-api odata

c# - query - Compatibilidad con ASP.NET WebApi OData para DTO



web api rest c# (3)

Tengo la entidad Project y ProjectDTO. Estoy intentando crear un método de controlador WebAPI que pueda tomar y devolver ProjectDTOs y hacer que sea compatible con OData.

El problema es que estoy usando ORM que puede consultar la base de datos utilizando la entidad Proyecto y no el Proyecto DTO. ¿Hay alguna manera de que pueda aplicar el filtrado / clasificación / paginación de OData basado en ProjectDTO a la consulta de la entidad del proyecto?

public ODataQueryResult<ProjectDTO> GetProjects(ODataQueryOptions<ProjectDTO> query) { var context = new ORM_Context(); var projects = context.Projects; // IQueryable<Project> var projectDtos = query.ApplyTo(projectDTOs)); // <-- I want to achieve something similar here var projectDTOs = projects.Select( x => new ProjectDTO { Id = x.Id, Name = x.Name }); var projectsQueriedList = projectDtos.ToList(); var result = new ODataQueryResult<ProjectDTO>(projectsQueriedList, totalCount); return result; }


Algo como esto (no he intentado compilarlo)

using(var dataContext = new ORM_Context()) { var projects = dataContext.Projects; // IQueryable<Project> //Create a set of ODataQueryOptions for the internal class ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); modelBuilder.EntitySet<Project>("Project"); var context = new ODataQueryContext( modelBuilder.GetEdmModel(), typeof(Project)); var newOptions = new ODataQueryOptions<Project>(context, Request); var t = new ODataValidationSettings() { MaxTop = 25 }; var s = new ODataQuerySettings() { PageSize = 25 }; newOptions.Validate(t); IEnumerable<Project> internalResults = (IEnumerable<Project>)newOptions.ApplyTo(projects, s); int skip = newOptions.Skip == null ? 0 : newOptions.Skip.Value; int take = newOptions.Top == null ? 25 : newOptions.Top.Value; var projectDTOs = internalResults.Skip(skip).Take(take).Select(x => new ProjectDTO { Id = x.Id, Name = x.Name }); var projectsQueriedList = projectDtos.ToList(); var result = new ODataQueryResult<ProjectDTO>( projectsQueriedList, totalCount); return result; }


Prueba esto:

public object GetProjects(ODataQueryOptions<Project> query) { var context = new ORM_Context(); var projects = query.ApplyTo(context.Projects); var projectDTOs = projects.Select( x => new ProjectDTO { Id = x.Id, Name = x.Name }); return new { TotalCount = Request.GetInlineCount(), //before paging Results = projectDTOs.ToList() }; }

Aparentemente, lo más importante aquí es pasar el tipo correcto a ODataQueryOptions <> y luego realiza su magia muy bien. Esto se debe a que utiliza ese tipo particular para consultar el contexto de colección / db, por lo que debe ser del tipo que realmente está almacenado en la colección / contexto en lugar de lo que está intentando devolver.

Obviamente, sus DTO deben parecerse mucho a sus objetos ORM (y lo hacen en su fragmento) o esto no funcionará muy bien desde la perspectiva del usuario / cliente.

No intenté compilar el código anterior porque no tengo tus clases ni otra infraestructura, pero debería ser suficiente para transmitir la idea.


Creo que la forma más fácil de hacerlo es mediante el uso de AutoMapper. Entonces, para tu DTO

[DataContract(Name = "Products")] public class ProductDTO { [Key] [DataMember] public string MyProductMember1 { get; set; } [DataMember] public string MyProductMember2 { get; set; } ... }

debe escribir en algún lugar de la configuración de AutoMapper:

Mapper.CreateMap<Product, ProductDTO>();

y en algún lugar en la construcción de IEdmModel para OData:

builder.EntitySet<ProductDTO>("Products");

y el código para su controlador se verá como

public class ProductsController : ODataController { [EnableQuery] public IHttpActionResult Get() { var products = context.Products; // IQueryable<Product> return Ok(products.Project().To<ProductDTO>()); } }

De esta manera, no necesita exponer sus entidades ORM directamente, y puede usar OData para filtrar, paginar, contar e incluso expandir colecciones anidadas, y para EF se traducirá en las solicitudes SQL correspondientes utilizando la tabla a la que está asignado el Producto. Pero tenga cuidado: para casos más complicados (colecciones anidadas, por ejemplo) puede resultar en una solicitud SQL no óptima.