如何使用带有LINQ的标量函数来实体? [英] How to use scalar-valued function with linq to entity?
问题描述
可能重复:
可以将SQL级别的功能提供给LINQ to Entity查询?
我有一个标量函数来获取两点之间的距离,我想使用它来查询最接近的记录。
标量函数适用于linq到sql,但失败与EF
标量函数
USE [GeoData]
GO
/ ******对象:UserDefinedFunction [dbo]。[DistanceBetween]脚本日期:09/18/2012 19:40:44 ****** /
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo]。[DistanceBetween](@ Lat1 as real,
@ Long1 as real,@ Lat2 as real,@ Long2 as real)
RETURNS real
AS
BEGIN
DECLARE @ dLat1InRad as float(53);
SET @ dLat1InRad = @ Lat1;
DECLARE @ dLong1InRad as float(53);
SET @ dLong1InRad = @ Long1;
DECLARE @ dLat2InRad as float(53);
SET @ dLat2InRad = @ Lat2;
DECLARE @ dLong2InRad as float(53);
SET @ dLong2InRad = @ Long2;
DECLARE @dLongitude as float(53);
SET @dLongitude = @ dLong2InRad - @ dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @ dLat2InRad - @ dLat1InRad;
/ *中级结果a。 * /
DECLARE @a as float(53);
SET @a = SQUARE(SIN(@dLatitude / 2.0))+ COS(@ dLat1InRad)
* COS(@ dLat2InRad)
* SQUARE(SIN(@dLongitude / 2.0));
/ *中间结果c(Radians中的大圆距离)。 * /
DECLARE @c为真实;
SET @c = 2.0 * ATN2(SQRT(@a),SQRT(1.0 - @a));
DECLARE @kEarthRadius为真实的;
/ * SET kEarthRadius = 3956.0英里* /
SET @kEarthRadius = 6376.5; / * kms * /
DECLARE @dDistance为真实;
SET @dDistance = @kEarthRadius * @c;
return(@dDistance);
END
GO
我添加了一个ado.net实体模型,从数据库中更新模型,并选择
$ b pre>
< Function Name =DistanceBetween之间的距离ReturnType =realAggregate = falseBuiltIn =falseNiladicFunction =falseIsComposable =trueParameterTypeSemantics =AllowImplicitConversionSchema =dbo>
< Parameter Name =Lat1Type =realMode =In/>
< Parameter Name =Long1Type =realMode =In/>
< Parameter Name =Lat2Type =realMode =In/>
< Parameter Name =Long2Type =realMode =In/>
< / Function>
我做了一个部分课程,写了这个方法
public partial class GeoDataEntities
{
[EdmFunction(GeoDataModel.Store,DistanceBetween)]
public double DistanceBetween(double lat1, double lon1,double lat2,double lon2)
{
throw new NotImplementedException();
}
}
我尝试了很多次以使用此代码查询功能但是它得到一个错误
var NParcel = db.geoAddresses.Where(g => db.DistanceBetween(21.5,39.5, g.lat,g.lon)< 20);
当我尝试计数
或 foreach
NParcel我收到这个错误
指定的方法'Double DistanceBetween(Double,Double ,Double,
Double)'在类型'EFSample.GeoDataEntities'不能被翻译成
到LINQ to Entities存储表达式。
和stacktrace
at
System.Data.Objects.ELinq.ExpressionConverter.ThrowUnresolvableFunction(Expression
$ b System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.TranslateFunctionCall(ExpressionConverter
父,MethodCallExpression调用,EdmFunctionAttribute
functionAttribute)在
System.Data .Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter
parent,MethodCallExpression linq)at
System.Data.Objects.ELinq.Express在
System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression
linq)at
System.Data.Objects中的ionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter
parent,BinaryExpression linq)
System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter
parent,MethodCallExpression调用,DbExpression& amp;来源,
DbExpressionBinding& sourceBinding,DbExpression& lambda)
System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter
父,MethodCallExpression调用)在
System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator。
System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression
linq)
System.Data.Objects.ELinq.ExpressionConverter(ExpressionConverter
parent,MethodCallExpression linq)在
System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter
parent,MethodCallExpression linq)$ b $中的方法CallTranslator.AggregateTranslator.Translate(ExpressionConverter
父,MethodCallExpression调用) b System.Data.Objects.ELinq.ExpressionConverter.Convert()at
System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1
forMergeOption)at
System.Data.Objects.ObjectQuery
System.Data.Objects.ObjectQuery 1.CetResults(Nullable1
forMergeOption)1.System.Collections.Generic.IEnumerable.GetEnumerator()
在System.Linq.Enumerable.Single [TSource](IEnumerable1源)
1来源)
System.Linq.Queryable.Count [TSource](IQueryable
这是你如何做到的:
步骤1:在edmx
< Function Name = DistanceBetweenAggregate =falseBuiltIn =falseNiladicFunction =falseIsComposable =falseParameterTypeSemantics =AllowImplicitConversionSchema =dbo>
< CommandText>
选择dbo.DistanceBetween(@ lat1,@ long1,@ lat2,@ long2)
< / CommandText>
< Parameter Name =Lat1Type =realMode =In/>
< Parameter Name =Long1Type =realMode =In/>
< Parameter Name =Lat2Type =realMode =In/>
< Parameter Name =Long2Type =realMode =In/>
< / Function>
步骤2:导入功能
- 双击
edmx
- 在模型浏览器视图中,展开
GeoDataModel.Store
(可以命名为不同) - 展开
存储过程/函数
- 双击
DistanceBetween
-
Scalars = Single
- 单击确定
步骤3:在C#中: / strong>
GeoDataEntities db = new GeoDataEntities();
var first = db.DistanceBetween(234,2342,424,243).First()。
注意 code> IsComposable =false而没有 ReturnType
,不要忘记添加:
< CommandText>
选择dbo.DistanceBetween(@ lat1,@ long1,@ lat2,@ long2)
< / CommandText>
希望帮助....
Possible Duplicate:
Can SQL level functions be made available to LINQ to Entity queries?
I have a scalar function that gets the distance between two points and I want to use it to query closest record to point. The scalar function works with linq to sql but fails with EF
the scalar function
USE [GeoData]
GO
/****** Object: UserDefinedFunction [dbo].[DistanceBetween] Script Date: 09/18/2012 19:40:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[DistanceBetween](@Lat1 as real,
@Long1 as real, @Lat2 as real, @Long2 as real)
RETURNS real
AS
BEGIN
DECLARE @dLat1InRad as float(53);
SET @dLat1InRad = @Lat1;
DECLARE @dLong1InRad as float(53);
SET @dLong1InRad = @Long1;
DECLARE @dLat2InRad as float(53);
SET @dLat2InRad = @Lat2;
DECLARE @dLong2InRad as float(53);
SET @dLong2InRad = @Long2 ;
DECLARE @dLongitude as float(53);
SET @dLongitude = @dLong2InRad - @dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @dLat2InRad - @dLat1InRad;
/* Intermediate result a. */
DECLARE @a as float(53);
SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad)
* COS (@dLat2InRad)
* SQUARE(SIN (@dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE @c as real;
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a));
DECLARE @kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET @kEarthRadius = 6376.5; /* kms */
DECLARE @dDistance as real;
SET @dDistance = @kEarthRadius * @c;
return (@dDistance);
END
GO
I added an ado.net entity model, updated the model from the database and chose distancebetween
<Function Name="DistanceBetween" ReturnType="real" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="Lat1" Type="real" Mode="In" />
<Parameter Name="Long1" Type="real" Mode="In" />
<Parameter Name="Lat2" Type="real" Mode="In" />
<Parameter Name="Long2" Type="real" Mode="In" />
</Function>
I made a partial class and wrote this method
public partial class GeoDataEntities
{
[EdmFunction("GeoDataModel.Store", "DistanceBetween")]
public double DistanceBetween(double lat1, double lon1, double lat2, double lon2)
{
throw new NotImplementedException();
}
}
I tried many times to query the function with this code but it gets an error
var NParcel = db.geoAddresses.Where(g=> db.DistanceBetween(21.5,39.5, g.lat,g.lon) < 20);
when i try to count
or foreach
the NParcel I get this error
The specified method 'Double DistanceBetween(Double, Double, Double, Double)' on the type 'EFSample.GeoDataEntities' cannot be translated into a LINQ to Entities store expression.
and stacktrace
at System.Data.Objects.ELinq.ExpressionConverter.ThrowUnresolvableFunction(Expression Expression) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.TranslateFunctionCall(ExpressionConverter parent, MethodCallExpression call, EdmFunctionAttribute functionAttribute) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.AggregateTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.Convert() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable
1 forMergeOption) at System.Data.Objects.ObjectQuery
1.GetResults(Nullable1 forMergeOption) at System.Data.Objects.ObjectQuery
1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Linq.Enumerable.Single[TSource](IEnumerable1 source) at System.Linq.Queryable.Count[TSource](IQueryable
1 source)
Here is how you do it:
Step 1: In edmx
<Function Name="DistanceBetween" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<CommandText>
select dbo.DistanceBetween(@lat1,@long1,@lat2,@long2)
</CommandText>
<Parameter Name="Lat1" Type="real" Mode="In" />
<Parameter Name="Long1" Type="real" Mode="In" />
<Parameter Name="Lat2" Type="real" Mode="In" />
<Parameter Name="Long2" Type="real" Mode="In" />
</Function>
step 2: Import the function
- double click the
edmx
- In the Model Browser view, expand
GeoDataModel.Store
(could be named different) - expand
stored procedures /function
- double click
DistanceBetween
Scalars = Single
- Click OK
Step 3: In C#:
GeoDataEntities db = new GeoDataEntities();
var first = db.DistanceBetween(234, 2342, 424, 243).First().Value;
note that IsComposable="false"
and no ReturnType
and dont forget to add the :
<CommandText>
select dbo.DistanceBetween(@lat1,@long1,@lat2,@long2)
</CommandText>
Hope that help....
这篇关于如何使用带有LINQ的标量函数来实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!