2014-10-29 162 views
7

我的問題與此類似:How do I map an OData query against a DTO to an EF entity? 我有一個簡單的設置來測試ASP.NET Web API OData V4 $過濾功能。我想要做的是爲ProductDTO的某些屬性「別名」以匹配產品實體的屬性。用戶將調用的ProductsController例如有以下要求:如何將OData查詢映射到DTO到另一個實體?

獲得產品$過濾器=顯示名​​稱EQ '測試'

產品類:

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<ProductDTO> Get(ODataQueryOptions<Product> q) 
    { 
     IQueryable<Product> products = this._products.AsQueryable(); 
     if (q.Filter != null) products = q.Filter.ApplyTo(this._products.AsQueryable(), new ODataQuerySettings()) as IQueryable<Product>; 
     return products.Select(p => new ProductDTO(p)); 
    } 
} 

當然,我收到以下異常:

找不到一個叫上輸入 '顯示名稱' 財產 'TestAPI.Models.Product'

我試着通過將以下行添加到WebApiConfig.cs中,使用新引入的別名功能:

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> products = builder.EntitySet<Product>("Product"); 
     products.EntityType.Property(p => p.Name).Name = "DisplayName"; 
     products.EntityType.Property(p => p.Level).Name = "DisplayLevel"; 
     return builder.GetEdmModel(); 
    } 
} 

我想我錯誤地使用了別名功能,因爲拋出了與上面描述的相同的異常。如果我調用它的工作原理如下要求,但這不是我想要實現:

獲得產品$濾芯的名稱EQ「測試」

更新:

我同意gdoron,該Get終點應該是這樣的:

public IEnumerable<ProductDTO> Get(ODataQueryOptions<ProductDTO> q) 

但這應該是可以解決的,而不AutoMapper?

回答

10

我找到了一個不使用AutoMapper的解決方案。

ProductsController類現在看起來是這樣的:

public class ProductsController : ApiController 
{ 
    public IEnumerable<ProductDTO> Get(ODataQueryOptions<ProductDTO> q) 
    { 
     IQueryable<Product> 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<string, string> { { "$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<Product>; 
     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> product = builder.EntitySet<Product>("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(); 
    } 
} 
+0

冠軍。它對我來說就像一個魅力。 – Immortal 2015-11-12 13:38:27

+0

聰明!爲什麼使用automapper,而你完全可以用C#來完成它。它也可以通過可查詢和表達式樹來完成。 – user1019042 2016-10-13 13:06:44

4

如果你決定要使用的DTO(這絕對是在我看來是個好主意),然後用它...
$metadata應該反映而不是EF實體的DTO的屬性名稱,因爲這是客戶得到什麼,這就是客戶應該發送的內容。
這意味着你應該Get端點改變這樣的事情:

public IEnumerable<ProductDTO> Get(ODataQueryOptions<ProductDTO> q) 

爲了避免ProductDTOProduct之間的耦合,您可以使用AutoMapper的類之間進行映射爲您服務。另外,如果你使用AutoMapper的Project方法,你可以清理你的方法來的財產以後,如:

public IQueryable<ProductDTO> Get(ProductDTO dto) 

您可以檢查Asp.net official demo for versioning,它大量使用的DTO和AutoMapper,它會給你一個很好的方向,只是忽略版本,如果它現在不感興趣。

+0

這些示例很好,但可惜我沒有看到轉換計算字段(那些沒有存儲在數據庫中但在調用時在內存中計算的字段)。我寧願不在處理業務狀態邏輯的視圖模型中複製我的代碼。我想向用戶公開一些計算的字段。任何提示可以讓我使用AutoMapper? – 2016-02-04 06:00:07

1

嘗試使用AutoMapper,你將需要這些引用添加到您的控制器

using AutoMapper; 
using AutoMapper.QueryableExtensions; 

你的方法

[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)] 
public IQueryable<ObjectDTO> Get() 
{ 
    return <Repo>.GetAll().Project().To<ObjectDTO>(); 
} 

在您的全球

protected void Application_Start() 
{ 
     //Usually in a diff class Mapping.ConfigureDataTransferObjects(); 
     Mapper.CreateMap<Object, ObjectDTO>(); 
     Mapper.CreateMap<ObjectDTO, Object>(); 
} 

感謝

相關問題