C# 使用字段类型的泛型筛选构建动态Where子句
这个问题源于我发布的另一个封闭的问题 我有以下电话C# 使用字段类型的泛型筛选构建动态Where子句,c#,linq,C#,Linq,这个问题源于我发布的另一个封闭的问题 我有以下电话 var query = _context.Listings.AsQueryable(); query = query.WhereEqualIfSpecified(x => x.HasBalcony, true); query = query.ApplyRangeFilter(x => x.BedroomsAvailable, 1, 9); query = query.A
var query = _context.Listings.AsQueryable();
query = query.WhereEqualIfSpecified(x => x.HasBalcony, true);
query = query.ApplyRangeFilter(x => x.BedroomsAvailable, 1, 9);
query = query.ApplyRangeFilter(x => x.Baths, 1.0, 2.5);
query = query.ApplyRangeFilter(x => x.Price, 1000.00, 2000.00);
var listings = query.ToList();
我想将WhereEqualIfSpecified
设置为泛型,这样它不仅适用于bools,而且一旦我将方法更改为following
public static IQueryable<T> WhereEqualIfSpecified<T>(this IQueryable<T> query, Expression<Func<T, T>> fieldExpression, T filterValue)
{
return filterValue is null
? query
: query.Where(fieldExpression.Compose(value => value.Equals(filterValue)));
}
简而言之,我希望
WhereEqualIfSpecified
和ApplyRangeFilter
都是泛型的,这样它就可以接受任何类型,而不是使用重载函数您还需要一个泛型类型。在字段表达式中,表示func接受一个T
并返回一个T
:表达式字段表达式
。但是它应该使用T
并返回其他类型
请注意,您需要稍微更改调用,以使最小/最大值类型与属性类型匹配:query=query.ApplyRangeFilter(x=>x.Price,1000M,2000M)代码>
然而,数据列表似乎来自EF数据获取。请注意,这些类型的复杂筛选要求在客户端筛选数据,即获取所有数据,然后在应用程序中执行查询。如果有大量数据,这将是非常缓慢和昂贵的。我已经修复了指定为您答案的Whereequalifs,我试图解决ApplyRangeFilter过载的问题,但似乎我可以这样做,而不是复杂的通用版本使用更复杂的ApplyRangeFilter
编辑我的答案。如果有效,请将其标记为正确的解决方案。:-)上面的结果是内存中的过滤器vs sql,这是我试图避免的,您必须使查询更加简单,以便在数据库服务器上执行查询。所以这是你的问题,你要么关闭这个(根本没有提到),要么(最好)打开一个新的。但简单地说;你不能做任何表达式魔术而期望它变成一个SQL查询。我将在第一部分中标记这个答案,并在没有重载的情况下找出如何处理第二个答案
public partial class Listing
{
public decimal? Price { get; set; }
public int? BedroomsAvailable { get; set; }
public double? Baths { get; set; }
public bool? HasBalcony { get; set; }
public bool? HasElevator { get; set; }
}
public static class ExtensionMethods
{
public static IQueryable<T> WhereEqualIfSpecified<T>(this IQueryable<T> query, Expression<Func<T, bool?>> fieldExpression, bool? filterValue)
{
return filterValue is null
? query
: query.Where(fieldExpression.Compose(value => value.Equals(filterValue)));
}
public static IQueryable<T> ApplyRangeFilter<T>(this IQueryable<T> query, Expression<Func<T, int?>> filter, int? minValue, int? maxValue)
{
if (minValue is null && maxValue is null) return query;
if (maxValue != null && minValue != null)
{
return minValue == maxValue?
query.Where(filter.Compose(value => value.Equals(minValue))):
query.Where(filter.Compose(value => value >= minValue && value <= maxValue));
}
return query.Where(maxValue != null ? filter.Compose(value => value <= maxValue) : filter.Compose(value => value >= minValue));
}
//copied from https://stackoverflow.com/questions/37602729/convert-linq-expression-obj-obj-prop-into-parent-parent-obj-prop/37602870#37602870
public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
this Expression<Func<T, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
return Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
}
public class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression ex)
{
return ex == @from ? to : base.Visit(ex);
}
}
public static Expression Replace(this Expression ex,
Expression from,
Expression to)
{
return new ReplaceVisitor(from, to).Visit(ex);
}
}
public static IQueryable<TItem> WhereEqualIfSpecified<TItem, TFilterValue>(this IQueryable<TItem> query, Expression<Func<TItem, TFilterValue>> fieldExpression, TFilterValue filterValue)
{
return filterValue is null
? query
: query.Where(fieldExpression.Compose(value => value.Equals(filterValue)));
}
public static IQueryable<TItem> ApplyRangeFilter<TItem, TValue>(this IQueryable<TItem> query, Expression<Func<TItem, TValue?>> filter, TValue? minValue, TValue? maxValue)
where TValue: struct, IComparable
{
if (minValue is null && maxValue is null) return query;
if (maxValue != null && minValue != null)
{
return minValue.Value.CompareTo(maxValue.Value) == 0
? query.Where(filter.Compose(value => value != null && value.Value.Equals(minValue.Value)))
: query.Where(filter.Compose(value => value != null && value.Value.CompareTo(minValue.Value) >= 0 && value.Value.CompareTo(maxValue.Value) <= 0));
}
return query.Where(maxValue != null
? filter.Compose(value => value != null && value.Value.CompareTo(maxValue.Value) <= 0)
: filter.Compose(value => value != null && value.Value.CompareTo(minValue.Value) >= 0));
}