簡體   English   中英

oData EndPoint:是否影響 SQL 查詢?

[英]oData EndPoint: Does it affect the SQL query?

我一直在為我的 api 使用 OData。

雖然我通常喜歡它所提供的東西,但它只使用數據發布我的查詢,這迫使我提前構建所有關系。

帶有 EntityFramework 的 oData EndPoint 是否傳遞我的 oData 參數以在我的 SQL 查詢之前執行?

現在,如果我計划可能使用 $Expand 等 oData 語法,我必須提前使用 EF Include。 再一次,問題是 EF 必須建立我可能使用 $Expand 的所有潛在關系......即使我沒有 $expand 任何東西。

另一個例子是如果我要使用 $top(100) 語法。 假設我有 10000 個結果,EF 將從數據庫下載所有 10000 個,然后 OData 將選擇前 100 個。

在這種情況下,oData 端點是否會在 EF 和 DB 之間注入自身,並且僅從 DB 中選擇 100 個結果?

一般來說,OData 和 EF 齊頭並進,OData 將傳入的 HTTP 請求轉換為Linq-to-Entities表達式,然后 EF 將其轉換為 SQL 表達式。

tl;博士

您的所有評論和觀察都指向您的控制器中的錯誤實現,這聽起來很可疑,好像您遵循的是基於存儲庫模式的示例而不是基於 EF 的示例。

帶有 EntityFramework 的 oData EndPoint 是否傳遞我的 oData 參數以在我的 SQL 查詢之前執行?

這正是 OData 框架旨在啟用的功能,但有一個警告,您必須以可以傳遞參數的方式配置控制器。

有兩種機制允許這種情況發生,第一種是您的控制器需要返回IQueryable<T>結果(或者您必須將IQueryable<T>傳遞給協商的響應處理程序之一)。 另一個是你不能應用你自己的可能與參數相矛盾的過濾器表達式,否則你可能會導致沒有記錄被返回。

以下是 OData 控制器上的兩個標准Get端點的示例,該端點將返回一個Vehicle查詢,該查詢將允許傳遞$expand$select$filter表達式:

[ODataRoute]
[HttpGet]
[OData.EnableQuery]
public IHttpActionResult Get(ODataQueryOptions<Vehicle> queryOptions)
{
    return Ok(db.Vehicles);
}

[ODataRoute]
[HttpGet]
[OData.EnableQuery]
public IHttpActionResult Get([FromOdataUri] int key, ODataQueryOptions<Vehicle> queryOptions)
{
    return SingleResultAction(db.Vehicles.Where(x => x.Id == key));
}

如果我使用以下查詢選項調用它:

$filter=Make eq Holden and Model eq Commodore&$orderby=Year desc

然后這將轉換為類似於此的 SQL 查詢:
(SELECT * 將完全展開)

DECLARE @make varchar(20) = 'Holden';
DECLARE @model varchar(20) = 'Commodore';

SELECT * 
FROM Vehicle
WHERE Make = @make
  AND Model = @model
ORDER BY Year DESC

是的,參數也將被正確參數化!

這個簡單的控制器也會通過$expand請求並自動加入必要的表,我們根本不需要預先知道或考慮潛在的包含

事實上,如果我加上$skip=5$top=2這些子句也會直接應用到 SQL 語句中,最多只會從數據庫中返回 2 行。


有兩種常見的場景,所有這些自動神奇的查詢翻譯和通過 mumbo jumbo 都會被破壞。

  1. 在控制器方法中,(對於集合或項目)您不返回IQueryable結果,或者您以其他方式執行查詢並將其解析為IEnumerable ,然后您已返回該結果,也許是通過將其轉換回IQueryable .

    • 這往往會發生,因為當我們開始時,我們傾向於遵循簡單的非基於 EF 的 OData 示例,它們通常遵循存儲庫模式,因為在單頁代碼中解釋模型、示例數據和實現很簡單。

    公共服務公告:不要嘗試使用 Repository Pattern 來包裝 EF 模型和上下文,幾乎所有來自 EF 的好處都將丟失。 EF 是一個工作單元模式,OData 旨在直接使用它。

    下面的控制器實現不會將查詢選項傳遞給數據庫, $top$skip$filter$select$orderby仍然會被應用,如果可以的話,但只有在首先所有記錄檢索到內存之后:

     return Ok(db.Vehicles.ToList());
    • $expand不會被應用,或者它會導致NULL相關記錄。
  2. 另一個常見問題是,如果我們想要自動支持整個范圍的查詢選項,那么在處理了一條數據記錄(或多個記錄)的Actions之后,我們再次需要確保我們返回一個從DbContext查詢的IQueryable表達式。 如果表達式是IQueryable ,但僅查詢內存中已有的內容,則$expand$filter例如只能應用於已加載的數據。

    • SingleResultAction是一個很好的輔助方法,用於返回范圍為單個項目的IQueryable ,但它仍然允許應用完整范圍的QueryOptions

歡迎來到痛苦的世界 - Odata 是一個好主意,但實施起來卻很糟糕。 但是,是的,這是一個了不起的 - 自己做。

它只使用 Post my query 的數據,這迫使我提前構建所有關系。

如果這問我認為它做什么(你的英語非常不清楚),那么不,它沒有 - 你的錯是直接暴露 ef 對象。 我有單獨的 api 對象並使用 AutoMapper ProjectTo 公開它們。 是的,我需要提前定義關系,但不是在 ef 級別。

我必須提前使用 EF Include。

那是因為你決定這樣做。 正如我所說,我實際上使用 Automapper ProjectTo - 並從 OdataOptions SelectExpand 信息中動態獲取必要的擴展。 無需將整個對象圖發送到 EF(如果您使用包含擴展所有可能的擴展,就會發生這種情況),這將導致非常糟糕的性能。 只需一頁編程即可獲得相關的包含。

在這種情況下,oData 端點是否會在 EF 和 DB 之間注入自身,並且僅從 DB 中選擇 100 個結果?

如果它是這樣編程的,是的。 如果有人對 LINQ 編程提出挑戰,並將 ef 打包到一種荒謬的低效存儲庫模式中,該模式返回 INumerable - 那么不,它可能會將其全部拉出。 去過那里見過。 但通常 TOP(1) 導致 sql 只選擇第一項。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM