LINQ 考量 (WCF Data Services)
本主題所提供的資訊是關於您要使用 WCF Data Services 用戶端時所撰寫和執行 LINQ 查詢的方式,以及使用 LINQ 查詢實作 Open Data Protocol (OData) 之資料服務的限制。 如需詳細資訊 針對 OData 型資料服務撰寫和執行查詢的詳細資訊,請參閱查詢資料服務 (WCF Data Services)。
此主題包括下列章節:
撰寫 LINQ 查詢
LINQ 可讓您針對實作 IEnumerable<T> 之物件的集合,撰寫查詢。 Visual Studio 中的 [加入服務參考] 對話方塊和 DataSvcUtil.exe 工具都是用來產生 OData 服務的表示法,作為繼承自 DataServiceContext 的實體容器類別,以及表示摘要中傳回之實體的物件。 這些工具也會針對服務公開為摘要的集合,產生實體容器類別的屬性。 封裝資料服務之類別的這些每個屬性都會傳回 DataServiceQuery<TElement>。 DataServiceQuery<TElement> 類別會實作 LINQ 所定義的 IQueryable<T> 介面,因此,您可以針對資料服務所公開的摘要,撰寫 LINQ 查詢,用戶端程式庫會將這些摘要轉譯為執行時傳送至資料服務的查詢要求 URI。
重要
可以用 LINQ 語法表示的查詢集合會比在 OData 資料服務所使用之 URI 語法中啟用的查詢集合更廣泛。當查詢無法對應至目標資料服務中的 URI 時,就會引發 NotSupportedException。如需詳細資訊,請參閱 本主題中的Unsupported LINQ Methods。
下列範例是 LINQ 查詢,它會傳回運費成本超過 30 美元的 Orders,並依運送日期排序結果 (從最近的運送日期開始):
Dim selectedOrders = From o In context.Orders _
Where (o.Freight > 30) _
Order By o.ShippedDate Descending _
Select o
var selectedOrders = from o in context.Orders
where o.Freight > 30
orderby o.ShippedDate descending
select o;
此 LINQ 查詢會轉譯為下列查詢 URI,其會針對 Northwind 架構的快速入門資料服務執行:
https://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30
如需 LINQ 的一般詳細資訊,請參閱Language-Integrated Query (LINQ)。
LINQ 可讓您同時使用語言專屬的宣告式查詢語法 (如以上範例所示),以及所謂標準查詢運算子的一組查詢方法來撰寫查詢。 相當於以上範例的查詢僅能使用以方法為基礎的語法撰寫,如以下範例所示:
Dim selectedOrders = context.Orders _
.Where(Function(o) o.Freight.Value > 30) _
.OrderByDescending(Function(o) o.ShippedDate)
var selectedOrders = context.Orders
.Where(o => o.Freight > 30)
.OrderByDescending(o => o.ShippedDate);
WCF Data Services 用戶端可以將這兩種撰寫查詢轉譯成查詢 URI,而且您可以藉由將查詢方法附加到查詢運算式,來擴充 LINQ 查詢。 當您藉由將方法語法附加到查詢運算式或 DataServiceQuery<TElement> 來撰寫 LINQ 查詢時,會以呼叫方法的順序,將運算加入至查詢 URI。 這相當於呼叫 AddQueryOption(String, Object) 方法,將每個查詢選項加入至查詢 URI。
執行 LINQ 查詢
當 First<TSource> 或 Single<TSource> 之類的特定 LINQ 查詢方法附加至查詢時,會執行該查詢。 以隱含的方式列舉結果時 (例如在 foreach 執行迴圈期間),或將查詢指派到 List 集合時,也會執行查詢。 如需詳細資訊,請參閱查詢資料服務 (WCF Data Services)。
用戶端會以兩個部分執行 LINQ 查詢。 如果可能,查詢中的 LINQ 運算式會先在用戶端上進行評估,然後產生 URI 架構的查詢,並將其傳送至資料服務,以便針對服務中的資料進行評估。 如需詳細資訊,請參閱查詢資料服務 (WCF Data Services) 中的Client versus Server Execution一節。
在與 OData 相容的查詢 URI 中無法轉譯 LINQ 查詢時,如果嘗試執行,就會引發例外狀況。 如需詳細資訊,請參閱查詢資料服務 (WCF Data Services)。
LINQ 查詢範例
以下各節中的範例會示範可以針對 OData 服務執行的 LINQ 查詢種類。
篩選
本節中的 LINQ 查詢範例會篩選服務所傳回之摘要中的資料。
下列範例是相當於篩選傳回之 Orders 實體的查詢,因此只會傳回運費成本超過 30 美元的訂單:
使用 LINQ 查詢語法:
Dim filteredOrders = From o In context.Orders Where o.Freight.Value > 30 Select o
var filteredOrders = from o in context.Orders where o.Freight > 30 select o;
使用 LINQ 查詢方法:
Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)
var filteredOrders = context.Orders .Where(o => o.Freight > 30);
URI 查詢字串 $filter 選項:
' Define a query for orders with a Freight value greater than 30. Dim filteredOrders _ = context.Orders.AddQueryOption("$filter", "Freight gt 30M")
// Define a query for orders with a Freight value greater than 30. var filteredOrders = context.Orders.AddQueryOption("$filter", "Freight gt 30M");
先前的所有範例都會轉譯為查詢 URI:https://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M。
您也可以使用 All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) 和 Any<TSource>(IEnumerable<TSource>) 運算子,根據集合屬性撰寫篩選實體的查詢。 在此情況下,將會針對傳回相關實體集合及基本和複雜類型集合的屬性評估此述詞。 例如,下列查詢會傳回具有領域的任何員工,而且領域的描述包含提供的字串:
Dim filteredEmployees = From e In context.Employees _
Where e.Territories.Any(Function(t) t.TerritoryDescription.Contains(territory))
Select e
var filteredEmployees = from e in context.Employees
where e.Territories.Any(t => t.TerritoryDescription.Contains(territory))
select e;
在此查詢中,Any<TSource>(IEnumerable<TSource>) 運算子可讓您周遊於 Employees 與 Territories 之間的多對多關聯,以便根據相關領域的評估篩選員工。 如需詳細資訊,請參閱文章 WCF Data Services 中的任何/所有支援。
排序
下列範例示範相當於依公司名稱和郵遞區號,以遞減的方式排序傳回之資料的查詢:
使用 LINQ 查詢語法:
Dim sortedCustomers = From c In context.Customers Order By c.CompanyName Ascending, c.PostalCode Descending Select c
var sortedCustomers = from c in context.Customers orderby c.CompanyName ascending, c.PostalCode descending select c;
使用 LINQ 查詢方法:
Dim sortedCustomers = context.Customers.OrderBy(Function(c) c.CompanyName) _ .ThenByDescending(Function(c) c.PostalCode)
var sortedCustomers = context.Customers.OrderBy(c => c.CompanyName) .ThenByDescending(c => c.PostalCode);
URI 查詢字串 $orderby 選項:
Dim sortedCustomers = context.Customers _ .AddQueryOption("$orderby", "CompanyName, PostalCode desc")
var sortedCustomers = context.Customers .AddQueryOption("$orderby", "CompanyName, PostalCode desc");
先前的所有範例都會轉譯為查詢 URI:https://localhost:12345/northwind.svc/Customers()?$orderby=CompanyName,PostalCode desc。
投影
下列範例示範相當於將傳回的資料投影成較窄之 CustomerAddress 類型的查詢:
使用 LINQ 查詢語法:
Dim projectedQuery = From c In context.Customers Select New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country }
var projectedQuery = from c in context.Customers select new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country };
使用 LINQ 查詢方法:
Dim projectedQuery = context.Customers.Where(Function(c) c.Country = "Germany") _ .Select(Function(c) New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country })
var projectedQuery = context.Customers.Where(c => c.Country == "Germany") .Select(c => new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country});
注意
您無法使用 AddQueryOption(String, Object) 方法,將 $select 查詢選項加入至查詢 URI。建議您使用 LINQ Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>) 方法,讓用戶端在要求 URI 中產生 $select 查詢選項。
先前的兩個範例都會轉譯為查詢 URI:"https://localhost:12345/northwind.svc/Customers()?$filter=Country eq 'GerGerm'&$select=CustomerID,Address,City,Region,PostalCode,Country"。
用戶端分頁
下列範例示範相當於要求包含 25 個訂單,並略過前 50 個訂單之排序實體頁面的查詢:
將查詢方法套用至 LINQ 查詢:
Dim pagedOrders = (From o In context.Orders Order By o.OrderDate Descending Select o) _ .Skip(50).Take(25)
var pagedOrders = (from o in context.Orders orderby o.OrderDate descending select o).Skip(50).Take(25);
URI 查詢字串 $skip 和 $top 選項:
Dim pagedOrders = context.Orders _ .AddQueryOption("$orderby", "OrderDate desc") _ .AddQueryOption("$skip", 50) _ .AddQueryOption("$top", 25) _
var pagedOrders = context.Orders .AddQueryOption("$orderby", "OrderDate desc") .AddQueryOption("$skip", 50) .AddQueryOption("$top", 25);
先前的兩個範例都會轉譯為查詢 URI:https://localhost:12345/northwind.svc/Orders()?$orderby=OrderDate desc&$skip=50&$top=25。
展開
當您查詢 OData 資料服務時,可以要求與查詢做為目標之實體相關的實體包含傳回的摘要。 系統會針對 LINQ 查詢做為目標的實體集,在 DataServiceQuery<TElement> 上呼叫 Expand(String) 方法,並將相關的實體集名稱當做 path 參數提供。 如需詳細資訊,請參閱載入延後的內容 (WCF Data Services)。
下列範例示範相當於在查詢中使用 Expand(String) 方法的方式:
使用 LINQ 查詢語法:
Dim ordersQuery = From o In context.Orders.Expand("Order_Details") Where o.CustomerID = "ALFKI" Select o
var ordersQuery = from o in context.Orders.Expand("Order_Details") where o.CustomerID == "ALFKI" select o;
使用 LINQ 查詢方法:
Dim ordersQuery = context.Orders.Expand("Order_Details") _ .Where(Function(o) o.CustomerID = "ALFKI")
var ordersQuery = context.Orders.Expand("Order_Details") .Where(o => o.CustomerID == "ALFKI");
先前的兩個範例都會轉譯為查詢 URI:https://localhost:12345/northwind.svc/Orders()?$filter=CustomerID eq 'ALFKI'&$expand=Order_Details。
不支援的 LINQ 方法
下表包含不支援,而且不得包含在針對 OData 服務執行之查詢中的 LINQ 方法類別:
運算類型 |
不支援的方法 |
---|---|
設定運算子 |
針對 DataServiceQuery<TElement>,以下是不支援的設定運算子: |
排序運算子 |
針對 DataServiceQuery<TElement>,不支援需要 IComparer<T> 的下列排序運算子: |
投影及篩選運算子 |
針對 DataServiceQuery<TElement>,不支援接受位置引數的下列投影及篩選運算子: |
群組運算子 |
針對 DataServiceQuery<TElement>,不支援所有群組運算子,包括: 群組運算必須在用戶端上執行。 |
彙總運算子 |
針對 DataServiceQuery<TElement>,不支援所有彙總運算,包括: 彙總運算必須在用戶端上執行,或由服務作業封裝。 |
分頁運算子 |
針對 DataServiceQuery<TElement>,不支援下列分頁運算子: 注意 在空序列上執行的分頁運算子會傳回 null。 |
其他運算子 |
針對 DataServiceQuery<TElement>,不支援下列其他運算子: |
支援的運算式函數
下列 Common Language Runtime (CLR) 方法和屬性可以在查詢運算式中轉譯,以便加入 OData 服務的要求 URI,因此受到支援:
String 成員 |
支援的 OData 函數 |
---|---|
string concat(string p0, string p1) |
|
bool substringof(string p0, string p1) |
|
bool endswith(string p0, string p1) |
|
int indexof(string p0, string p1) |
|
int length(string p0) |
|
string replace(string p0, string find, string replace) |
|
string substring(string p0, int pos) |
|
string substring(string p0, int pos, int length) |
|
string tolower(string p0) |
|
string toupper(string p0) |
|
string trim(string p0) |
DateTime 成員1 |
支援的 OData 函數 |
---|---|
int day(DateTime p0) |
|
int hour(DateTime p0) |
|
int minute(DateTime p0) |
|
int month(DateTime p0) |
|
int second(DateTime p0) |
|
int year(DateTime p0) |
1相等的 Microsoft.VisualBasic.DateAndTime 日期和時間屬性,以及 Visual Basic 中的 DatePart 方法也受到支援。
Math 成員 |
支援的 OData 函數 |
---|---|
decimal ceiling(decimal p0) |
|
double ceiling(double p0) |
|
decimal floor(decimal p0) |
|
double floor(double p0) |
|
decimal round(decimal p0) |
|
double round(double p0) |
Expression 成員 |
支援的 OData 函數 |
---|---|
bool isof(type p0) |
用戶端可能也可以評估用戶端上的其他 CLR 函數。 系統會針對無法在用戶端上評估,而且無法轉譯為有效要求 URI 以便在伺服器上進行評估的所有運算式,引發 NotSupportedException。
版本控制需求
LINQ 支援具有下列 OData 通訊協定版本控制需求:
- 對於 All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) 和 Any<TSource>(IEnumerable<TSource>) 運算子的支援,會要求用戶端與資料服務都支援 OData 通訊協定 3.0 版和更新版本。
如需詳細資訊,請參閱資料服務版本控制 (WCF Data Services)。