【问题标题】:Entity Framework execute query with not mapped properties. Expression tree实体框架使用未映射的属性执行查询。表达式树
【发布时间】:2016-06-01 12:25:15
【问题描述】:

我想在 iqueryable 上使用表达式树执行 linq 方法,该函数来自我传递 linq 方法名称和属性名称的函数。但我的示例方法仅适用于映射属性。例如,当我尝试查找计算属性的最大值时,它会引发异常。

我的课:

    public partial class Something
    {
        public int a { get; set; }
        public int b { get; set; }
    }

    public partial class Something
    {
        public int calculated { get { return a * b; } }
    }

示例方法:

public static object ExecuteLinqMethod(IQueryable<T> q, string Field, string Method)
    {
        var param = Expression.Parameter(typeof(T), "p");

        Expression prop = Expression.Property(param, Field);

        var exp = Expression.Lambda(prop, param);

            Type[] types = new Type[] { q.ElementType, exp.Body.Type };
            var mce = Expression.Call(typeof(Queryable),Method,types,q.Expression,exp);

            return q.Provider.Execute(mce);
    }

【问题讨论】:

  • 为什么你认为表达式树可以让你做一些 EF 查询提供者不支持的事情?
  • 我需要通用方法来查找映射和未映射属性的最大值、最小值、总和等。使用给定的函数名称和属性名称作为此方法的参数。可以通过表达式树或其他方式完成。
  • 那你为什么不先找到“其他方式”,如果需要的话再寻求表达式树的帮助。我想主要问题是如何确定该字段是否已映射。即使你这样做了,由于无法知道计算出的属性正在使用什么,唯一的执行方法是针对IEnumerable&lt;T&gt;执行它。
  • 我尝试了其他方法但没有成功。我有大型数据库并将所有记录获取到 IEnumerable 导致内存不足异常。
  • 嗯,那么 IMO 除了传递包含仅基于映射字段而不是字段名称的计算的 Expression 之外,没有其他解决方案。

标签: c# entity-framework linq expression-trees


【解决方案1】:

要能够查询计算的属性,您至少有 2 个选项:

1)您将计算值与行(或在不同的表中)一起存储在数据库中,并在查询中使用它们当然这需要数据模型更改和数据冗余,但这是最高效的方式。但不是那么令人兴奋,所以让我们继续

2) 您需要能够以 sql 能够理解的方式表达您“计算”属性的方式,这意味着需要在最终查询中将属性替换为 linq 表达式。我在 2009 年发现了 Eric Lippert 的一篇关于注册内联此类属性的精彩文章,但我再也找不到它了。因此,这里是另一个 link ,具有相同的想法。基本上,您将计算定义为表达式树,并在代码中使用编译后的版本。

为方便起见,您可以使用

[AttributeUsage(AttributeTargets.Property)]
class CalculatedByAttribute: Attribute
{
    public string StaticMethodName {get; private set;}
    public CalculatedByAttribute(string staticMethodName)
    {
        StaticMethodName = staticMethodName;
    }
}

喜欢:

public partial class Something
{
    [CalculatedBy("calculatedExpression")]
    public int calculated { get { return calculatedExpression.Compile()(this); } }
    public static Expression<Func<Something, int>> calculatedExpression = s => s.a * s.b;
}

(当然你可以缓存编译):)

然后在您的方法中,如果属性具有您的属性,您将获得静态属性值,并在查询中使用它。顺带一提:

public static object ExecuteLinqMethod<T>(IQueryable<T> q, string Field, string Method)
{
    var propInfo = typeof(T).GetProperty(Field);
    LambdaExpression exp;
    var myAttr = propInfo.GetCustomAttributes(typeof(CalculatedByAttribute), true).OfType<CalculatedByAttribute>().FirstOrDefault();
    if (myAttr != null)
        exp = (LambdaExpression)typeof(T).GetField(myAttr.StaticMethodName, BindingFlags.Static | BindingFlags.Public).GetValue(null);
    else
    {
        var param = Expression.Parameter(typeof(T), "p");
        Expression prop = Expression.Property(param, Field);
        exp = Expression.Lambda(prop, param);
    }

    Type[] types = new Type[] { q.ElementType, exp.Body.Type };
    var mce = Expression.Call(typeof(Queryable),Method,types,q.Expression,exp);

    return q.Provider.Execute(mce);
}

【讨论】:

  • 谢谢!它适用于计算映射属性,但我不知道如何处理基于映射和计算字段的计算。当我尝试使用嵌套表达式时,我收到错误“LINQ to Entities 不支持 LINQ 表达式节点类型‘Invoke’。”。例如,我尝试过这样的事情: [CalculatedBy("calculatedExpression1")] public int computed1 { get { return computedExpression1.Compile()(this); } } public static Expression> computedExpression1 = s => s.a * computedExpression.Compile()(s);
  • 你确定是我? 2009年是很久以前的事了,但我不记得写过这样的文章。我的微软同事马特·沃伦(Matt Warren)当时写了一些关于这类事情的文章。也许您正在考虑其中之一?
  • @AdamBubula:如果您需要构建更复杂的场景,请查看此答案:stackoverflow.com/questions/29448432/…
  • @EricLippert: 抱歉,我记错了,这篇文章是少数几篇不是你写的(我读过的)好文章之一:P 实际上在你的评论之后我找到了它,今天整个这个东西叫做 Microsoft.Linq.Translations 项目(nuget 包)。AdamBubula:随意使用它,它有更好的注册语法:)
  • @MBoros 我尝试使用问题链接中的方法,但仍然收到错误“不支持'Invoke'”。在链接问题中,有一个谓词传递给 where 函数,在我的情况下,我想在计算中使用嵌套表达式。我的示例表达式。 public static Expression> CalculatedExpressionWrapped() { return s => s.a + s.b; } public static Expression> NextCalculatedExpression = s => Math.Round(CalculatedExpressionWrapped().AsQuote()(s) / s.d,2);
猜你喜欢
  • 1970-01-01
  • 2015-11-27
  • 1970-01-01
  • 2015-01-22
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 2014-08-08
  • 1970-01-01
相关资源
最近更新 更多