1,UDF 简介
UDF可以分为两中类型。一种为Scalar Valued Function,简称为SVF,是返回值类型的UDF. 另一种为Table Valued Function 简称为TVF,是返回一个table的UDF. 人们通常喜欢拿UDF和Store Procedure做比较。其实,他们各有千秋。UDF最多只能返回一个RowSet,而Store Procedure可以是多个。Store Procedure支持CUD操作,而UDF不支持。但是UDF在sql 中支持内联查询,这个又是Sprocs所不能及的。因此Linq To Sql 也支持UDF的内联查询。
2,SVF
看下面这个例子。返回某个类别产品最小的单元价格。
CREATE 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
<Function Name="dbo.MinUnitPriceByCategory" Method="MinUnitPriceByCategory" IsComposable="true">
<Parameter Name="categoryID" Type="System.Int32" DbType="Int" />
<Return Type="System.Decimal" />
</Function>无论是UDF还是Sprocs都会被映射为Function. 而IsComposable="true"是UDF独有的一个特性,是标志UDF身份的,Linq用它来区别Sprocs和UDF。这个字段说明,该函数是支持内联查询的。Name则是其在数据库中的名称。再来看其生成的code.
[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));
}
int result = db.IntSVF(variable);
int result = db.IntSVF(constant);
SELECT * FROM Products AS t0
WHERE t0.UnitPrice = dbo.MinUnitPriceByCategory(t0.CategoryID)
var q = from p in db.Products
where p.UnitPrice == db.MinUnitPriceByCategory(p.CategoryID)
select p;
CREATE 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
SELECT CategoryID, [dbo].[TotalProductUnitPriceByCategory](CategoryID) AS [TotalUnitPrice]
FROM Categories
var q = from c in db.Categories
select new {c.CategoryID, TotalUnitPrice = db.TotalProductUnitPriceByCategory(c.CategoryID)};
var q = from p in db.Products
where p.UnitsOnOrder >= db.SVF(p.UnitsInStock)
group p by db.SVF(p.CategoryID) into g
order by db.SVF(g.Key)
select db.SVF(g.Key);3,系统函数的映射
目前为止,无论是OR designer还是SqlMetal均不支持对系统函数的映射。笔者也只是尝试着,手工映射,并成功调用。我们拿Var函数举例。Var是求方差。让我们来模仿上面那个dbml来改写自己的dbml。我们将要多money类型做求var值。并且希望能够调用sql server提供的var函数。那就需要我们将映射的名称改成var,并且改动参数和返回值类型。其最后的dbml为:
<Function Name="Var" Method="Var" IsComposable="true">
<Parameter Name="para" Type="System.Decimal" DbType="Money" />
<Return Type="System.Decimal" />
</Function>
[Function(IsComposable=true)]
public System.Nullable<decimal> Var([Parameter(DbType="Money")] System.Nullable<decimal> para)
{
return ((System.Nullable<decimal>)(this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), para).ReturnValue));
}
var q = (from p in db.Products
select db.Var(p.UnitPrice)).ToList();
SELECT CONVERT(Decimal(29,4),Var([t0].[UnitPrice])) AS [value]
FROM [dbo].[Products] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel
SELECT TOP 10 * FROM TABLE1 ORDER BY NEWID();因为NewId返回uniqueidentifier类型,我们将这个函数定义为
[Function(Name = "NewID", IsComposable = true)]
public Guid NewID()
{
return ((Guid)(this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod()))).ReturnValue));
}
var q = db.Table1.OrderBy(p => db.NewID()).Take(10).ToList();4 TVF
返回一个table 的UDF称为TVF.看下面例子

CREATE FUNCTION [dbo].[ProductsUnderThisUnitPrice]
(@price Money
)
RETURNS TABLE
AS
RETURN
SELECT *
FROM Products as P
Where p.UnitPrice < @price
<Function Name="dbo.ProductsUnderThisUnitPrice" Method="ProductsUnderThisUnitPrice" IsComposable="true">
<Parameter Name="price" Type="System.Decimal" DbType="Money" />
<ElementType Name="ProductsUnderThisUnitPriceResult">
<Column Name="ProductID" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
<Column Name="ProductName" Type="System.String" DbType="NVarChar(40) NOT NULL" CanBeNull="false" />
<Column Name="SupplierID" Type="System.Int32" DbType="Int" CanBeNull="true" />
<Column Name="CategoryID" Type="System.Int32" DbType="Int" CanBeNull="true" />
<Column Name="QuantityPerUnit" Type="System.String" DbType="NVarChar(20)" CanBeNull="true" />
<Column Name="UnitPrice" Type="System.Decimal" DbType="Money" CanBeNull="true" />
<Column Name="UnitsInStock" Type="System.Int16" DbType="SmallInt" CanBeNull="true" />
<Column Name="UnitsOnOrder" Type="System.Int16" DbType="SmallInt" CanBeNull="true" />
<Column Name="ReorderLevel" Type="System.Int16" DbType="SmallInt" CanBeNull="true" />
<Column Name="Discontinued" Type="System.Boolean" DbType="Bit NOT NULL" CanBeNull="false" />
</ElementType>
</Function>
var q = from p in db.ProductsUnderThisUnitPrice(10.25M)
select p;
var q = from c in db.Categories
join p in db.ProductsUnderThisUnitPrice(8.50M) on c.CategoryID equals p.CategoryID into prods
from p in prods
select new {c.CategoryID, c.CategoryName, p.ProductName, p.UnitPrice};因为,sql中支持TVF的in-line操作,所以Linq To Sql完全支持其对等的操作。他们所生成的sql语句不再列出。
总结:
通过本文,我们可以看出Linq To Sql完全融入了Sql中UDF,包括对其内联操作的支持。对于某些特殊需求,用户可以手工将函数映射为code,但这并不说明,任何函数都适用。
TrackBack:http://www.cnblogs.com/126/archive/2007/08/10/849679.html
