Linq用戶定義函數(shù)剖析
學(xué)習(xí)Linq用戶定義函數(shù)時(shí),經(jīng)常會(huì)遇到Linq用戶定義函數(shù)問題,這里將介紹XX問題的解決方法。
Linq用戶定義函數(shù)
我們可以在LINQ to SQL中使用Linq用戶定義函數(shù)。只要把Linq用戶定義函數(shù)拖到O/R設(shè)計(jì)器中,LINQ to SQL自動(dòng)使用FunctionAttribute屬性和ParameterAttribute屬性(如果需要)將其函數(shù)指定為方法。這時(shí),我們只需簡(jiǎn)單調(diào)用即可。
在這里注意:使用Linq用戶定義函數(shù)的時(shí)候必須滿足以下形式之一,否則會(huì)出現(xiàn)InvalidOperationException異常情況。
◆具有正確映射屬性的方法調(diào)用的函數(shù)。這里使用FunctionAttribute屬性和 ParameterAttribute屬性。
◆特定于LINQ to SQL的靜態(tài)SQL方法。
◆.NET Framework方法支持的函數(shù)。
下面介紹幾個(gè)例子:
1.在Select中使用用戶定義的標(biāo)量函數(shù)
所謂標(biāo)量函數(shù)是指返回在 RETURNS 子句中定義的類型的單個(gè)數(shù)據(jù)值??梢允褂盟袠?biāo)量數(shù)據(jù)類型,包括 bigint 和 sql_variant。不支持 timestamp 數(shù)據(jù)類型、用戶定義數(shù)據(jù)類型和非標(biāo)量類型(如 table 或 cursor)。在 BEGIN...END 塊中定義的函數(shù)主體包含返回該值的 Transact-SQL 語句系列。返回類型可以是除 text、ntext、image、cursor 和 timestamp 之外的任何數(shù)據(jù)類型。我們?cè)谙到y(tǒng)自帶的NORTHWND.MDF數(shù)據(jù)庫(kù)中,有3個(gè)自定義函數(shù),這里使用TotalProductUnitPriceByCategory,其代碼如下:
- ALTER FUNCTION [dbo].[TotalProductUnitPriceByCategory]
- (@categoryID int)
- RETURNS Money
- AS
- BEGIN
- -- Declare the return variable here
- DECLARE @ResultVar Money
- -- Add the T-SQL statements to compute the return value here
- SELECT @ResultVar = (Select SUM(UnitPrice)
- from Products
- where CategoryID = @categoryID)
- -- Return the result of the function
- RETURN @ResultVar
- END
我們將其拖到設(shè)計(jì)器中,LINQ to SQL通過使用 FunctionAttribute 屬性將類中定義的客戶端方法映射到用戶定義的函數(shù)。請(qǐng)注意,這個(gè)方法體會(huì)構(gòu)造一個(gè)捕獲方法調(diào)用意向的表達(dá)式,并將該表達(dá)式傳遞給 DataContext 進(jìn)行轉(zhuǎn)換和執(zhí)行。
- [Function(Name="dbo.TotalProductUnitPriceByCategory",
- IsComposable=true)]
- public System.Nullable<decimal> TotalProductUnitPriceByCategory(
- [Parameter(DbType="Int")] System.Nullable<int> categoryID)
- {
- return ((System.Nullable<decimal>)(this.ExecuteMethodCall(this,
- ((MethodInfo)(MethodInfo.GetCurrentMethod())), categoryID)
- .ReturnValue));
- }
我們使用時(shí),可以用以下代碼來調(diào)用:
- var q = from c in db.Categories
- select new
- {
- c.CategoryID,
- TotalUnitPrice =
- db.TotalProductUnitPriceByCategory(c.CategoryID)
- };
這時(shí),LINQ to SQL自動(dòng)生成SQL語句如下:
- SELECT [t0].[CategoryID], CONVERT(Decimal(29,4),
- [dbo].[TotalProductUnitPriceByCategory]([t0].[CategoryID]))
- AS [TotalUnitPrice] FROM [dbo].[Categories] AS [t0]
2.在Where從句中使用用戶定義的標(biāo)量函數(shù)
這個(gè)例子使用方法同上一個(gè)例子原理基本相同了,MinUnitPriceByCategory自定義函數(shù)如下:
- ALTER FUNCTION [dbo].[MinUnitPriceByCategory]
- (@categoryID INT
- )
- RETURNS Money
- AS
- BEGIN
- -- Declare the return variable here
- DECLARE @ResultVar Money
- -- Add the T-SQL statements to compute the return value here
- SELECT @ResultVar = MIN(p.UnitPrice) FROM Products as p
- WHERE p.CategoryID = @categoryID
- -- Return the result of the function
- RETURN @ResultVar
- END
拖到設(shè)計(jì)器中,生成代碼如下:
- [Function(Name="dbo.MinUnitPriceByCategory", IsComposable=true)]
- public System.Nullable<decimal> MinUnitPriceByCategory(
- [Parameter(DbType="Int")] System.Nullable<int> categoryID)
- {
- return ((System.Nullable<decimal>)(this.ExecuteMethodCall(
- this, ((MethodInfo)(MethodInfo.GetCurrentMethod())),
- categoryID).ReturnValue));
- }
這時(shí)可以使用了:注意這里在 LINQ to SQL 查詢中,對(duì)生成的Linq用戶定義函數(shù)方法MinUnitPriceByCategory的內(nèi)聯(lián)調(diào)用。此函數(shù)不會(huì)立即執(zhí)行,這是因?yàn)椴樵儠?huì)延遲執(zhí)行。延遲執(zhí)行的查詢中包含的函數(shù)直到此查詢執(zhí)行時(shí)才會(huì)執(zhí)行。為此查詢生成的 SQL 會(huì)轉(zhuǎn)換成對(duì)數(shù)據(jù)庫(kù)中Linq用戶定義函數(shù)的調(diào)用(請(qǐng)參見此查詢后面的生成的 SQL語句),當(dāng)在查詢外部調(diào)用這個(gè)函數(shù)時(shí),LINQ to SQL 會(huì)用方法調(diào)用表達(dá)式創(chuàng)建一個(gè)簡(jiǎn)單查詢并執(zhí)行。
- var q =
- from p in db.Products
- where p.UnitPrice ==
- db.MinUnitPriceByCategory(p.CategoryID)
- select p;
它自動(dòng)生成的SQL語句如下:
- SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID],
- [t0].[CategoryID],[t0].[QuantityPerUnit], [t0].[UnitPrice],
- [t0].[UnitsInStock], [t0].[UnitsOnOrder],[t0].[ReorderLevel],
- [t0].[Discontinued]FROM [dbo].[Products] AS [t0]
- WHERE [t0].[UnitPrice] =
- [dbo].[MinUnitPriceByCategory]([t0].[CategoryID])
【編輯推薦】