使用Entity Framework 6调用数据库函数

15
我按照这些说明将一个标量函数添加到我的Entity Framework 6数据模型中。 如何在linq to entity中使用标量值函数? 然而,我无法在LINQ查询中调用该函数,尽管在DataContext上直接调用该方法是有效的。
using (Entities context = new Entities()) {
    // This works.
    var Test1 = context.fn_GetRatingValue(8, 9, 0).FirstOrDefault();
    // This doesn't work.
    var Test2 = (from r in context.MediaRatings
                select context.fn_GetRatingValue(r.Height, r.Depth, 0)).ToList();
}

第二个查询语句出现了错误。
LINQ to Entities does not recognize the method 'System.Data.Entity.Core.Objects.ObjectResult`1[System.Nullable`1[System.Single]] fn_GetRatingValue(System.Nullable`1[System.Single], System.Nullable`1[System.Single], System.Nullable`1[System.Single])' method, and this method cannot be translated into a store expression.

此外,设计师向我发出了警告。
Error 6046: Unable to generate function import return type of the store function 'fn_GetRatingValue'. The store function will be ignored and the function import will not be generated.

我做错了什么?如何在LINQ查询中调用数据库函数?
此外,如果查询代码有时针对数据库执行,有时在内存中执行,是否有一种方法可以调用该函数以适用于两种情况?我有一个相同功能的C#版本。
谢谢!
编辑:这是我试图使用的函数。
public float? GetValue(float? Height, float? Depth, float ratio) {
    if (Height != null || Depth != null) {
        float HeightCalc = Height ?? Depth.Value;
        float DepthCalc = Depth ?? Height.Value;
        if (ratio < 0)
            DepthCalc = DepthCalc + (HeightCalc - DepthCalc) * -ratio;
        else if (ratio > 0)
            HeightCalc = HeightCalc + (DepthCalc - HeightCalc) * ratio;
        return (float)Math.Round(HeightCalc * DepthCalc * .12, 1);
    } else
        return null;
}

这段代码也可以写成一行。我可以在需要使用的任何地方复制/粘贴这行代码,但那会产生非常丑陋的代码,虽然这样也可以运行。我更愿意将其保留为一个函数。

return (float)Math.Round(
    (Height.HasValue ? Height.Value + (ratio > 0 ? ((Depth ?? Height.Value) - Height.Value) * ratio : 0) : Depth.Value) *
    (Depth.HasValue ? Depth.Value + (ratio < 0 ? ((Height ?? Depth.Value) - Depth.Value) * -ratio : 0) : Height.Value)
    * .12, 1);

有人对这个有什么想法吗? - Etienne Charland
我通过在EDMX文件中将所有函数参数名称改为小写来消除了6046警告,但我仍然无法在查询中使用该函数。 - Etienne Charland
以这种方式做事很麻烦,这可能是人们不想解决这个问题的原因。我会用SQL来做所有的事情:将其创建为视图。然后将该视图添加为模型类(或在使用Visual Studio中的Code First生成实体框架连接“ADO .NET Entity”时让它为您执行)。然后,您只需使用该模型来检索结果。然后,您可以对返回的数据集进行LINQ查询,或使用foreach循环提取数据,并使用if语句设置您的标准,比较高度/深度值与您的比率。 - vapcguy
1个回答

16

我找到了答案。虽然我在有关实体框架6中EdmFunctionAttribute已被弃用的文档中找到的信息很少,但我设法使此代码工作。

在EDMX文件中,IsComposable必须为True并且必须删除CommandText。我只需要函数声明而不需要函数导入。

然后,在我的数据上下文的部分类中,我创建了这个函数。

[DbFunction("NaturalGroundingVideosModel.Store", "fn_GetRatingValue")]
public float? DbGetValue(float? height, float? depth, float ratio) {
    List<ObjectParameter> parameters = new List<ObjectParameter>(3);
    parameters.Add(new ObjectParameter("height", height));
    parameters.Add(new ObjectParameter("depth", depth));
    parameters.Add(new ObjectParameter("ratio", ratio));
    var lObjectContext = ((IObjectContextAdapter)this).ObjectContext;
    var output = lObjectContext.
            CreateQuery<float?>("NaturalGroundingVideosModel.Store.fn_GetRatingValue(@height, @depth, @ratio)", parameters.ToArray())
        .Execute(MergeOption.NoTracking)
        .FirstOrDefault();
    return output;
}

我将该函数添加到MediaRating对象中,以便在不需要对数据上下文进行引用的情况下调用它。

var Test2 = (from r in context.MediaRatings
    select r.DbGetValue(r.Height, r.Depth, 0)).ToList();

这个有效!


4
我刚刚意识到在构建LINQ查询时,DbGetValue代码甚至不会被调用!因此,你可以删除函数中的代码,或者在不调用数据库的情况下计算它,因为该代码仅在直接调用查询之外进行调用。 - Etienne Charland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接