实体框架执行没有映射属性的查询。表达式树

时间:2016-06-01 12:25:15

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

我想在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);
    }

1 个答案:

答案 0 :(得分:1)

为了能够查询计算出的属性,您至少有两个选项:

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

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