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