How can I do a Func<object[],Expression<Func<T,bool>>> dynamic? - c#

I am working on doing a .Find(objects[] keys) method that dynamically creates an lambda function to query a database.
Basically what I want is:
var mykeys = new Guid("37ec1659-b35e-46c9-a7fc-e9802644ca1a");
IQueryable<T> database ;
Func<object[],Expression<Func<T,bool>>> objectFinder = CreateKeyExpression(typeof(T));
var foundObject = database.FirstOrDefault(objectFinder(mykeys));
and
private LambdaExpression CreateKeyExpression(Type C1Type)
{
ParameterExpression instanceParameter = Expression.Parameter(C1Type);
ParameterExpression keyParameters = Expression.Parameter(typeof(object[]));
PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();
var expr = Expression.Equal( Expression.TypeAs( Expression.Property(instanceParameter,objectKeys[0]),typeof(object)),
Expression.ArrayIndex(keyParameters,Expression.Constant(0)));
for (int i = 1; i < objectKeys.Length; i++)
{
expr = Expression.AndAlso(expr, Expression.Equal(
Expression.Property(instanceParameter,objectKeys[i]),
Expression.ArrayIndex(keyParameters,Expression.Constant(i))
));
}
var lmp= Expression.Lambda(expr, instanceParameter);
return Expression.Lambda(lmp, keyParameters);
}
Any ideas of how I can achieve this? The above gives me a Func<object[],Func<T,bool>>, which makes the IQueryable to a IEnumerable, meaning it won't do it on the database end.

You need to use the Expression.Constant method instead of the Expression.ArrayIndex, because you will not be able to pass to your expression array with the key values ​​using the FirstOrDefault method.
private static LambdaExpression CreateKeyExpression(Type C1Type, object[] parameters)
{
var instanceParameter = Expression.Parameter(C1Type);
PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();
var expr = Expression.Equal(Expression.Property(instanceParameter, objectKeys[0]),
Expression.Constant(parameters[0], objectKeys[0].PropertyType));
for (int i = 1; i < objectKeys.Length; i++)
{
expr = Expression.AndAlso(expr, Expression.Equal(
Expression.Property(instanceParameter, objectKeys[i]),
Expression.Constant(parameters[i], objectKeys[i].PropertyType)));
}
return Expression.Lambda(expr, instanceParameter);
}
var parameters = new object[] { Guid.NewGuid(), Guid.NewGuid() };
var lambdaExpression = CreateKeyExpression(typeof(TestClass), parameters);
var testClasses = new List<TestClass>() { new TestClass { Id = (Guid)parameters[0], Id1 = (Guid)parameters[1] } };
var testClass = testClasses.AsQueryable().FirstOrDefault((Expression<Func<TestClass, bool>>)lambdaExpression);

