如何将OData查询与DTO映射到另一个实体?
我的问题与此问题非常相似: 如何将针对DTO的OData查询映射到EF实体? 我有一个简单的设置来测试ASP.NET Web API OData V4 $filterfunction。 我想做的是“别名”ProductDTO的一些属性以匹配Product实体的属性。 用户将使用以下请求调用ProductsController:
GET产品?$ filter = DisplayName eq’test’
产品类:
public class Product { public int Id { get; set; } public string Name { get; set; } public int Level { get; set; } public Product() { } }
ProductDTO类:
public class ProductDTO { public int Id { get; set; } public string DisplayName { get; set; } public int DisplayLevel { get; set; } public ProductDTO(Product product) { this.DisplayName = product.Name; this.DisplayLevel = product.Level; } }
ProductsController:
public class ProductsController : ApiController { public IEnumerable Get(ODataQueryOptions q) { IQueryable products = this._products.AsQueryable(); if (q.Filter != null) products = q.Filter.ApplyTo(this._products.AsQueryable(), new ODataQuerySettings()) as IQueryable; return products.Select(p => new ProductDTO(p)); } }
当然我得到以下exception:
无法在“TestAPI.Models.Product”类型上找到名为“DisplayName”的属性
我尝试通过将以下行添加到WebApiConfig.cs来使用新引入的别名function
public static class WebApiConfig { public static void Register(HttpConfiguration config) { … IEdmModel model = GetModel(); config.MapODataServiceRoute("*", "*", model); } private static IEdmModel GetModel() { ODataModelBuilder builder = new ODataConventionModelBuilder(); EntitySetConfiguration products = builder.EntitySet("Product"); products.EntityType.Property(p => p.Name).Name = "DisplayName"; products.EntityType.Property(p => p.Level).Name = "DisplayLevel"; return builder.GetEdmModel(); } }
我想我错误地使用了别名function,因为抛出了与上述相同的exception。 如果我调用以下请求它可以工作,但这不是我想要实现的:
GET产品?$ filter =名称eq’test’
更新:
我同意gdoron, Get
端点应该如下所示:
public IEnumerable Get(ODataQueryOptions q)
但是如果没有AutoMapper,这应该是可以解决的吗?
我没有使用AutoMapper就找到了解决方案。
ProductsController现在看起来像这样:
public class ProductsController : ApiController { public IEnumerable Get(ODataQueryOptions q) { IQueryable products = this._products.AsQueryable(); IEdmModel model = GetModel(); IEdmType type = model.FindDeclaredType("TestAPI.Models.Product"); IEdmNavigationSource source = model.FindDeclaredEntitySet("Products"); ODataQueryOptionParser parser = new ODataQueryOptionParser(model, type, source, new Dictionary { { "$filter", q.Filter.RawValue } }); ODataQueryContext context = new ODataQueryContext(model, typeof(Product), q.Context.Path); FilterQueryOption filter = new FilterQueryOption(q.Filter.RawValue, context, parser); if (filter != null) products = filter.ApplyTo(products, new ODataQuerySettings()) as IQueryable ; return products.Select(p => new ProductDTO(p)); } }
WebApiConfig:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { … IEdmModel model = GetModel(); config.MapODataServiceRoute("*", "*", model); } private static IEdmModel GetModel() { ODataModelBuilder builder = new ODataConventionModelBuilder(); EntitySetConfiguration product = builder.EntitySet ("Products"); product.EntityType.Name = "Product"; product.EntityType.Namespace = "TestAPI.Models"; product.EntityType.Property(p => p.Name).Name = "DisplayName"; product.EntityType.Property(p => p.Level).Name = "DisplayLevel"; return builder.GetEdmModel(); } }
如果您决定使用DTO(我认为这绝对是一个好主意),那么使用它……
$metadata
应该反映DTO的属性名称而不是EF实体,因为这是客户端获得的,这是客户端应该发送的内容。
这意味着您应该将Get
端点更改为以下内容:
public IEnumerable Get(ODataQueryOptions q)
为了避免ProductDTO
和Product
之间的耦合,您可以使用AutoMapper在类之间进行映射。 此外,如果您使用AutoMapper的Project
方法,您可以将方法清理为:
public IQueryable Get(ProductDTO dto)
您可以查看Asp.net官方演示版的版本 ,它大量使用DTO和AutoMapper,它会给你一个很好的方向,如果你现在不感兴趣,就忽略版本控制。
尝试使用AutoMapper,您需要将这些引用添加到控制器
using AutoMapper; using AutoMapper.QueryableExtensions;
你的方法
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)] public IQueryable Get() { return dbContext.Entities.ProjectTo (); }
在你的全球
protected void Application_Start() { //Usually in a diff class Mapping.ConfigureDataTransferObjects(); Mapper.CreateMap(); Mapper.CreateMap(); }
Patrick,您可以从计算的sourceValue填充目标值,例如:
Mapper.CreateMap() .ForMember(dest => dest.InvoiceCount, opt => opt.MapFrom(src => src.Invoices.Count()));
我从这里得到了这个例子: http : //codethug.com/2015/02/13/web-api-deep-dive-dto-transformations-and-automapper-part-5-of-6/
Arturo,如果不是复杂的映射,你可以在CreateMap上使用reverseMap来做一个单行。