问题描述
主要任务
为实体框架创建 IQueryable 扩展,可以使用 MemberExpression 设置查询中使用的字段。
WhereOverlap 就是一个例子。 WhereOverlap 通过两个 DateTime 字段构建对实体的查询。
Orders 有 DateTime 字段 StartDate、EndDate。
Trips 有 DateTime 字段 From、To。
var dateFrom = DateTime.Now.AddDays(-30);
var dateto = DateTime.Now.AddDays(-15);
var orders = await db.Orders
.WhereOverlap(
// MemberExpressions (Orders has StartDate,EndDate fields)
fromField: es => es.StartDate,toField: es => es.EndDate,from: dateFrom,to: dateto)
.ToListAsync();
var trips = await db.Trips
.WhereOverlap(
// MemberExpressions (Trips has From,To fields)
fromField: es => es.From,toField: es => es.To,to: dateto)
.ToListAsync();
为了实现我们想要的,我们需要回答以下两个问题中的任何一个:
或问题 1
要创建此类 IQueryable 扩展,需要创建具有以下签名的 Where 扩展:
public static class QueryableWhereOverlapExtensions
{
static IQueryable<TEnt> Where<TEnt,TParam,TParam2>(this IQueryable<TEnt> source,Expression<Func<TEnt,TParam>> property,TParam2>> property2,Expression<Func<TParam,TParam2,bool>> where)
{
// ??? HOW TO DO THIS
return ...
}
public static IQueryable<TEnt> WhereOverlap<TEnt>(this IQueryable<TEnt> source,DateTime>> startField,DateTime>> endField,DateTime from,DateTime to)
{
// example of usage Where extension
return source.Where(startField,endField,(start,end) => start <= to && end >= from);
}
}
如何实现带有显示签名的 IQueryable Where 扩展?
或问题 2
可以使用 A universal PredicateBuilder 构建动态查询表单表达式 (Expression
static Expression<Func<TEnt,bool>> ApplayWhere<TEnt,TParam>(this Expression<Func<TEnt,TParam>> field,bool>> where)
{
// ??? HOW TO DO THIS
}
public static IQueryable<TEnt> WhereOverlap<TEnt>(this IQueryable<TEnt> source,DateTime to)
{
// example of usage ApplayWhere
Expression<Func<TEnt,bool>> startExpr = startField.ApplayWhere(start => start <= to);
Expression<Func<TEnt,bool>> endExpr = endField.ApplayWhere(end => end >= from);
return source.Where(PredicateBuilder.And(startExpr,endExpr));
}
如何实现 ApplayWhere?
解决方法
这不完全是一个答案。但是我创建了 WhereOverlap 扩展,并提供了一种创建可重用 linq 查询的方法,可以使用 MemberExpression 来设置查询中使用的字段。
首先我们需要 MemberExpressionExtensions:
public static class MemberExpressionExtensions {
public static Expression<Func<TEnt,bool>> HasVal<TEnt,TProp>(this Expression<Func<TEnt,TProp?>> field) where TProp : struct
=> Expression.Lambda<Func<TEnt,bool>>(Expression.NotEqual(field.Body,Expression.Constant(null,typeof(TProp?))),field.Parameters);
public static Expression<Func<TEnt,bool>> HasNoVal<TEnt,bool>>(Expression.Equal(field.Body,bool>> LessOrEqual<TEnt,TProp>> field,TProp val)
=> Expression.Lambda<Func<TEnt,bool>>(Expression.LessThanOrEqual(field.Body,Expression.Constant(val,typeof(TProp))),bool>> Less<TEnt,bool>>(Expression.LessThan(field.Body,bool>> GreaterOrEqual<TEnt,bool>>(Expression.GreaterThanOrEqual(field.Body,bool>> Greater<TEnt,bool>>(Expression.GreaterThan(field.Body,field.Parameters);
}
现在我们可以创建可重用的查询构建器
class BigPayFilter {
readonly decimal Limit;
public BigPayFilter(decimal limit) {
Limit = limit;
}
public Expression<Func<TEnt,bool>> Create<TEnt>(
Expression<Func<TEnt,decimal>> field) {
// GreaterOrEqual is extension
return field.GreaterOrEqual(Limit);
}
}
使用。 例如,有 Payout 和 Premium 对象:
class Payout {
public decimal Total { get; set; }
}
class Premium {
public decimal Sum { get; set; }
}
// filter to find payments greater or equal 1000
//
// you can get limit value from configuration (in this example limit is 1000),// and put BigPayFilter to IoC-container
var bigPayFilter = new BigPayFilter(1000);
// use BigPayFilter for payouts
var payoutPredicate =
bigPayFilter.Create<Payout>(pp => pp.Total);
var payouts = new[] {
new Payout{ Total = 100 },new Payout{ Total = 50 },new Payout{ Total = 25.5m },new Payout{ Total = 1050.67m }
}
.AsQueryable()
.Where(payoutPredicate)
.ToList();
// use BigPayFilter for premiums
var premiumPredicate =
bigPayFilter.Create<Premium>(pp => pp.Sum);
var premiums = new[] {
new Premium{ Sum = 2000 },new Premium{ Sum = 50.08m },new Premium{ Sum = 25.5m },new Premium{ Sum = 1070.07m }
}
.AsQueryable()
.Where(premiumPredicate)
.ToList();
对于更复杂的查询,您必须将 MemberExpressionExtensions 与 Pete Montgomery 创建的“A universal PredicateBuilder”结合起来。
此解决方案完全支持实体框架,包括异步操作。