I have to use Expression from LINQ to evaluate some data. I have created this small test program. The first example works find. It returns TRUE if any string is "abc".
public class ContainProgramClass
{
static void Main(string[] args)
{
ParameterExpression instance = Expression.Parameter(typeof(ContainClass), "item");
var mainClass = new ContainClass { Data = new List<string> { "abc", "def" } };
var expression = mainClass.CreateExpression(instance);
var lambda = Expression.Lambda<Func<ContainClass, bool>>(expression, instance);
Func<ContainClass, bool> f = lambda.Compile();
var result = f(mainClass);
Console.WriteLine($"Result: {result}"); // Result true
mainClass.Data = new List<string> { "foo", "bar" };
result = f(mainClass);
Console.WriteLine($"Result: {result}"); // Result false
Console.ReadKey();
}
}
public class ContainClass
{
private static readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(
BindingFlags.Static | BindingFlags.Public)
.Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);
private static readonly MethodInfo EnumerableCastMethod = typeof(Enumerable).GetMethod("Cast");
private static MethodInfo GenericContainsMethod = MethodContains.MakeGenericMethod(typeof(object));
public List<string> Data { get; set; }
public Expression CreateExpression(Expression instance)
{
string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
MethodCallExpression genericCollectionPropertyAccessor = Expression.Call(null
, EnumerableCastMethod.MakeGenericMethod(new[] { typeof(object) })
, collectionPropertyAccessor);
var distinctValueConstant = Expression.Constant("abc");
var containsExpression = Expression.Call(
ContainClass.GenericContainsMethod,
genericCollectionPropertyAccessor,
distinctValueConstant);
return containsExpression;
}
}
However, I would like for it to return TRUE if any SUBstring is "abc". Like this:
public class AnyProgramClass
{
static void Main(string[] args)
{
ParameterExpression instance = Expression.Parameter(typeof(ContainClass), "item");
var mainClass = new ContainClass { Data = new List<string> { "abcef", "ghi" } };
var expression = mainClass.CreateExpression(instance);
var lambda = Expression.Lambda<Func<ContainClass, bool>>(expression, instance);
Func<ContainClass, bool> f = lambda.Compile();
var result = f(mainClass);
Console.WriteLine($"Result: {result}"); // Result true
mainClass.Data = new List<string> { "abc", "bar" };
result = f(mainClass);
Console.WriteLine($"Result: {result}"); // Result true
mainClass.Data = new List<string> { "foo", "bar" };
result = f(mainClass);
Console.WriteLine($"Result: {result}"); // Result false
Console.ReadKey();
}
}
public class MainClass1
{
private static readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(
BindingFlags.Static | BindingFlags.Public)
.Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);
private static readonly MethodInfo MethodAny = typeof(Enumerable).GetMethods(
BindingFlags.Static | BindingFlags.Public)
.Single(m => m.Name == nameof(Enumerable.Any) && m.GetParameters().Length == 2);
private static readonly MethodInfo EnumerableCastMethod = typeof(Enumerable).GetMethod("Cast");
private static MethodInfo GenericContainsMethod = MethodContains.MakeGenericMethod(typeof(object));
private static MethodInfo GenericAnyMethod = MethodAny.MakeGenericMethod(typeof(object));
public List<string> Data { get; set; }
public Expression CreateExpression(Expression instance)
{
string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
// would something like
// listOfStrings.Any(str => str.Contains("abc"))
return anyExpression;
}
}
I really have no idea on how to implement that Any(....). I usually get lost in the data types. Hopes some Expression-expert can lent me hand with this :)
You can do it for example like this:
string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
var anyMethod = MethodAny.MakeGenericMethod(typeof(string));
Expression<Func<string, bool>> contains = x => x.Contains("abc");
return Expression.Call(anyMethod, collectionPropertyAccessor, contains);
Here you let compiler build expression you are passing to Any(..) call. If that doesn't suite your needs, you can build that expression manually:
string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
var anyMethod = MethodAny.MakeGenericMethod(typeof(string));
// find string.Contains(string)
var containsMethod = typeof(string).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(c => c.Name == "Contains" && c.GetParameters().Length == 1 && c.GetParameters()[0].ParameterType == typeof(string));
// new parameter for sub-expression you pass to Any
ParameterExpression x = Expression.Parameter(typeof(string), "x");
// (string x) => x.Contains("abc") expression
Expression<Func<string, bool>> contains = Expression.Lambda<Func<string, bool>>(
Expression.Call(x, containsMethod, Expression.Constant("abc")), x);
return Expression.Call(anyMethod, collectionPropertyAccessor, contains);
Related
I need to write a generic method for getting distinct values and propertyName is not known in advance. I want to do it using the LINQ expression. I am trying the below way but when getting the result from SelectMethod invoke I am getting WhereSelectListIterator. I want to convert it to IEnumerable so I can call the Distinct method. But I am not to cast it to IEnumerable(as it's not implemented it). How to get Enumerable back from
WhereSelectListIterator or is there any way I can get IEnumerable directly from invoke of generic method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp10
{
public class EmployeeEqualityComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
return x.Id == y.Id;
}
public int GetHashCode(Employee obj)
{
return obj.Id;
}
}
class Program
{
static void Main(string[] args)
{
var employees = new List<Employee>()
{ new Employee(){Id=1},
new Employee(){Id=2},
new Employee(){Id=1}};
var values1 = employees.Select(obj => obj.Id).Distinct();
var values2 = employees.Distinct(new EmployeeEqualityComparer());
var values= GetDistinctValue(employees, "Id");
}
private static readonly MethodInfo DistinctMethod = typeof(Enumerable).GetMethods().First(method =>
method.Name == "Distinct" &&
method.GetParameters().Length == 1);
private static readonly MethodInfo SelectMethod = typeof(Enumerable).GetMethods().First(method =>
method.Name == "Select" &&
method.GetParameters().Length == 2);
public static IEnumerable<object> GetDistinctValue<T>(IEnumerable<T> records, string propertyName)
{
try
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
Expression propertyExpression = Expression.Property(parameterExpression, propertyName);
var lambda = Expression.Lambda(propertyExpression, parameterExpression);
var propertyType = propertyExpression.Type;
// MethodCallExpression compareCall = Expression.Call(typeof(Program), "Compare", Type.EmptyTypes, propertyExpression, Expression.Constant(""), Expression.Constant(""), Expression.Constant(""));
//LambdaExpression lambda = Expression.Lambda<Func<T, bool>>(compareCall, parameterExpression);
MethodInfo genericMethod = SelectMethod.MakeGenericMethod(typeof(T),propertyType);
var result = genericMethod.Invoke(null, new object[] { records, lambda.Compile() });
MethodInfo distinctGenericMethod = DistinctMethod.MakeGenericMethod(result.GetType());
var finalResult = distinctGenericMethod.Invoke(null, new object[] { result});
return null;
}
catch(Exception exception)
{
Console.WriteLine(exception.Message);
}
return null;
}
}
public class Employee
{
public int Age { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
}
This is working version of GetDistinctValue. Main idea that you can work with IEnumerable via IQueryable which is dynamic by default.
public static IEnumerable<object> GetDistinctValue<T>(IEnumerable<T> records, string propertyName)
{
var parameterExpression = Expression.Parameter(typeof(T), "e");
var body = (Expression)Expression.Property(parameterExpression, propertyName);
if (body.Type != typeof(object))
{
body = Expression.Convert(body, typeof(object));
}
var lambda = Expression.Lambda(body, parameterExpression);
// turn IEnumerable into IQueryable
var queryable = records.AsQueryable();
var queryExpression = queryable.Expression;
// records.Select(e => (object)e.propertyName)
queryExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Select),
new[] { typeof(T), typeof(object) }, queryExpression, lambda);
// records.Select(e => (object)e.propertyName).Distinct()
queryExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Distinct), new[] { typeof(object) },
queryExpression);
// creating IQueryable<object> from generated expression
var resultQuery = queryable.Provider.CreateQuery<object>(queryExpression);
// turn IQueryable into IEnumerable
return resultQuery.AsEnumerable();
}
I have a problem converting simple linq query to Lambda Expression.
My queries look like this:
int[] array = List<int> array2 = sql.OfType<Table1>().Select(x=>x.ID).Take(10).ToList();
var result = sql.OfType<Table1>().Where(x => array.Contains(x.ID)).Take(10).ToList();
and the final result should be:
static void DynamicSQLQuery<T>(IQueryable<T> sql, string fieldName)
{
List<int> array = sql.OfType<T>().Select(SelectExpression<T>(fieldName)).Take(10).ToList();
var result = sql.OfType<T>().Where(InExpression<T>(fieldName, array)).Take(10).ToList();
}
Class
public class Table1
{
public int ID { get; set; }
public string Name { get; set; }
}
I already converted the first lambda:
public static Expression<Func<T, int>> SelectExpression<T>(string fieldName)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression selection = Expression.PropertyOrField(param, fieldName);
var lambdaExp = Expression.Lambda<Func<T, int>>(selection, param);
return lambdaExp;
}
But stuck on the second one:
static Expression<Func<T, bool>> InExpression<T>(string propertyName,IEnumerable<int> array)
{
System.Reflection.MethodInfo containsMethod = typeof(IEnumerable<int>).GetMethod("Contains");
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.PropertyOrField(param, propertyName);//x.{property}
var constant = Expression.Constant(3);
var body = Expression.GreaterThanOrEqual(member, constant); //x.{property} >= 3 but I need array.Contains(x.{property})
var finalExpression = Expression.Lambda<Func<T, bool>>(body, param);
return finalExpression;
}
Can anyone help me to make the lambda expression x=>array2.Contains(x.ID) in InExpression method?
Also, I'll be very grateful for some link to article/tutorial about creating these type of expressions.
Probably something like:
static Expression<Func<T, bool>> InExpression<T>(
string propertyName, IEnumerable<int> array)
{
var p = Expression.Parameter(typeof(T), "x");
var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == "Contains" && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
var property = Expression.PropertyOrField(p, propertyName);
var body = Expression.Call(contains, Expression.Constant(array), property);
return Expression.Lambda<Func<T, bool>>(body, p);
}
The trick here is to start off with something simple that compiles; for example:
using System.Linq;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
public class C {
static Expression<Func<Foo, bool>> InExpression<T>(
string propertyName,IEnumerable<int> array)
{
return x => array.Contains(x.Id);
}
}
class Foo {
public int Id {get;set;}
}
Now either compile it and look in ildasm/reflector, or (and much simpler): run that through https://sharplab.io specifying C# as the output, like this
This shows you the code that the compiler generated:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
C.<>c__DisplayClass0_0<T> <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0<T>();
<>c__DisplayClass0_.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
Expression arg_77_0 = null;
MethodInfo arg_77_1 = methodof(IEnumerable<!!0>.Contains(!!0));
Expression[] expr_38 = new Expression[2];
expr_38[0] = Expression.Field(Expression.Constant(<>c__DisplayClass0_, typeof(C.<>c__DisplayClass0_0<T>)), fieldof(C.<>c__DisplayClass0_0<T>.array));
Expression[] expr_5F = expr_38;
expr_5F[1] = Expression.Property(parameterExpression, methodof(Foo.get_Id()));
Expression arg_86_0 = Expression.Call(arg_77_0, arg_77_1, expr_5F);
ParameterExpression[] expr_82 = new ParameterExpression[1];
expr_82[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(arg_86_0, expr_82);
}
Note that there's a few things in here we need to fixup, but it allows us to see what it is doing - things like memberof and fieldof don't actually exist, for example, so we need to look them up via reflection.
A humanized version of the above:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
ExpressionState state = new ExpressionState();
state.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
MethodInfo contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == nameof(Enumerable.Contains) && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
Expression[] callArgs = new Expression[2];
callArgs[0] = Expression.Field(Expression.Constant(state, typeof(ExpressionState)), nameof(ExpressionState.array));
callArgs[1] = Expression.Property(parameterExpression, propertyName);
Expression body = Expression.Call(null, contains, callArgs);
ParameterExpression[] parameters = new ParameterExpression[1];
parameters[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(body, parameters);
}
with:
class ExpressionState
{
public IEnumerable<int> array;
}
This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 5 months ago.
I used the following methods to construct Order By Expression. Original Source
It is really slick. The downside is it only works if Property is string type.
How can I make it to accept different Property type without creating a bunch of methods for different data types?
public static bool PropertyExists<T>(string propertyName)
{
return typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) != null;
}
public static Expression<Func<T, string>> GetPropertyExpression<T>(string propertyName)
{
if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
var paramterExpression = Expression.Parameter(typeof(T));
return (Expression<Func<T, string>>)Expression.Lambda(
Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
}
Usage
// orderBy can be either Name or City.
if (QueryHelper.PropertyExists<Club>(orderBy))
{
var orderByExpression = QueryHelper.GetPropertyExpression<Club>(orderBy);
clubQuery = clubQuery.OrderBy(orderByExpression);
}
else
{
clubQuery = clubQuery.OrderBy(c => c.Id);
}
Problem
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public DateTime CreateDate { get; set; } <= this won't work
}
My Current Approach (Too many if statements)
public static Expression<Func<TSource, TKey>>
GetPropertyExpression<TSource, TKey>(string propertyName)
{
if (typeof (TSource).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
var paramterExpression = Expression.Parameter(typeof (TSource));
return (Expression<Func<TSource, TKey>>)
Expression.Lambda(Expression.PropertyOrField(
paramterExpression, propertyName), paramterExpression);
}
The downside is I end up with a lot of if statements for each datatype.
if (QueryHelper.PropertyExists<Club>(orderBy))
{
if(orderBy == "CreateDate")
{
var orderByExpression = GetPropertyExpression<Club, DateTime>(orderBy);
...
}
else if(orderBy == "Name" || orderBy == "City")
{
var orderByExpression = GetPropertyExpression<Club, string>(orderBy);
...
}
...
}
else
{
clubQuery = clubQuery.OrderBy(c => c.Id);
}
I found a solution with the help of Jon Skeet's old answer.
public static class QueryHelper
{
private static readonly MethodInfo OrderByMethod =
typeof (Queryable).GetMethods().Single(method =>
method.Name == "OrderBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo OrderByDescendingMethod =
typeof (Queryable).GetMethods().Single(method =>
method.Name == "OrderByDescending" && method.GetParameters().Length == 2);
public static bool PropertyExists<T>(this IQueryable<T> source, string propertyName)
{
return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) != null;
}
public static IQueryable<T> OrderByProperty<T>(
this IQueryable<T> source, string propertyName)
{
if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
MethodInfo genericMethod =
OrderByMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
object ret = genericMethod.Invoke(null, new object[] {source, lambda});
return (IQueryable<T>) ret;
}
public static IQueryable<T> OrderByPropertyDescending<T>(
this IQueryable<T> source, string propertyName)
{
if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
MethodInfo genericMethod =
OrderByDescendingMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
object ret = genericMethod.Invoke(null, new object[] {source, lambda});
return (IQueryable<T>) ret;
}
}
Usage
string orderBy = "Name";
if (query.PropertyExists(orderBy))
{
query = query.OrderByProperty(orderBy);
- OR -
query = query.OrderByPropertyDescending(orderBy);
}
option 1 :
this can done using expression : check this sample
public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable, string propertyOrFieldName, bool ascending = true)
{
var elementType = typeof (T);
var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";
var parameterExpression = Expression.Parameter(elementType);
var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, propertyOrFieldName);
var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);
var orderByExpression = Expression.Call(typeof (Queryable), orderByMethodName,
new[] {elementType, propertyOrFieldExpression.Type}, queryable.Expression, selector);
return queryable.Provider.CreateQuery<T>(orderByExpression);
}
option 2 (if you are using ef core):
public static IQueryable<TEntity> ApplyOrderBy<TEntity>(
this IQueryable<TEntity> query, string? orderBy, string orderDirection)
{
if (orderBy is null) return query;
query = orderDirection == "Asc"
? query.OrderBy(p => EF.Property<TEntity>(p!, orderBy))
: query.OrderByDescending(p => EF.Property<TEntity>(p!, orderBy));
return query;
}
I have, IMHO, a simpler solution:
public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, bool ascending , string sortingProperty)
{
if (ascending)
return source.OrderBy(item => item.GetReflectedPropertyValue(sortingProperty));
else
return source.OrderByDescending(item => item.GetReflectedPropertyValue(sortingProperty));
}
private static object GetReflectedPropertyValue(this object subject, string field)
{
return subject.GetType().GetProperty(field).GetValue(subject, null);
}
The usage is:
myQueryableCollection.Sort(ascending: true, "Name")
Of course, the PropertyExist() helper is a great addition...
I'm using this search function. but I need it to do an "and" not an "or" I cant seem to get it to return the results I want. I need to perform a search function where the search results match the text entered in box. But only a partial search. For example if I type in "Super D" I want it to find everything that contains a "Super" AND a "D".
public static class ObjectContextExtensions
{
public static IQueryable<T> FullTextSearch<T>(this IQueryable<T> queryable, string searchKey)
{
return FullTextSearch<T>(queryable, searchKey, false);
}
public static IQueryable<T> FullTextSearch<T>(this IQueryable<T> queryable, string searchKey,
bool exactMatch)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "c");
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
// MethodInfo toStringMethod = typeof (object).GetMethod("ToString", new Type[] {});
var publicProperties =
typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(p => p.PropertyType == typeof(string));
Expression orExpressions = null;
string[] searchKeyParts;
if (searchKey == null)
{
searchKey = "0";
}
searchKeyParts = !exactMatch ? searchKey.Split(' ') : new[] { searchKey };
foreach (MethodCallExpression callContainsMethod in from property in publicProperties
select Expression.Property(parameter, property) into nameProperty
from searchKeyPart in searchKeyParts
let searchKeyExpression = Expression.Constant(searchKeyPart) let containsParamConverted = Expression.Convert(searchKeyExpression, typeof(string))
select Expression.Call(nameProperty, containsMethod, (Expression)containsParamConverted))
{
if (orExpressions == null)
{
orExpressions = callContainsMethod;
}
else
{
orExpressions = Expression.Or(orExpressions,callContainsMethod);
}
}
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<T, bool>>(orExpressions, new ParameterExpression[] { parameter }));
return queryable.Provider.CreateQuery<T>(whereCallExpression);
}
}
Looks as though setting exactMatch to true would do the trick as it wouldn't split the search terms up.
FullTextSearch<MyType>(searchKey, true)
Failing that, change
orExpressions = Expression.Or(orExpressions,callContainsMethod);
to
andExpressions = Expression.And(andExpressions,callContainsMethod);
I am just getting started with expression trees so I hope this makes sense. I am trying to create an expression tree to represent:
t => t.SomeProperty.Contains("stringValue");
So far I have got:
private static Expression.Lambda<Func<string, bool>> GetContainsExpression<T>(string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameter, propertyName);
var containsMethodExp = Expression.*SomeMemberReferenceFunction*("Contains", propertyExp) //this is where I got lost, obviously :)
...
return Expression.Lambda<Func<string, bool>>(containsMethodExp, parameterExp); //then something like this
}
I just don't know how to reference the String.Contains() method.
Help appreciated.
Something like:
class Foo
{
public string Bar { get; set; }
}
static void Main()
{
var lambda = GetExpression<Foo>("Bar", "abc");
Foo foo = new Foo { Bar = "aabca" };
bool test = lambda.Compile()(foo);
}
static Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
You might find this helpful.
To perform a search like:
ef.Entities.Where(entity => arr.Contains(entity.Name)).ToArray();
which the trace string will be:
SELECT .... From Entities ... Where Name In ("abc", "def", "qaz")
I use the method I created below:
ef.Entities.Where(ContainsPredicate<Entity, string>(arr, "Name")).ToArray();
public Expression<Func<TEntity, bool>> ContainsPredicate<TEntity, T>(T[] arr, string fieldname) where TEntity : class {
ParameterExpression entity = Expression.Parameter(typeof(TEntity), "entity");
MemberExpression member = Expression.Property(entity, fieldname);
var containsMethods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Contains");
MethodInfo method = null;
foreach (var m in containsMethods) {
if (m.GetParameters().Count() == 2) {
method = m;
break;
}
}
method = method.MakeGenericMethod(member.Type);
var exprContains = Expression.Call(method, new Expression[] { Expression.Constant(arr), member });
return Expression.Lambda<Func<TEntity, bool>>(exprContains, entity);
}
How about this:
Expression<Func<string, string, bool>> expFunc = (name, value) => name.Contains(value);
In the client code:
bool result = expFunc.Compile()("FooBar", "Foo"); //result true
result = expFunc.Compile()("FooBar", "Boo"); //result false
Here is how to create an expression tree of string.Contains.
var method = typeof(Enumerable)
.GetRuntimeMethods()
.Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);
var containsMethod = method.MakeGenericMethod(typeof(string));
var doesContain = Expression
.Call(containsMethod, Expression.Constant(criteria.ToArray()),
Expression.Property(p, "MyParam"));
Actual usage at https://raw.githubusercontent.com/xavierjohn/Its.Cqrs/e44797ef6f47424a1b145d69889bf940b5581eb8/Domain.Sql/CatchupEventFilter.cs