Dynamic Linq query Contains List
Solution 1
You could write something like this that builds your query function dynamically:
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();
}
Then, it could be used like this:
foos.Where(PropertyCheck<Foo, int>("MyId", x => idList.Contains(x)));
Of course, you could also just provide your own Where
extension method that does all that at once:
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));
Solution 2
You could use the expressions
to do this dynamic query, try something like this, for sample:
import these namespaces:
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
And try this:
// 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: make sure your property has a method called contains
.
Solution 3
If you look at the source of Dynamic LINQ then you can see that parsing in many cases depends on variable predefinedTypes
.
In your case you need change this variable like this
static readonly Type[] predefinedTypes = {
....
,typeof(List<int>)
};
after that next code will be work
List<int> idList = new List<int> { 1, 5, 6};
....
string someId = "CustomId";
q = q.Where("@0.Contains(" + someId + ")", idList);
Solution 4
@Felipe Oriani in his 90% answer used the string.Contains
method and the yourId
single value, but asked was:
q = q.Where(a => idList.Contains(a.MyId));
which is member (property) access to the a
.
So here is the final tested extension method:
/// <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;
}
The example:
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);
Related videos on Youtube
borisdj
IT consultant, .NET developer, believer in science and technology
Updated on June 30, 2022Comments
-
borisdj almost 2 years
I am using dynamic Linq for generic search. I have list of Ids:
List<int> idList = new List<int> { 1, 5, 6};
In plain Linq, I would write:
q = q.Where(a => idList.Contains(a.MyId));
But now I have to use
System.Linq.Dynamic
because I don't know in advance name of the column.string someId = "CustomId"; q = q.Where("@0"+ ".Contains(" + someId + ")", idList.ToArray());
But this gives error:
"No applicable method 'Contains' exists in type 'Int32'"
How can I achieve this?
Is there some extension library that implements
Contains
fordynamic
Linq or some other way. -
borisdj about 10 yearsWhere would put these predefinedTypes. Did you mean to override DynamicLinq.cs class or to extended these types somehow?
-
Grundy about 10 years@Boris i mean edit this variable in
DynamicLinq.cs
-
borisdj about 10 yearsI have installed Linq.Dynamic via nuget and it is a library so I can't edit code. I know I can download it's source from github and then change it but I would prefer not having to do that.
-
Grundy about 10 years@Boris in some cases it may be useful :-)
-
borisdj about 10 yearsthat I agree. +1 for usefulness.
-
Franki1986 almost 7 yearsIs there a way to create it in a string only way ? Something like q = q.Where("(new List<int>{ 1, 5, 6}).Contains(outerIt.CustomId));
-
Grundy almost 7 years@Franki1986, not sure, that DynamicLinq can parse all language construction, so possibly you can do this with a bit another syntax, or can't. But you can try :) I not watch to this library, so possibly even this workaround not needed
-
Franki1986 almost 7 yearsSorry for the bad question, I tried it but this does not work, I mean is this possible with another syntax.. Theretically this is nothing but a static array..
-
Grundy almost 7 years@Franki1986, but to parse it, library can be more complicated, so seems, workaround with parameter is one way, to pass constant list to query. But, as i say before, possibly something has changed now :)
-
Robin Ding over 3 yearsI like this solution more. But it has problem when I use string[] instead of List<string>, because it cant find the Contains method. I fix the issue by changing to
var containsMethod = typeof(ICollection<TProperty>).GetMethod(nameof(ICollection<TProperty>.Contains), new[] { typeof(TProperty) });