我需要针对返回的列表编写LINQ查询,该列表包含多个可选参数。将有以下变量:
计划, ID, 名字, 姓, DateFrom, DateTo, MemDateOfBirth
我想使用这些参数通过LINQ返回过滤后的列表,但所有参数都是可选的。当用户点击搜索按钮时,将提供至少一个,但是由用户决定他们想要搜索什么。如果它们提供的数量超过1,我需要按照它们提供的所有方法进行过滤......
因此,例如,如果他们提供了一个名字以及往返日期,我希望返回一个人的所有实例的过滤列表,该名称由起始日期之间的第一个名称等等...
使用LINQ实现此目的的最简单方法是什么?这些变量是可选参数,因此可以提供任何或所有变量。我知道我可以返回主列表,然后多次过滤它以获得结果,但我想知道是否有更快,更简单的方法通过LINQ来做...
提前感谢您的帮助!
答案 0 :(得分:2)
我发现这是解决此类问题的最简单方法
var q = from mt in myTable
where (mt.FIrstname == FirstNameparam || FirstNameparam == null)
&& (mt.lastname == lastnameParam || lastnameParam == null)
&& (mt.DateField == DateParam || DateParam == null)
select new
{
mt.FIrstname,
mt.lastname,
mt.DateField
};
答案 1 :(得分:1)
昨天我在面试中被问到了这个问题,所以这是我的想法:)
以前的所有答案(动态LINQ,多个Where
子句)都不会让您摆脱检查是否指定了可选参数的代码-例如,要使用dLINQ,您仍然必须创建一个“连接的字符串。这些串联将基于您是否存在可选参数,因此,如果您可以以相同的方式直接创建Where
序列,那么为什么要创建字符串(或为什么要设置过滤器列表)?
好吧,老实说,我的方法也包含了这种逻辑,但是您不会看到它(嗯..也许只是一点点:))。
因此,我们要使用的是:
if-Where
)首先,让我向您展示它如何工作。假设我们有一个模型:
public class Person
{
public string Name { get; }
public int Age { get; }
public Person( string name, int age )
{
Name = name;
Age = age;
}
}
您也将拥有。。我们将其称为Filtering object
。
整洁的奖金!例如,如果您使用ASP.Net Core,则可以使用GET
,以[FromQuery]
方法自动获得此对象。
public class PersonFilterParams : IFilterParams<Person>
{
[Filter( FilterType.Equals )]
public string Name { get; set; }
[Filter( nameof( Person.Age ), FilterType.GreaterOrEquals )]
public int? MinAge { get; set; }
[Filter( nameof( Person.Age ), FilterType.LessOrEquals )]
public int? MaxAge { get; set; }
[Filter( nameof( NonExistingProp ), FilterType.LessOrEquals )]
public int? NonExistingProp { get; set; }
}
这是使用方法:
// you can skip properies here
var filter = new PersonFilterParams
{
//Name = "Name 4",
//MinAge = 2,
MaxAge = 20,
NonExistingProp = 20,
};
var filteredPersons = persons
.Filter( filter )
.ToList();
仅此而已!
现在让我们看看它是如何实现的。
简而言之:
我们有扩展方法,该方法接受Filtering对象,使用反射将其分解,因为非null属性使用表达式创建lambda,并将这些lambda添加到源IEnumerable<T>
。
对于类型安全过滤对象必须实现通用接口IFilterParams<T>
。
代码:
public enum FilterType
{
None,
Less,
LessOrEquals,
Equals,
Greater,
GreaterOrEquals,
}
[AttributeUsage( AttributeTargets.Property, Inherited = false, AllowMultiple = false )]
sealed class FilterAttribute : Attribute
{
public string PropName { get; }
public FilterType FilterType { get; }
public FilterAttribute() : this( null, FilterType.Equals )
{
}
public FilterAttribute( FilterType filterType ) : this( null, filterType )
{
}
public FilterAttribute( string propName, FilterType filterType )
{
PropName = propName;
FilterType = filterType;
}
}
public interface IFilterParams<T>
{
}
public static class Extensions
{
public static IEnumerable<T> Filter<T>( this IEnumerable<T> source, IFilterParams<T> filterParams )
{
var sourceProps = typeof( T ).GetProperties();
var filterProps = filterParams.GetType().GetProperties();
foreach ( var prop in filterProps )
{
var filterAttr = prop.GetCustomAttribute<FilterAttribute>();
if ( filterAttr == null )
continue;
object val = prop.GetValue( filterParams );
if ( val == null )
continue;
// oops.. little hole..
if ( prop.PropertyType == typeof( string ) && (string)val == string.Empty )
continue;
string propName = string.IsNullOrEmpty( filterAttr.PropName )
? prop.Name
: filterAttr.PropName;
if ( !sourceProps.Any( x => x.Name == propName ) )
continue;
Func<T, bool> filter = CreateFilter<T>( propName, filterAttr.FilterType, val );
source = source.Where( filter );
}
return source;
}
private static Func<T, bool> CreateFilter<T>( string propName, FilterType filterType, object val )
{
var item = Expression.Parameter( typeof( T ), "x" );
var propEx = Expression.Property( item, propName );
var valEx = Expression.Constant( val );
Expression compareEx = null;
switch ( filterType )
{
case FilterType.LessOrEquals:
compareEx = Expression.LessThanOrEqual( propEx, valEx );
break;
case FilterType.Less:
compareEx = Expression.LessThan( propEx, valEx );
break;
case FilterType.Equals:
compareEx = Expression.Equal( propEx, valEx );
break;
case FilterType.Greater:
compareEx = Expression.GreaterThan( propEx, valEx );
break;
case FilterType.GreaterOrEquals:
compareEx = Expression.GreaterThanOrEqual( propEx, valEx );
break;
default:
throw new Exception( $"Unknown FilterType '{filterType}' on property '{propName}'!" );
}
var lambda = Expression.Lambda<Func<T, bool>>( compareEx, item );
Func<T, bool> filter = lambda.Compile();
return filter;
}
}
答案 2 :(得分:0)
您可以使用EntityFramework.DynamicFilters来创建动态过滤器,也可以在linq中动态创建where子句。这是一个很好的教程,如何动态创建where子句。
https://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq
这个过滤器创建有点乏味,但这符合您的目的。
答案 3 :(得分:0)
假设您不想对许多表进行概括,我会做类似的事情:
var query =
from m in db.table
select m;
if (plan.HasValue)
{
query = query.Where( x => x.plan == plan.Value);
}
... other filters ....
// Then use data (for ex. make a list).
var list = query.ToList();
根据提供程序的不同,您也可以直接在LINQ中编写条件,例如:
from m id db.table
where plan == null || m.plan == plan
where...
select m;
或使用!plan.HasValue || m.plan == plan
。
然而,这更脆弱,因为并非所有提供商都以相同的方式处理这些情况或支持它们。如果相应的列允许为null,则需要特别小心。