【问题标题】:Add custom function to EntityFramework将自定义函数添加到 EntityFramework
【发布时间】:2017-06-24 00:24:28
【问题描述】:

我想在我的项目中添加一个函数,以便我可以过滤我的数据。

我希望该函数返回 2 个 GPS 位置之间的距离(以公里为单位)。

到目前为止,我已经完成了: 在我的 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>

创建了一个同名的部分类,以便能够定义该函数:

[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;
    }

并在我的代码中调用它:

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)
                                ;

但它不起作用我得到以下异常:

准备函数定义时出错 'DataModel.DistanceBetweenTwoPositions'。请参阅内部异常 详情。

内部异常:

System.Data.Entity.Core.EntitySqlException: 'DistanceBetweenTwoPositions' 无法解析为有效类型或 功能。靠近简单标识符,第 2 行,第 13 列。 System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertMethodExpr(MethodExpr methodExpr, Boolean includeInlineFunctions, SemanticResolver sr) at System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertMethodExpr(节点 expr, SemanticResolver sr) 在 System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.Convert(节点 astExpr, SemanticResolver sr) 在 System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertValueExpressionAllowUntypedNulls(节点 astExpr, SemanticResolver sr) 在 System.Data.Entity.Core.Common.EntitySql.SemanticAnalyzer.ConvertQueryStatementToDbExpression(语句 astStatement, SemanticResolver sr, List1& 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, IEnumerable1 参数, IEnumerable1 variables, Func3 分析函数)在 System.Data.Entity.Core.Common.EntitySql.CqlQuery.AnalyzeQueryExpressionSemantics(节点 astQueryCommand, 透视透视图, ParserOptions parserOptions, IEnumerable1 parameters, IEnumerable1 变量)在 System.Data.Entity.Core.Common.EntitySql.CqlQuery.c__DisplayClass4.b__3(节点 astCommand, ParserOptions 已验证ParserOptions) 在 System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileCommon[TResult](字符串 commandText, ParserOptions parserOptions, Func3 compilationFunction) at System.Data.Entity.Core.Common.EntitySql.CqlQuery.CompileQueryCommandLambda(String queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable1 参数, IEnumerable1 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 功能)在 System.Data.Entity.Core.Common.Utils.Memoizer2.<>c__DisplayClass2.<Evaluate>b__0() at System.Data.Entity.Core.Common.Utils.Memoizer2.Result.GetValue()
在 System.Data.Entity.Core.Common.Utils.Memoizer`2.Evaluate(TArg arg) 在 System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.GetGeneratedFunctionDefinition(EdmFunction 功能)在 System.Data.Entity.Core.Metadata.Edm.MetadataWorkspace.GetGeneratedFunctionDefinition(EdmFunction 功能)在 System.Data.Entity.Core.Query.PlanCompiler.ITreeGenerator.Visit(DbFunctionExpression e)

我一直在关注那篇文章的答案,感谢他的精彩解释:LINQ to Entities does not recognize the method 'Double Parse(System.String)' method, and this method cannot be translated into a store expression

【问题讨论】:

标签: c# entity-framework linq


【解决方案1】:

我的第一个观察是这是否应该在 EF 映射中?从概念上讲,位置是否知道如何计算到另一个任意点的距离?实际上,我会将所有这些放在名为 DistanceCalculator 的单独类中,该类需要 2 个任意点并返回一个距离。然后可以在具体化查询结果后调用它。

您的实现中的问题是您的函数无法转换回 SQL。 EF 不知道如何将这些 Math.* 函数转换为 SQL。首先将所有这些 Math.* 调用替换为 SqlFunctions 中的相应调用。这个类有

提供调用函数的公共语言运行时 (CLR) 方法 LINQ to Entities 查询中的数据库。

这应该会为 EF 生成可用的 SQL。

【讨论】:

    【解决方案2】:

    如果您使用的是 EF6+,则可以将 DistanceBetweenTwoPositions 的逻辑移动为 SQL 标量值函数。

    更新您的 EF 模型并将标量值函数导入您的模型。

    如果您以这种方式映射它,您将能够像拥有它一样在查询中使用它。

    希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-10-31
      • 2021-07-23
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 2021-04-09
      相关资源
      最近更新 更多