动态Linq查询包含列表

问题描述 投票:4回答:5

我正在使用动态Linq进行通用搜索。我有ID列表:

List<int> idList = new List<int> { 1, 5, 6};

在普通的Linq,我会写:

q = q.Where(a => idList.Contains(a.MyId));

但现在我必须使用System.Linq.Dynamic,因为我事先并不知道该列的名称。

string someId = "CustomId";
q = q.Where("@0"+ ".Contains(" + someId + ")", idList.ToArray());

但这会给出错误:

“类型'Int32'中不存在适用的方法'包含'”

我怎样才能做到这一点?

是否有一些扩展库为Contains Linq或其他方式实现dynamic

c# linq dynamic arraylist contains
5个回答
4
投票

您可以编写类似这样的内容来动态构建查询函数:

public static Func<ObjT, bool> PropertyCheck<ObjT, PropT>(string propertyName, Expression<Func<PropT, bool>> predicate)
{
    var paramExpr = Expression.Parameter(typeof(ObjT));
    var propExpr = Expression.Property(paramExpr, propertyName);
    return Expression.Lambda<Func<ObjT, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile();
}

然后,它可以像这样使用:

foos.Where(PropertyCheck<Foo, int>("MyId", x => idList.Contains(x)));

当然,您也可以提供自己的Where扩展方法,同时完成所有这些操作:

public static IEnumerable<T> Where<T, PropT>(this IEnumerable<T> self, string propertyName, Expression<Func<PropT, bool>> predicate)
{
    var paramExpr = Expression.Parameter(typeof(T));
    var propExpr = Expression.Property(paramExpr, propertyName);
    return self.Where<T>(Expression.Lambda<Func<T, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile());
}
foos.Where<Foo, int>("MyId", x => idList.Contains(x));

4
投票

您可以使用expressions执行此动态查询,尝试类似这样的示例:

导入这些命名空间:

using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

试试这个:

// a reference parameter
var x = Expression.Parameter(typeof (YourType), "x");

// contains method
var containsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)});

// reference a field
var fieldExpression = Expression.Property(instance, "PropertyName");

// your value
var valueExpression = Expression.Constant(yourId);

// call the contains from a property and apply the value
var containsValueExpression = Expression.Call(fieldExpression, containsMethod, valueExpression);

// create your final lambda Expression
var filterLambda = Expression.Lambda<Func<YourType, bool>>(containsValueExpression, x);

// apply on your query
q = q.Where(finalLambda);

Obs:确保您的属性有一个名为contains的方法。


1
投票

如果你看一下Dynamic LINQ的源代码,那么你可以看到在许多情况下解析依赖于变量predefinedTypes

在您的情况下,您需要像这样更改此变量

static readonly Type[] predefinedTypes = {
    ....
    ,typeof(List<int>)
};

之后,下一个代码将起作用

List<int> idList = new List<int> { 1, 5, 6};
....
string someId = "CustomId";
q = q.Where("@0.Contains(" + someId + ")", idList);

0
投票

皮肤这种猫的另一种方法是将包含转换为OR。

someArray.Constains(someField)相当于:

someField == someArray [0]或someField == someArray [1]等等。

这不是理想的,但如果阵列很小,它可以工作。


0
投票

@Felipe Oriani在90%的答案中使用了string.Contains方法和yourId单值,但问的是:

q = q.Where(a => idList.Contains(a.MyId));

这是成员(财产)访问a

所以这是最终测试的扩展方法:

/// <summary>
/// Creates lambda expression predicate: (TEntity entity) => collection.Contains(entity.property)
/// </summary>
public static Expression<Func<TEntity, bool>> ContainsExpression<TEntity, TProperty, TCollection>(
    this TCollection collection, 
    Expression<Func<TEntity, TProperty>> property
)
    where TCollection : ICollection<TProperty>
{
    // contains method
    MethodInfo containsMethod = typeof(TCollection).GetMethod(nameof(collection.Contains), new[] { typeof(TProperty) });

    // your value
    ConstantExpression collectionInstanceExpression = Expression.Constant(collection);

    // call the contains from a property and apply the value
    var containsValueExpression = Expression.Call(collectionInstanceExpression, containsMethod, property.Body);

    // create your final lambda Expression
    Expression<Func<TEntity, bool>> result = Expression.Lambda<Func<TEntity, bool>>(containsValueExpression, property.Parameters[0]);

    return result;
}

这个例子:

List<int> idList = new List<int> { 1, 5, 6 };

Expression<Func<MyEntity,int>> idExpression = entity => entity.Id;
var contains = idList.ContainsExpression(idExpression)

IQueryable<MyEntity> q = DbContext.Set<MyEntity>().Where(contains);
© www.soinside.com 2019 - 2024. All rights reserved.