假设你有如下的数据库自定义函数:
CREATE FUNCTION [dbo].[DistanceBetween](
@Lat1 as real,
@Long1 as real,
@Lat2 as real,
@Long2 as real)
RETURNS real
AS
BEGIN
…
END
同时你想把这个自定义函数用在Entity Framework中.
在EF中定义该函数
首先在XML编辑器中打开EDMX文件,在<edmx:StorageModels>的子节点<Schema>中增加一个<Function>节点,如下图所示
要增加的节点代码如下:
<Function Name="DistanceBetween"
IsComposable="true"
Schema="dbo"
Aggregate="false"
BuiltIn="false"
ReturnType="float">
<Parameter Name="Lat1" Type="float" Mode="In"/>
<Parameter Name="Long1" Type="float" Mode="In"/>
<Parameter Name="Lat2" Type="float" Mode="In"/>
<Parameter Name="Long2" Type="float" Mode="In"/>
</Function>
用eSQL的方式使用该函数
现在,该函数在eSQL中可以被像下面这样调用:
SELECT VALUE(D) FROM MyModel.Dinners AS D
WHERE StorageNamespace.DistanceBetween(
D.Latitude,D.Longitude,-34,174) < 50
MyModel是当前的EntityContainer (和你使用的ObjectContext基本是一致的),StorageNamespace 是 storage model schema namespace.
在LINQ语句中使用该函数
我们在大多数情况下不会使用eSQL,我们可能会想在Linq语句中使用该函数?
在3.5 SP1 中像下面这样:
var nearbyDinners =
from d in ctx.Dinners.Where(
“StorageNamespace.DistanceBetween(it.Latitude, it.Longitude, –34, 174) < 50”
) select d;
这里其实是把eSQL语法通过查询语句构造方法嵌入到了Linq语句中来使得LINQ语句可以访问该自定义函数,你同样可以传入相关的参数。
上面的方式可以很好的运行,但是并不完美,如果可以不用字符串这种非强类型的方式就好了。
在EF 4.0的改进
在EF4.0中,你可以如下方式重写上面的代码:
var nearbyDinners =
from d in ctx.Dinners
where DistanceBetween(d.Latitude, d.Longitude, –34,174) < 50
select d;
要像上面那样写,你需要一个额外的辅助方法,如下所示:
[EdmFunction("StorageNamespace", "DistanceBetween")]
public double DistanceBetween(
double lat1,
double long1,
double lat2,
double long2)
{
throw new NotImplementedException("You can only call this method as part of a LINQ expression");
}
看了上面的代码,你会感到奇怪,为什么要在代码中跑出一个异常呢?
You may be wondering why the method throws an exception?
其实我们并不是真正需要执行这个方法,我们只是利用这个方法协助我们书写LINQ查询,用这样的方式把eSQL的书写方式翻译成LINQ的方式。EF会根据上面方法上面的EdmFunction属性推到出应该调用哪一个数据库自定义方法。