private Expression<Func<object[], Expression<Func<C1Source, bool>>>> CreateKeyExpression<C1Source>()
{
ParameterExpression instanceParameter = Expression.Parameter(typeof(C1Source));
ParameterExpression keyParameters = Expression.Parameter(typeof(object[]));
PropertyInfo[] objectKeys = typeof(C1Source).GetKeyProperties().ToArray();
var expr = Expression.Equal( Expression.Property(instanceParameter,objectKeys[0]),
Expression.Convert(
Expression.ArrayIndex(keyParameters,Expression.Constant(0)),
objectKeys[0].PropertyType)
);
for (int i = 1; i < objectKeys.Length; i++)
{
expr = Expression.AndAlso(expr, Expression.Equal(
Expression.Property(instanceParameter,objectKeys[i]),
Expression.Convert(
Expression.ArrayIndex(keyParameters,Expression.Constant(i)),
objectKeys[i].PropertyType)
);
}
var lmp= Expression.Lambda(expr, instanceParameter);
return Expression.Lambda<Func<object[], Expression<Func<C1Source, bool>>>>(lmp, keyParameters);
}

Related

How can I build expression c=>c.workers.any(o=>o.code=='XXX' || c.name=='XXX') dynamically

I learn how to build simple expression which like
c=>c.code=='XXX';
I create these expression by method below:
public static Expression<Func<T, bool>> BuildStringEqualLambda(string propertyName, string propertyValue)
{
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression propertyExp = parameterExp;
foreach (var property in propertyName.Split('.'))
{
propertyExp = Expression.PropertyOrField(propertyExp, property);
}
Expression right = Expression.Constant(propertyValue);
Expression e1 = Expression.Equal(propertyExp, right);
return Expression.Lambda<Func<T, bool>>(e1, new ParameterExpression[] { parameterExp });
}
I think I can build expression
o=>o.code=='XXX' || c.name=='XXX'
But I don't know how can I build method Any.
Thanks
It should be something like:
public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
if (propertyNameValues == null || propertyNameValues.Length == 0)
{
throw new ArgumentException(nameof(propertyNameValues));
}
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression body = null;
foreach (var propertyNameValue in propertyNameValues)
{
Expression propertyExp = parameterExp;
foreach (var property in propertyNameValue.Item1.Split('.'))
{
propertyExp = Expression.PropertyOrField(propertyExp, property);
}
Expression right = Expression.Constant(propertyNameValue.Item2);
Expression eq = Expression.Equal(propertyExp, right);
body = body == null ? eq : Expression.OrElse(body, eq);
}
return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}
You can probably use some funny LINQ with Aggregate to reduce the number of lines, but it would be undebuggable.
In the end you use the Expression.OrElse (not the Expression.Or that is |!) and you handle the first element case.
Use it like:
var exp = BuildStringEqualLambda(
Tuple.Create("prop1", "value1"),
Tuple.Create("prop2", "value2"),
Tuple.Create("prop3", "value3")
);
Using some LINQ and Aggregate (for those who can't live without LINQing everything) (note that while I wouldn't ever use the LINQed version of the code... It is quite unreadable... Enumerable.Aggregate is "terrible" 😊):
public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
if (propertyNameValues == null || propertyNameValues.Length == 0)
{
throw new ArgumentException(nameof(propertyNameValues));
}
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression body = propertyNameValues
.Select(x => BuildEqualityExpression<T>(parameterExp, x.Item1, x.Item2))
.Aggregate((acc, x) => Expression.OrElse(acc, x));
return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}
private static Expression BuildEqualityExpression<T>(ParameterExpression parameterExp, string propertyName, string propertyValue)
{
Expression propertyExp = propertyName
.Split('.')
.Aggregate((Expression)parameterExp, (acc, x) => Expression.PropertyOrField(acc, x));
Expression right = Expression.Constant(propertyValue);
Expression eq = Expression.Equal(propertyExp, right);
return eq;
}

C# Dynamic Expression Tree for search list using Where (or Any) and generics

