Related Articles:
How do I build Expression Call for Any Method with generic parameter
LINQ Expression Tree: Call Any method against a DbSet
I'd like to be able to dynamically construct a where statement that checks the values of the property again a list of values.
Statically the call might look like:
IEnumerable<Guid> ids = //.... list of guids
context.DbSet<Person>()
.Where(p => ids.Any(i => i == p.id))
.ToList();
Table
[Table("Person")]
class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
I receive data via JSON so it's not strongly typed:
class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
}
Mostly Working example relevant to core question:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var query = new Query
{
PropertyName = "id",
AnyValues = new List<Guid> { Guid.Empty }
};
var exp = ToExpression<Person>(query);
var people = context.DbSet<Person>().Where(exp).ToListAsync();
}
public static Expression ToExpression<TModel>(Query query)
{
Expression result = null;
var propInfo = typeof(TModel).GetProperty(query.PropertyName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
if (propInfo != null)
{
var propType = propInfo.PropertyType;
var parameterExpression = Expression.Parameter(typeof(TModel), nameof(TModel));
var memberExpression = Expression.Property(parameterExpression, query.PropertyName);
var constantExpression = GetConstantExpressionForType(propType, query.AnyValues);
if (memberExpression != null && constantExpression != null)
{
// https://stackoverflow.com/questions/34730432/how-do-i-build-expression-call-for-any-method-with-generic-parameter
// var org = Expression.Parameter(typeof(Organization), "org");
// Expression<Func<OrganizationField, bool>> predicate = a => a.CustomField.Name == filter.Name && values.Contains(a.Value);
// var body = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(OrganizationField) },
// Expression.PropertyOrField(org, "OrganizationFields"), predicate);
// var lambda = Expression.Lambda<Func<Organization, bool>>(body, org);
// STUCK HERE
result = Expression.Call(
typeof(Enumerable),
"Any",
?? );
}
}
return result;
}
// Just a Hack for the example, not relevant to the question itself
public static ConstantExpression GetConstantExpressionForType(Type type, object values)
{
ConstantExpression result = null;
if (type == typeof(Guid))
{
result = Expression.Constant(values, typeof(IEnumerable<Guid>));
}
else if (type == typeof(string))
{
result = Expression.Constant(values, typeof(IEnumerable<string>));
}
else if (type == typeof(int))
{
result = Expression.Constant(values, typeof(IEnumerable<int>));
}
return result;
}
}
public class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
public class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
//public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
public object AnyValues { get; set; } // to simply the core of the question
}