简体   繁体   English

将自定义函数添加到EntityFramework

[英]Add custom function to EntityFramework

I would like to add a function in my project so that I can filter my data. 我想在项目中添加一个函数,以便可以过滤数据。

I would like that function to returns the distance in kms between 2 GPS positions. 我希望该函数返回2个GPS位置之间的距离(以公里为单位)。

So far I've done : added this in my edmx in the ConceptualModels>Schema : 到目前为止,我已经完成:在edmx中的ConceptualModels> Schema中添加了它:

<Function Name="DistanceBetweenTwoPositions" ReturnType="Edm.Double">
          <Parameter Name="latitude_1" Type="Edm.Double" />
          <Parameter Name="longitude_1" Type="Edm.Double" />
          <Parameter Name="latitude_2" Type="Edm.Double" />
          <Parameter Name="longitude_2" Type="Edm.Double" />
          <DefiningExpression>
            DistanceBetweenTwoPositions(latitude_1, longitude_1, latitude_2, longitude_2)
          </DefiningExpression>
        </Function>

Created a partial class with the same name to be able to define that function : 创建了具有相同名称的部分类,以便能够定义该函数:

[DbFunctionAttribute("DataModel", "DistanceBetweenTwoPositions")]
    public static double DistanceBetweenTwoPositions(double latitude_1, double longitude_1, double latitude_2, double longitude_2)
    {
        var rlat1 = Math.PI * latitude_1 / 180;
        var rlat2 = Math.PI * latitude_2 / 180;
        var rlon1 = Math.PI * longitude_1 / 180;
        var rlon2 = Math.PI * longitude_2 / 180;

        var theta = longitude_1 - longitude_2;
        var rtheta = Math.PI * theta / 180;

        var dist = Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) * Math.Cos(rlat2) * Math.Cos(rtheta);
        dist = Math.Acos(dist);
        dist = dist * 180 / Math.PI;
        dist = dist * 60 * 1.1515;

        dist = dist * 1.609344; // Conversion to kms
        return dist;
    }

And called it in my code : 并在我的代码中调用它:

double latitude = 0;
double longitude = 0;
var request = (from house in db.Houses
                                select 
                                new
                                {
                                    house,
                                    DistanceFromUser = BackboneDBEntitiesLocal.DistanceBetweenTwoPositions(latitude, longitude, house.Latitude.Value), house.Longitude.Value)) 
                                })
                                .Where(u=>u.DistanceFromUser <= range)
                                .OrderBy(u=>u.DistanceFromUser)
                                ;

But it doesn't work I get the following exception : 但它不起作用,我得到以下异常:

An error occurred while preparing definition of the function 'DataModel.DistanceBetweenTwoPositions'. 准备函数“ DataModel.DistanceBetweenTwoPositions”的定义时发生错误。 See the inner exception for details. 有关详细信息,请参见内部异常。

InnerException : InnerException:

