我制作了一个动态报表构建器,允许用户从预定义类(通过Entity Framework映射到数据库表)中选择字段作为其数据的过滤器。为了构建我的LINQ to Entities查询,由于查询的动态特性,我使用了表达式树。我让它适用于几乎所有非自定义场景,但我真的很难让它适用于一些自定义场景。
我的一个自定义查询的模型的缩写版本如下所示:
public class Attendee {
public int ID { get; set; }
public DateTime? CancelledOn { get; set; }
[ForeignKey("Event")]
public int Event_ID { get; set; }
public virtual Event Event { get; set; }
}
public class Event {
public int ID { get; set; }
public virtual ICollection<Attendee> Attendees { get; set; }
}
用户想要运行的示例查询是过滤以仅显示具有超过10个未被取消的与会者的事件。如果我在正常的IQueryable查询中写这个,我会把它写成
db.Event.Where(s => s.Attendees.Count(a => a.CancelledOn == null) > 10);
我已经设置了表达式树框架,我可以处理&#34;&gt; 10&#34;部分已经,但我无法弄清楚如何动态生成&#34; s.Attendees.Count(a =&gt; a.CancelledOn == null)&#34;部分。我已经阅读过有关在顶级属性上执行Count或Sum的SO帖子,但我无法破解任何这些解决方案来处理过滤导航属性。帖子示例:Dynamic LINQ, Select function, works on Enumerable, but not Queryable
下面的屏幕截图是使用表达式树构建的不同过滤器的示例,因此您可以看到我工作的示例。 &#34; PE&#34;是传入的Type的ParameterExpression,&#34; Event&#34;。 &#34;表达&#34;是我试图创造和评估的东西。 http://grab.by/RoYm
上面运行的LINQ查询是
db.Event.Where(s=> s.StartDate >= '1/1/2016 12:00 am')
如果我需要包含任何其他代码段,请随时与我们联系。
答案 0 :(得分:3)
不确定您正在寻找的方法的输入参数是什么,但以下内容应该为您提供一个起点。关键部分是构建对Enumerable.Count(predicate)
方法的方法调用。
static Expression<Func<TSource, bool>> MakeCountPredicate<TSource>(string collectionName, string itemName, ExpressionType itemComparison, string itemValue, ExpressionType countComparison, int countValue)
{
var source = Expression.Parameter(typeof(TSource), "s");
var collection = Expression.Property(source, collectionName);
var itemType = collection.Type.GetInterfaces()
.Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.GetGenericArguments()[0];
var item = Expression.Parameter(itemType, "e");
var itemProperty = Expression.Property(item, itemName);
var itemPredicate = Expression.Lambda(
Expression.MakeBinary(itemComparison, itemProperty, Expression.Constant(
string.IsNullOrEmpty(itemValue) || itemValue.Equals("null", StringComparison.OrdinalIgnoreCase) ? null :
Convert.ChangeType(itemValue, itemProperty.Type))),
item);
var itemCount = Expression.Call(
typeof(Enumerable), "Count", new[] { itemType },
collection, itemPredicate);
var predicate = Expression.Lambda<Func<TSource, bool>>(
Expression.MakeBinary(countComparison, itemCount, Expression.Constant(countValue)),
source);
return predicate;
}
所以样本谓词表达式
Expression<Func<Event, bool>> predicate =
s => s.Attendees.Count(a => a.CancelledOn == null) > 10
可以像这样动态构建
var predicate = MakeCountPredicate<Event>("Attendees",
"CancelledOn", ExpressionType.Equal, "null", ExpressionType.GreaterThan, 10);
答案 1 :(得分:0)
要为Expression
生成Count
树,您应该为相应的Call
生成Method
。
这是代码的初稿......
Expression callExpr = Expression.Call(
Expression.Constant(s.Attendees),
typeof(ICollection<Attendee>).GetMethod("get_Count")); // + 2 Arguments
当然,你必须详细说明并将其合并到主程序中。
基本用法示例是
// Print out the expression.
Debug.WriteLine(callExpr.ToString());
// The following statement first creates an expression tree,
// then compiles it, and then executes it.
Debug.WriteLine(Expression.Lambda<Func<int>>(callExpr).Compile()());
最后, Count Expression
(NodeType Call
)必须包含2个参数(作为第三个参数,未在上面显示):
Lambda
的{{1}}