2012-03-27 117 views
9

我用ASP.NET Web API構建了一個漂亮的小API,但我想從我的上下文(實體框架)AsQueryable返回實體是不正確的,所以我將所有東西映射到DTO對象。ASP.NET Web API返回可查詢的DTO?

但是我不太明白:我如何保持我的上下文可查詢,但仍然只返回DTO而不是實體?或者這是不可能的?

這是我的代碼:

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(); 
    } 
} 

正如你可以看到我加載數據,並作出一點IEnumerable集合可查詢。問題是,爲這段代碼生成的查詢可能效率很低,因爲它先加載所有項目(或至少20個第一項),然後過濾輸出。

我希望我描述我的問題儘可能好,這有點難以解釋。我在Google上找不到任何關於它的內容。

+0

不要將Web API端點公開爲IQueryable ......如果您真的需要這樣,請使用Web API OData。否則,只需使用普通的舊REST端點並在控制器操作中公開任何可能的過濾參數。 – mare 2014-06-01 16:20:50

回答

7

不要先選擇內存中的所有內容。做這樣的事情:

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下面的代碼是你想要做一次,例如在靜態構造函數或WebApiConfig.cs文件的東西。

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

這是否在EF4中工作?據我所知,EF不會讓你映射到EF中未定義的類型。 – 2012-03-27 18:46:05

+0

所以我只需要在應用程序啓動時定義我的映射一次?不知道。感謝您指出:) – 2012-03-27 18:49:50

+0

@Ryan。在EF查詢中,您正在檢索EF實體,只有使用AutoMapper將加載的這些實體轉換爲DTO。但是這樣你得到延遲加載,所以訂單/過濾器在數據庫中完成,並且只有最多20條記錄從EF實體映射到DTO。 – Maurice 2012-03-27 18:53:56

3

如果唯一的查詢發生在我們看到的代碼中(即排序和拿)你的代碼是好的。它只會映射結果(最大20)。但是,既然你正在返回IQueryable,我假設你想進一步查詢結果。可能是OData樣式參數?

對於最多20件商品,您最好不要寫任何代碼。其餘的查詢將作爲對象查詢來執行。但是,如果您決定刪除該約束(最多20個),或者在進一步查詢後進行查詢,那麼這種方法效率不高。

基本上,如果您希望所有查詢都在EF數據庫中運行,您需要將查詢鏈最末端的映射移動。

你可以做的其實就是返回實際的實體對象

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

並告訴MVC如何在MediaTypeFormatter序列化此。在這裏你可以使用AutoMapper。 。