Currently, I have a working helper function that creates a simple expression to search a generic entity (TEntity) for a search term (single word/phrase). It creates the following expression:
q => q.Product.ProductTitle.Contains( searchText: 'red shirt' )
I simply need to extend this helper to search each word includes in the search term (e.g. searchText:'red shirt' --> searchTerms:['red','shirt'])
var searchTerms = searchText.ToLower().Split( new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries ).ToList();
searchTerms.Any( s => !q.Product.ProductTitle.ToLower().Contains( s ) )
The function is listed below - I have commented code that needs to be completed.
public static Expression<Func<TEntity, bool>> CreateSearchQuery( List<PropertyInfo> searchPropertiesList, string searchText, SearchType searchType = SearchType.Contains )
{
if( string.IsNullOrWhiteSpace( searchText ) || searchPropertiesList == null || searchPropertiesList.Count <= 0 )
{
return null;
}
var searchTerms = searchText.ToLower().Split( new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries ).ToList();
var searchExpressionList = new List<Expression<Func<TEntity, bool>>>();
foreach( var property in searchPropertiesList.Where( x => x.GetCustomAttributes( false ).Any( c => c.GetType() != typeof( NotMappedAttribute ) ) ) )
{
//search param
var searchParam = Expression.Parameter( typeof( string ), "s" );
//search type
var searchTypeMethod = typeof( string ).GetMethod( searchType.ToString(), new[] { typeof( string ) } );
//entity expression.
var entityParam = Expression.Parameter( typeof( TEntity ), "q" );
var entityProperty = Expression.Property( entityParam, property );
var entityExpression = Expression.Call(
Expression.Call( entityProperty, typeof( string ).GetMethod( "ToLower", System.Type.EmptyTypes ) ),
searchTypeMethod,
Expression.Call( searchParam, typeof( string ).GetMethod( "ToLower", System.Type.EmptyTypes ) )
);
var entityPredicateBody = Expression.Lambda<Func<TEntity, bool>>( entityExpression, entityParam );
////TODO: CONSIDER EACH TERM AND CREATE WHERE/ANY EXPRESSION
//searchTerms.Any( s => !q.Product.ProductTitle.ToLower().Contains( s ) )
//var filterExpression = Expression.Call(
// typeof( Enumerable ),
// "Where",
// new[] { typeof( TEntity ) },
// searchParam,
// entityPredicateBody );
//var expressionBody = Expression.Lambda<Func<TEntity, bool>>( filterExpression, searchParam );
//TODO: REPLACE WITH NEW EXPRESSION (expressionBody)
searchExpressionList.Add( entityPredicateBody );
}
It should be:
public static Expression<Func<TEntity, bool>> CreateSearchQuery<TEntity>(List<PropertyInfo> properties, string text, SearchType searchType = SearchType.Contains)
{
if (string.IsNullOrWhiteSpace(text) || properties == null || properties.Count == 0)
{
return null;
}
// For comparison
//Expression<Func<ProductContainer, bool>> exp = q => searchText.Any(x => q.Product.ProductTitle.ToLower().Contains(x));
var expressions = new List<Expression>();
var entity = Expression.Parameter(typeof(TEntity), "q");
//search type
var searchMethod = typeof(string).GetMethod(searchType.ToString(), new[] { typeof(string) });
//search terms
var searchTerms = Expression.Constant(text.ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
//search param
var str = Expression.Parameter(typeof(string), "s");
foreach (var property in properties.Where(
x => x.GetCustomAttribute<NotMappedAttribute>() == null))
{
var entityProperty = Expression.Property(entity, property);
var toLower = Expression.Call(entityProperty, "ToLower", Type.EmptyTypes);
var contains = Expression.Call(toLower, searchMethod, str);
var anyExpression = Expression.Lambda<Func<string, bool>>(contains, str);
var any = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(string) }, searchTerms, anyExpression);
expressions.Add(any);
}
var ors = expressions.Aggregate((x, y) => Expression.Or(x, y));
var exp = Expression.Lambda<Func<TEntity, bool>>(ors, entity);
return exp;
}
I've put in or all the various expressions that are generated by the various properties (the Aggregate near the end of the method).

How to call a method with instance using Expression

For example I have some class with some property:
public class SomeClass
{
public Version Version { get; set; }
}
And I have a list of this type with sample data:
var list = new List<SomeClass>();
for (var i = 0; i < 1000; i++)
{
list.Add(new SomeClass
{
Version = new Version(i, i / 2, i / 3, i / 4),
});
}
I want to write method that filters by version using Version.Equals method:
var filterValue = new Version(12, 6, 4, 3);
var modelType = typeof(SomeClass);
var propertyType = typeof(Version);
var arg = Expression.Parameter(modelType, "x");
var property = Expression.Property(arg, "Version");
var value = Expression.Convert(Expression.Constant(filterValue), propertyType);
var versionEqualsMethod = typeof(Version).GetMethod("Equals", new[] { typeof(Version) });
/////////
Expression inst = null; // <-- ???
/////////
var expr = Expression.Call(inst, versionEqualsMethod, property, value);
var delegateType = typeof(Func<,>).MakeGenericType(modelType, typeof(bool));
var delegateValue = Expression.Lambda(delegateType, expr, arg).Compile();
var genericMethod =
typeof(Enumerable).GetMethods()
.First(
method =>
method.Name == "Where" && method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 1 && method.GetParameters().Length == 2)
.MakeGenericMethod(modelType);
var result = genericMethod.Invoke(null, new object[] { list, delegateValue });
What do I use as instance in Expression.Call?
UPDATE
Solution is:
var expr = Expression.Call(property, versionEqualsMethod, value);
You normally would do:
var filterValue = new Version(12, 6, 4, 3);
var modelType = typeof(SomeClass);
var propertyType = typeof(Version);
var arg = Expression.Parameter(modelType, "x");
var property = Expression.Property(arg, "Version");
// Changes from here onward
var value = Expression.Constant(filterValue);
var versionEqualsMethod = typeof(Version).GetMethod("Equals", new[] { typeof(Version) });
var expr = Expression.Call(property, versionEqualsMethod, value);
Because the Equals would be used like:
model.Version.Equals(filterValue);
I'm not handling the model.Version == null case!
Note that you don't need the Expression.Convert.
And what you are doing is ok if the "containing method" (the method where you'll put this code) is non-generic, but normally it would be a generic method, that has as the generic parameter the modelType, so the last part of the code would be different (starting from var delegateType =), because you could use the TModelType generic type directly.
Maybe I'm missing out on something, but wouldn't this work:
var results = list.Where(sc => sc.Version == filterVersion);
What you are trying to accomplish is much easier to do with reflection. Check this running online example. (If I understand you correctly that is... It would be helpful, if you could provide the signature of the function you are trying to write.)
Implementation
public static class Extensions
{
public static IEnumerable<T> Filter<T>(
this IEnumerable<T> enumerable, string propertyName, object filterValue)
{
var elementType = typeof (T);
var property = elementType.GetProperty(propertyName);
return enumerable.Where(element =>
{
var propertyValue = property.GetMethod.Invoke(element, new object[] {});
return propertyValue.Equals(filterValue);
});
}
}
Usage
var list = new List<SomeClass>();
for (var i = 0; i < 1000; i++)
{
list.Add(new SomeClass {Version = new Version(i, i/2, i/3, i/4)});
}
var filteredList = list.Filter("Version", new Version(12, 6, 4, 3));
Console.WriteLine(filteredList.Single().Version);

Dynamic extraction optimization of nested properties values

I have small piece of code responsible for dynamic extraction of properties values from objects instances through reflection:
public static object ExtractValue(object source, string property)
{
var props = property.Split('.');
var type = source.GetType();
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
if (pi == null)
throw new ArgumentException(string.Format("Field {0} not found.", prop));
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(source.GetType(), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var compiledLambda = lambda.Compile();
var value = compiledLambda.DynamicInvoke(source);
return value;
}
It can extract values of nested properties, like: ExtractValue(instance, "PropA.PropB.PropC").
Despite the fact I like this method and its implementation, when, say, PropB is null, DynamicInvoke() just throws NullReferenceException (wrapped by TargetInvocationException). Because I needed to know which exact property is null is such case, I modified its body a bit (standard step-by-step extraction chain):
public static object ExtractValue(object source, string property)
{
var props = property.Split('.');
for (var i = 0; i < props.Length; i++)
{
var type = source.GetType();
var prop = props[i];
var pi = type.GetProperty(prop);
if (pi == null)
throw new ArgumentException(string.Format("Field {0} not found.", prop));
source = pi.GetValue(source, null);
if (source == null && i < props.Length - 1)
throw new ArgumentNullException(pi.Name, "Extraction interrupted.");
}
return source;
}
Now it looks a bit worse (I like lambdas) but behaves much better, not only because it gives more meaningful information of what has failed, but also because this version is about 66 times faster than the first one (coarse test below):
var model = new ModelA
{
PropB = new ModelB {PropC = new ModelC {PropD = new ModelD {PropE = new ModelE {PropF = "hey"}}}}
};
const int times = 1000000;
var start = DateTime.Now;
for (var i = 0; i < times; i++)
ExtractValueFirst(model, "PropB.PropC.PropD.PropE.PropF");
var ticks_first = (DateTime.Now - start).Ticks;
Console.WriteLine(":: first - {0} iters tooks {1} ticks", times, ticks_first);
start = DateTime.Now;
for (var i = 0; i < times; i++)
ExtractValueSecond(model, "PropB.PropC.PropD.PropE.PropF");
var ticks_second= (DateTime.Now - start).Ticks;
Console.WriteLine(":: second - {0} iters tooks {1} ticks", times, ticks_second);
Console.WriteLine("ticks_first/ticks_second: {0}", (float)ticks_first / ticks_second);
Console.ReadLine();
How can this code be optimized in .NET to perform even faster (caching, direct IL maybe, etc)?
You can increase performance significantly by caching the compiled delegates:
static readonly ConcurrentDictionary<Tuple<Type,string>,Delegate> _delegateCache = new ConcurrentDictionary<Tuple<Type,string>,Delegate>();
public static object ExtractValue(object source, string expression)
{
Type type = source.GetType();
Delegate del = _delegateCache.GetOrAdd(new Tuple<Type,string>(type,expression),key => _getCompiledDelegate(key.Item1,key.Item2));
return del.DynamicInvoke(source);
}
// if you want to acces static aswell...
public static object ExtractStaticValue(Type type, string expression)
{
Delegate del = _delegateCache.GetOrAdd(new Tuple<Type,string>(type,expression),key => _getCompiledDelegate(key.Item1,key.Item2));
return del.DynamicInvoke(null);
}
private static Delegate _getCompiledDelegate(Type type, string expression)
{
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in property.Split('.'))
{
var pi = type.GetProperty(prop);
if (pi == null) throw new ArgumentException(string.Format("Field {0} not found.", prop));
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(source.GetType(), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
return lambda.Compile();
}
I've done some execution time measurements, which are presented below:
private static Func<object, object> _cachedFunc;
private static Delegate _cachedDel;
static void Main(string[] args)
{
var model = new ModelA
{
PropB = new ModelB {PropC = new ModelC {PropD = new ModelD {PropE = new ModelE {PropF = "hey"}}}}
};
const string property = "PropB.PropC.PropD.PropE.PropF";
var watch = new Stopwatch();
var t1 = MeasureTime(watch, () => ExtractValueDelegate(model, property), "compiled delegate dynamic invoke");
var t2 = MeasureTime(watch, () => ExtractValueCachedDelegate(model, property), "compiled delegate dynamic invoke / cached");
var t3 = MeasureTime(watch, () => ExtractValueFunc(model, property), "compiled func invoke");
var t4 = MeasureTime(watch, () => ExtractValueCachedFunc(model, property), "compiled func invoke / cached");
var t5 = MeasureTime(watch, () => ExtractValueStepByStep(model, property), "step-by-step reflection");
var t6 = MeasureTime(watch, () => ExtractValueStandard(model), "standard access (model.prop.prop...)");
Console.ReadLine();
}
public static long MeasureTime<T>(Stopwatch sw, Func<T> funcToMeasure, string funcName)
{
const int times = 100000;
sw.Reset();
sw.Start();
for (var i = 0; i < times; i++)
funcToMeasure();
sw.Stop();
Console.WriteLine(":: {0, -45} - {1} iters tooks {2, 10} ticks", funcName, times, sw.ElapsedTicks);
return sw.ElapsedTicks;
}
public static object ExtractValueDelegate(object source, string property)
{
var ptr = GetCompiledDelegate(source.GetType(), property);
return ptr.DynamicInvoke(source);
}
public static object ExtractValueCachedDelegate(object source, string property)
{
var ptr = _cachedDel ?? (_cachedDel = GetCompiledDelegate(source.GetType(), property));
return ptr.DynamicInvoke(source);
}
public static object ExtractValueFunc(object source, string property)
{
var ptr = GetCompiledFunc(source.GetType(), property);
return ptr(source); //return ptr.Invoke(source);
}
public static object ExtractValueCachedFunc(object source, string property)
{
var ptr = _cachedFunc ?? (_cachedFunc = GetCompiledFunc(source.GetType(), property));
return ptr(source); //return ptr.Invoke(source);
}
public static object ExtractValueStepByStep(object source, string property)
{
var props = property.Split('.');
for (var i = 0; i < props.Length; i++)
{
var type = source.GetType();
var prop = props[i];
var pi = type.GetProperty(prop);
if (pi == null)
throw new ArgumentException(string.Format("Field {0} not found.", prop));
source = pi.GetValue(source, null);
if (source == null && i < props.Length - 1)
throw new ArgumentNullException(pi.Name, "Extraction interrupted.");
}
return source;
}
public static object ExtractValueStandard(ModelA source)
{
return source.PropB.PropC.PropD.PropE.PropF;
}
private static Func<object, object> GetCompiledFunc(Type type, string property)
{
var arg = Expression.Parameter(typeof(object), "x");
Expression expr = Expression.Convert(arg, type);
var propType = type;
foreach (var prop in property.Split('.'))
{
var pi = propType.GetProperty(prop);
if (pi == null) throw new ArgumentException(string.Format("Field {0} not found.", prop));
expr = Expression.Property(expr, pi);
propType = pi.PropertyType;
}
expr = Expression.Convert(expr, typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(expr, arg);
return lambda.Compile();
}
private static Delegate GetCompiledDelegate(Type type, string property)
{
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
var propType = type;
foreach (var prop in property.Split('.'))
{
var pi = propType.GetProperty(prop);
if (pi == null) throw new ArgumentException(string.Format("Field {0} not found.", prop));
expr = Expression.Property(expr, pi);
propType = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(type, propType);
var lambda = Expression.Lambda(delegateType, expr, arg);
return lambda.Compile();
}
Btw: As you can see I've omitted storing compiled lambdas inside dictionary (like in the answer qiven by CSharpie), because dictionary lookup is time consuming when you compare it to compiled lambdas execution time.

Dealing with nulls indynamically created linq expression

I am dynamically creating a Linq expression based on string arrays and I'm running into a problem. The way that the expression is created and parenthesized it is causing this to throw an object null reference on Id 3. It creates this expression and If it were parenthesized correctly it wouldn't evaluate the second half of the expression and wouldn't throw an error I assume. Anyone have a way of creating the expression so it doesn't end up parenthesized like this?
{x => ((True And x.Id.ToString().ToLower().Contains("John")) Or ((x.Name != null) And x.Name.ToString().ToLower().Contains("John")))}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Test
{
public void someMethod()
{
var x = new List<Person>(new Person[] {
new Person { Id = 1, Name = "Jerry" },
new Person { Id = 2, Name = "Mary" },
new Person { Id = 3, Name = null },
new Person { Id = 4, Name = "John" },
new Person { Id = 5, Name = "Amy" }
});
var columns = new List<string>(new string[] {
"Name",
"Id"
});
var searchTerm = "John";
var searchColumns = columns.Select(a => new { ColName = a });
var type = typeof(Person);
ParameterExpression paramExpr = Expression.Parameter(type, "x");
Expression body = null;
var piList = new List<PropertyInfo>();
foreach (var s in searchColumns)
piList.Add(type.GetProperty(s.ColName));
if (piList[0].PropertyType.IsPrimitive || piList[0].PropertyType.Equals(typeof(DateTime)))
body = Expression.Constant(true);
else
body = Expression.NotEqual(Expression.Property(paramExpr, piList[0]), Expression.Constant(null, piList[0].PropertyType));
body = Expression.And(body,
Expression.Call(
Expression.Call(
Expression.Call(
Expression.Property(paramExpr, piList[0]),
typeof(Convert).GetMethod("ToString", Type.EmptyTypes)
),
typeof(string).GetMethod("ToLower", new Type[0])
),
typeof(string).GetMethod("Contains"),
Expression.Constant(searchTerm.ToLower())
));
for (int i = 1; i < piList.Count; i++)
{
Expression body1 = null;
if (piList[i].PropertyType.IsPrimitive || piList[i].PropertyType.Equals(typeof(DateTime)))
body1 = Expression.Constant(true);
else
body1 = Expression.NotEqual(Expression.Property(paramExpr, piList[i]), Expression.Constant(null, piList[i].PropertyType));
body =
Expression.Or(body,
Expression.And(body1,
Expression.Call(
Expression.Call(
Expression.Call(
Expression.Property(paramExpr, piList[i]),
typeof(Convert).GetMethod("ToString", Type.EmptyTypes)
),
typeof(string).GetMethod("ToLower", new Type[0])
),
typeof(string).GetMethod("Contains"),
Expression.Constant(searchTerm.ToLower())
)
));
}
var lambda = Expression.Lambda<Func<Person, bool>>(body, paramExpr);
}
}
Why not just build one of these. They both avoid the issue with null values for Name.
(x.Name ?? "").IndexOf("John", StringComparison.CurrentCultureIgnoreCase) >= 0
or, if you really want equality, not contains it as a substring
string.Equals(x.Name, "John", StringComparison.CurrentCultureIgnoreCase)
BTW - x.Id will never contain "John" and neither will lowercase strings.
Also, you might want to consider using a PredicateBuilder instead of building the expression directly.

Categories

Resources