System.Data.Entity.Core.EntitySqlException: 'DistanceBetweenTwoPositions' cannot be resolved into a valid type or function. System.Data.Entity.Core.EntitySqlException:'DistanceBetweenTwoPositions'无法解析为有效的类型或函数。 Near simple identifier, line 2, column 13. at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertMethodExpr(MethodExpr methodExpr, Boolean includeInlineFunctions, SemanticResolver sr) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertMethodExpr(Node expr, SemanticResolver sr) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.Convert(Node astExpr, SemanticResolver sr) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertValueExpressionAllowUntypedNulls(Node astExpr, SemanticResolver sr) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertQueryStatementToDbExpression(Statement astStatement, SemanticResolver sr, List 1& functionDefs) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.AnalyzeQueryCommand(Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<AnalyzeQueryExpressionSemantics>b__8(SemanticAnalyzer analyzer, Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeSemanticsCommon[TResult](Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable System.Data.Entity.Core.Core.Common.EntitySql.SemanticAnalyzer.ConvertMethodExpr(MethodExpr methodExpr,Boolean includeInlineFunctions,SemanticResolver sr)靠近简单标识符第2行第13列,位于System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.Convert(Node astExpr,SemanticResolver sr)在System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertValueExpressionAllowUntypedNull上的.ConvertMethodExpr(Node expr,SemanticResolver sr)在System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertQueryStatementToDbExpression(Statement astStatement,SemanticResolver sr,List 1& functionDefs) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.AnalyzeQueryCommand(Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<AnalyzeQueryExpressionSemantics>b__8(SemanticAnalyzer analyzer, Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeSemanticsCommon[TResult](Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable astExpr,SemanticResolver sr 1& functionDefs) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.AnalyzeQueryCommand(Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<AnalyzeQueryExpressionSemantics>b__8(SemanticAnalyzer analyzer, Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeSemanticsCommon[TResult](Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable 1& functionDefs) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.AnalyzeQueryCommand(Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<AnalyzeQueryExpressionSemantics>b__8(SemanticAnalyzer analyzer, Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeSemanticsCommon[TResult](Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable 1 parameters, IEnumerable 1 variables, Func 3 analysisFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeQueryExpressionSemantics(Node astQueryCommand, Perspective perspective, ParserOptions parserOptions, IEnumerable 1 parameters, IEnumerable 1 variables) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<>c__DisplayClass4.b__3(Node astCommand, ParserOptions validatedParserOptions) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileCommon[TResult](String commandText, ParserOptions parserOptions, Func 3 compilationFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileQueryCommandLambda(String queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable 1 parameters, IEnumerable 1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
1& functionDefs) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.AnalyzeQueryCommand(Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.<AnalyzeQueryExpressionSemantics>b__8(SemanticAnalyzer analyzer, Node astExpr) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeSemanticsCommon[TResult](Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable 1参数,IEnumerable 1 variables, Func 3 analysisFunction)位于System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeQueryExpressionSemantics(节点astQueryCommand,透视图,ParserOptions System.Data.Entity.Core.Common.EntitySql.CqlQuery。<> c__DisplayClass4.b__3(Node astCommand,ParserOptions validatedParserOptions)在System.Data.Entity.Core.Common.EntitySql处的parserOptions,IEnumerable 1 parameters, IEnumerable 1个变量。 CqlQuery.CompileCommon [TResult]( 3 compilationFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileQueryCommandLambda(String queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable 1参数,IEnumerable 1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
String commandText,ParserOptions parserOptions,Func 3 compilationFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileQueryCommandLambda(String queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable CompilationFunction 3 compilationFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileQueryCommandLambda(String queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable 1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
1 functionParameters, EdmItemCollection edmItemCollection) at System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.GenerateFunctionDefinition(EdmFunction function) at System.Data.Entity.Core.Common.Utils.Memoizer 2.<>c__DisplayClass2.<Evaluate>b__0() at System.Data.Entity.Core.Common.Utils.Memoizer 2.Result.GetValue()
1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
EdmFunction function) 1 variables)
at System.Data.Entity.Core.Mapping.ViewGeneration.Utils.ExternalCalls.CompileFunctionDefinition(String functionDefinition, IList
1 functionParameters,EdmItemCollection edmItemCollection)在System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.GenerateFunctionDefinition(EdmFunction function)在System.Data.Entity.Core.Common.Utils.Memoizer 2.<>c__DisplayClass2.<Evaluate>b__0() at System.Data.Entity.Core.Common.Utils.Memoizer System.Data.Entity.Core.Common.Utils.Memoizer 2.<>c__DisplayClass2.<Evaluate>b__0() at System.Data.Entity.Core.Common.Utils.Memoizer ()

at System.Data.Entity.Core.Common.Utils.Memoizer`2.Evaluate(TArg arg) at System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.GetGeneratedFunctionDefinition(EdmFunction function) at System.Data.Entity.Core.Metadata.Edm.MetadataWorkspace.GetGeneratedFunctionDefinition(EdmFunction function) at System.Data.Entity.Core.Query.PlanCompiler.ITreeGenerator.Visit(DbFunctionExpression e) 在System.Data.Entity.Core.Core.Common.Utils.Memoizer`2.Evaluate(TArg arg)在System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.GetGeneratedFunctionDefinition(EdmFunction function)在System.Data.Entity.Core System.Data.Entity.Core.Query.PlanCompiler.ITreeGenerator.Visit(DbFunctionExpression e)处的.Metadata.Edm.MetadataWorkspace.GetGeneratedFunctionDefinition(EdmFunction函数)

I've been following the answer of that article, thanks to him for the nice explanation : LINQ to Entities does not recognize the method 'Double Parse(System.String)' method, and this method cannot be translated into a store expression 我一直在关注该文章的答案,这要感谢他的出色解释: LINQ to Entities无法识别方法'Double Parse(System.String)',该方法无法转换为商店表达式

My first observation is should this even be in the EF mapping? 我的第一个观察结果是这是否应该在EF映射中? Conceptually does a Location know how to calculate a distance to another arbitrary point? 从概念上讲,位置是否知道如何计算到另一个任意点的距离? I'd actually put all this in separate class called DistanceCalculator that takes 2 arbitrary points and gives you back a distance. 实际上,我将所有这些都放在一个名为DistanceCalculator的单独的类中,该类获得2个任意点并为您提供一个距离。 This could then be called after you materialize your query results. 在实现查询结果后,可以调用此方法。

The issue in your implementation is that your function can't be converted back to SQL. 实现中的问题是您的函数无法转换回SQL。 EF doesn't know how to translate those Math.* functions to SQL. EF不知道如何将那些Math。*函数转换为SQL。 First replace all those Math.* calls with the corresponding calls in SqlFunctions . 首先,将所有这些Math。*调用替换为SqlFunctions中的相应调用。 This class has 这个班有

Provides common language runtime (CLR) methods that call functions in the database in LINQ to Entities queries. 提供公共语言运行时(CLR)方法,该方法在LINQ to Entities查询中调用数据库中的函数。

That should generate usable SQL for EF. 那应该为EF生成可用的SQL。

If you are using EF6+, you can move the logic of DistanceBetweenTwoPositions as a SQL scalar valued function. 如果使用的是EF6 +,则可以将DistanceBetweenTwoPositions的逻辑作为SQL标量值函数移动。

Update your EF model and import the Scalar valued function into your Model. 更新您的EF模型并将标量值函数导入模型。

If you have it mapped that way, you would be able to use it in your query like you have it. 如果您以这种方式进行了映射,则可以像在查询中一样在查询中使用它。

Hope that helps. 希望能有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM