Integer contains linq c# but using using expressions - c#

I want to create a dynamic filter for my repositories using linq expressions, I have other filters but i don't know how to make the next one using expressions: (the condition was taked from here)
var result = _studentRepotory.GetAll().Where(s =>
SqlFunctions.StringConvert((double)s.Id).Contains("91")).ToList();
I have a method that receives the value of a property, the propertyName and the filter operator type (enum):
public static class Helper {
public static Expression<Func<T, bool>> GetExpression<T>(object value, string propertyName, FilterOperatorType FilterType)
{
var parameter = Expression.Parameter(typeof(T));
var property = ExpressionUtils.GetPropertyChild(parameter, propertyName);
var constValue = Expression.Constant(value);
BinaryExpression expresion = null;
switch (FilterType)
{
case FilterOperatorType.Equal:
expresion = Expression.Equal(property, constValue);
break;
case FilterOperatorType.Greater:
expresion = Expression.GreaterThan(property, constValue);
break;
case FilterOperatorType.Less:
expresion = Expression.LessThan(property, constValue);
break;
case FilterOperatorType.GreaterOrEqual:
expresion = Expression.GreaterThanOrEqual(property, constValue);
break;
case FilterOperatorType.LessOrEqual:
expresion = Expression.LessThanOrEqual(property, constValue);
break;
}
var lambda = Expression.Lambda<Func<T, bool>>(expresion, parameter);
return lambda;
}
}
So, I want to add a new opertator type Contains that will evaluate if an integer contains some digits, in the first block of code I do it, but I want to do it with linq expressions using generics.
At the end I wil have:
Expression<Func<Student, bool>> where = Helper.GetExpression<Student>("91", "Id", FilterOperatorType.Contains);
var result = _studentRepotory.GetAll().Where(where).ToList();
The query should return all the students when the Id contains the digits 91.
Please help me, and tell me if you understand.

I'm at 2:00 am and still working on this, the brain works better at that time hehehe, here is the solution for create the expression:
object value = "91";
var parameter = Expression.Parameter(typeof(Student));
var property = ExpressionUtils.GetPropertyChild(parameter, "Id");
var constValue = Expression.Constant(value);
var expressionConvert = Expression.Convert(property, typeof(double?));
var methodStringConvert = typeof(SqlFunctions).GetMethod("StringConvert",
BindingFlags.Public | BindingFlags.Static, null,
CallingConventions.Any,
new Type[] { typeof(double?) },
null);
var methodContains = typeof(string).GetMethod("Contains",
BindingFlags.Public | BindingFlags.Instance,
null, CallingConventions.Any,
new Type[] { typeof(String) }, null);
var expresionStringConvert = Expression.Call(methodStringConvert, expressionConvert);
var expresionContains = Expression.Call(expresionStringConvert, methodContains, constValue);
var lambdaContains = Expression.Lambda<Func<Student, bool>>(expresionContains, parameter);
Now you can use lambdaContains in the Where method of the studentRepository

Here is a method. And you can search int columns as strings
https://gist.github.com/gnncl/e424adefc3e08b607beee8d638759492

Related

String expression to Linq Where

In my project, I want the user to be able to supply an argument in the shape of: "Fieldname=Value"; I will then check the arguments supplied, and check if the Fieldname is a valid property in a Model; then, I need to have a Linq.Where() query that dynamically selects the required Fieldname for the filtering:
start.exe -Filter "TestField=ThisValue"
should translate to:
List<Mutations>.Where(s => s.{TestField} == "ThisValue" ).FirstOrDefault
the problem is that I do not know how I convert a string, or a name of a property, to the s.{TestField} part..
You can use the Reflection and Expression APIs for this. To start, I am going to assume that you are actually using properties and not fields (you are using properties, right?)
var type = typeof(Mutations);
var member = type.GetProperty("TestProperty");
if (member == null)
throw new Exception("property does not exist");
var p1 = Expression.Parameter(type, "s");
var equal = Expression.Equal(
Expression.Property(p1, member),
Expression.Constant("Test Value", member.PropertyType)
);
var lambda = Expression.Lambda<Func<Mutations, bool>>(equal, p1);
var result = list.AsQueryable().FirstOrDefault(lambda);
If you are actually using public fields (why?!) you can make the following modifications GetProperty->GetField, Expression.Property->Expression.Field and member.PropertyType->member.FieldType. Use caution though; some ORMs only work with properties and thus would reject the otherwise valid Expression.
We can take the above and turn it into a reusable, generic method that returns an Expression:
using System.Linq.Expressions;
public static class ExpressionHelpers {
public static Expression CreateWhere<T>(string propertyName, string targetValue) {
var type = typeof(T);
var member = type.GetProperty(propertyName) ?? throw new Exception("Property does not exist");
var p1 = Expression.Parameter(type, "s");
var equal = Expression.Equal(
Expression.Property(p1, member),
Expression.Constant(targetValue, member.PropertyType)
);
return Expression.Lambda<Func<T, bool>>(equal, p1);
}
}
Calling this method might look like:
public static void SomeMethod() {
var list = new List<Mutations> { /* ... */ };
Expression clause = ExpressionHelpers.CreateWhere<Mutations>("TestProperty", "TestValue");
var result = list.AsQueryable().FirstOrDefault(clause);
if (result != null)
Console.WriteLine("Result = {0}", result);
}
Note that this doesn't do any sort of validation of the data types--it assumes the property is the same type as the input, which is currently string. If you need to deal with numbers or dates or what have you, you'll need to switch on the data type and provide the appropriate parsed data to the constant:
public static Expression CreateWhere<T>(string propertyName, string targetValue) {
var type = typeof(T);
var member = type.GetProperty(propertyName) ?? throw new Exception("Property does not exist");
var propType = member.PropertyType;
if ((propType.IsClass && propType != typeof(string)) || propType.IsInterface)
throw new Exception("Interfaces and Class Types are not supported");
var p1 = Expression.Parameter(type, "s");
Expression target = null;
if (propType == typeof(string))
target = Expression.Constant(targetValue, typeof(string));
else if (propType == typeof(int) && int.TryParse(targetValue, out var intVal))
target = Expression.Constant(intVal, typeof(int));
else if (propType == typeof(long) && long.TryParse(targetValue, out var longVal))
target = Expression.Constant(longVal, typeof(long));
else if (propType == typeof(DateTime) && DateTime.TryParse(targetValue, out var dateVal))
target = Expression.Constant(dateVal, typeof(DateTime));
else
throw new Exception("Target property type is not supported or value could not be parsed");
var equal = Expression.Equal(
Expression.Property(p1, member),
target
);
return Expression.Lambda<Func<T, bool>>(equal, p1);
}
As you can see this starts to get fairly complex with the more types you want to support. Also note that if you are using this without an ORM (just LINQ on a list) you are probably going to want add some support for case-[in]sensitive string comparisons. That can be delegated to a string.Equals call that could look something like this:
bool ignoreCase = true; // maybe a method parameter?
var prop = Expression.Property(p1, member);
Expression equal = null;
if (propType != typeof(string))
{
equal = Expression.Equal(prop, target);
}
else
{
var compareType = ignoreCase
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
var compareConst = Expression.Constant(compareType, typeof(StringComparison));
equal = Expression.Call(
typeof(string),
nameof(string.Equals),
new[] { typeof(string), typeof(string), typeof(StringComparison) },
prop,
target,
compareConst
);
}
return Expression.Lambda<Func<T, bool>>(equal, p1);
Note that depending on their support this may or may not work with an ORM (and may not be necessary since many databases are insensitive comparisons by default). The above also does not handle Nullable<T> (ie. int?) which adds its own set of complexities.

C# Using dynamic Linq expressions filter a column that is of type int using Startswith and getting Nullable`1 for conversion

As a basis for filtering using dynamic linq I used this answer and it works for columns of type String however I also needed to do startsWith/Contains on integer type columns as well - in this case I used this answer - and it falls over with the error shown a little below.
Here is the code that creates the IQueryable result.
public static IQueryable<T> Has<T>(this IQueryable<T> source, ExpressionFilter filter) {
if (source == null || filter.PropertyName.IsNull() || filter.Value == null) {
return source;
}
// Find the type incase we get an int column.
Type propertyType = source.ElementType.GetProperty(filter.PropertyName).PropertyType;
// For our Call.
MethodCallExpression methodCallExpression = null;
// If its a string we need to change it to lower case.
MethodCallExpression toLowerExpression = null;
// If its any one of the binary expressions.
Expression binaryExpression = null;
// ..and our predicate is initiated here as well.
Expression<Func<T, bool>> predicate = null;
// We need the parameter eg x => ..
ParameterExpression parameter = Expression.Parameter(source.ElementType, "x");
// Finally here is our Property expression eg x => LastName Last name being the property name
Expression propertyExp = Expression.Property(parameter, filter.PropertyName);
// our METHODINFO's
var CONTAINS_METHOD = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var STARTS_WITH_METHOD = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var ENDS_WITH_METHOD = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var BOOL_EQUAL_METHOD = typeof(bool).GetMethod("Equals", new Type[] { typeof(bool) });
var TO_LOWER_METHOD = typeof(string).GetMethod("ToLower", new Type[] { });
var TRIM_START = typeof(string).GetMethod("Trim", new Type[] { typeof(int) });
var STRING_CONVERT = typeof(SqlFunctions).GetMethod("StringConvert", new[] { typeof(double?) }); ////get the SqlFunctions.StringConvert method for nullable double
// We supply a type of object for our term to search on.. it needs to be a string.
ConstantExpression termConstant = Expression.Constant(filter.Value.ToString(), typeof(string));
// In case we get a propertyType of (int) we cant just perform a lower case on it.
if (propertyType == typeof(string)) {
toLowerExpression = Expression.Call(propertyExp, TO_LOWER_METHOD);
}
switch (filter.Comparison) {
case Comparison.Contains:
methodCallExpression = Expression.Call(toLowerExpression, CONTAINS_METHOD, termConstant);
break;
case Comparison.StartsWith:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
else
methodCallExpression = Expression.Call(toLowerExpression, STARTS_WITH_METHOD, termConstant); //WORKS HERE..
break;
case Comparison.EndsWith:
methodCallExpression = Expression.Call(toLowerExpression, ENDS_WITH_METHOD, termConstant);
break;
case Comparison.BoolTest:
bool parsedBoolValue;
if (bool.TryParse(filter.Value.ToString().ToLower(), out parsedBoolValue)) { // Its a bool column.
termConstant = Expression.Constant(parsedBoolValue, typeof(bool));
methodCallExpression = Expression.Call(propertyExp, BOOL_EQUAL_METHOD, termConstant);
}
break;
case Comparison.Equal:
binaryExpression = Expression.Equal(propertyExp, termConstant);
break;
case Comparison.GreaterThan:
binaryExpression = Expression.GreaterThan(propertyExp, termConstant);
break;
case Comparison.GreaterThanOrEqual:
binaryExpression = Expression.GreaterThanOrEqual(propertyExp, termConstant);
break;
case Comparison.LessThan:
binaryExpression = Expression.LessThan(propertyExp, termConstant);
break;
case Comparison.LessThanOrEqual:
binaryExpression = Expression.LessThanOrEqual(propertyExp, termConstant);
break;
case Comparison.NotEqual:
binaryExpression = Expression.NotEqual(propertyExp, termConstant);
break;
case Comparison.IndexOf:
binaryExpression = Expression.NotEqual(
Expression.Call(
propertyExp,
"IndexOf",
null,
Expression.Constant(filter.Value, typeof(string)),
Expression.Constant(StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison))
),
Expression.Constant(-1, typeof(int))
);
break;
default:
return null;
}
if (binaryExpression == null) {
predicate = Expression.Lambda<Func<T, bool>>(methodCallExpression, parameter);
}
else {
predicate = Expression.Lambda<Func<T, bool>>(binaryExpression, parameter);
}
methodCallExpression = Expression.Call(typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
So it works with columns of string type but when you try and do a filter on an int column it fails with..
"Operation is not valid due to the current state of the object."
For an int column it builds the expression with:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
and as an expression this yields
{x => StringConvert(Convert(x.ClientNo,
Nullable`1)).StartsWith("101")}
I could be wrong here but why would it put a back tick and a "1" after nullable?? is this why it falls over?
For completeness at the point it completes the "Has" method (adds to the expression tree) and this is the result (which fails):
Is there a better way to construct this expression so it works?

string expression to c# function delegate

I want to convert the following string into function delegate.
[Id]-[Description]
C# class:
public class Foo
{
public string Id {get;set;}
public string Description {get;set;}
}
Result function delegate:
Func<Foo, string> GetExpression = delegate()
{
return x => string.Format("{0}-{1}", x.Id, x.Description);
};
I think compiled lambda or expression parser would be a way here, but not sure about the best way much. Any inputs?
It's possible as: to construct Linq Expression then compile it. Compiled expression is an ordinary delegate, with no performance drawbacks.
An example of implementation if type of argument(Foo) is known at compile time:
class ParserCompiler
{
private static (string format, IReadOnlyCollection<string> propertyNames) Parse(string text)
{
var regex = new Regex(#"(.*?)\[(.+?)\](.*)");
var formatTemplate = new StringBuilder();
var propertyNames = new List<string>();
var restOfText = text;
Match match;
while ((match = regex.Match(restOfText)).Success)
{
formatTemplate.Append(match.Groups[1].Value);
formatTemplate.Append("{");
formatTemplate.Append(propertyNames.Count);
formatTemplate.Append("}");
propertyNames.Add(match.Groups[2].Value);
restOfText = match.Groups[3].Value;
}
formatTemplate.Append(restOfText);
return (formatTemplate.ToString(), propertyNames);
}
public static Func<T, string> GetExpression<T>(string text) //"[Id]-[Description]"
{
var parsed = Parse(text); //"{0}-{1} Id, Description"
var argumentExpression = Expression.Parameter(typeof(T));
var properties = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField)
.ToDictionary(keySelector: propInfo => propInfo.Name);
var formatParamsArrayExpr = Expression.NewArrayInit(
typeof(object),
parsed.propertyNames.Select(propName => Expression.Property(argumentExpression, properties[propName])));
var formatStaticMethod = typeof(string).GetMethod("Format", BindingFlags.Static | BindingFlags.Public, null,new[] { typeof(string), typeof(object[]) }, null);
var formatExpr = Expression.Call(
formatStaticMethod,
Expression.Constant(parsed.format, typeof(string)),
formatParamsArrayExpr);
var resultExpr = Expression.Lambda<Func<T, string>>(
formatExpr,
argumentExpression); // Expression<Func<Foo, string>> a = (Foo x) => string.Format("{0}-{1}", x.Id, x.Description);
return resultExpr.Compile();
}
}
And usage:
var func = ParserCompiler.GetExpression<Foo>("[Id]-[Description]");
var formattedString = func(new Foo {Id = "id1", Description = "desc1"});
An almost identical answer was posted while I was testing this, but, as the below code has an advantage of calling each property mentioned in the formatting string at most once, I'm posting it anyway:
public static Func<Foo, string> GetExpression(string query_string)
{
(string format_string, List<string> prop_names) = QueryStringToFormatString(query_string);
var lambda_parameter = Expression.Parameter(typeof(Foo));
Expression[] formatting_params = prop_names.Select(
p => Expression.MakeMemberAccess(lambda_parameter, typeof(Foo).GetProperty(p))
).ToArray();
var formatMethod = typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) });
var format_call = Expression.Call(formatMethod, Expression.Constant(format_string), Expression.NewArrayInit(typeof(object), formatting_params));
var lambda = Expression.Lambda(format_call, lambda_parameter) as Expression<Func<Foo, string>>;
return lambda.Compile();
}
// A *very* primitive parser, improve as needed
private static (string format_string, List<string> ordered_prop_names) QueryStringToFormatString(string query_string)
{
List<string> prop_names = new List<string>();
string format_string = Regex.Replace(query_string, #"\[.+?\]", m => {
string prop_name = m.Value.Substring(1, m.Value.Length - 2);
var known_pos = prop_names.IndexOf(prop_name);
if (known_pos < 0)
{
prop_names.Add(prop_name);
known_pos = prop_names.Count - 1;
}
return $"{{{known_pos}}}";
});
return (format_string, prop_names);
}
The inspiration comes from Generate lambda Expression By Clause using string.format in C#?.
A simple step by step version to create an Expression tree based on simple use case, can help in creating any kind of Expression tree
What we want to Achieve: (coding in linqpad, Dump is a print call)
Expression<Func<Foo,string>> expression = (f) => string.Format($"{f.Id}-
{f.Description}");
var foo = new Foo{Id = "1",Description="Test"};
var func = expression.Compile();
func(foo).Dump(); // Result "1-Test"
expression.Dump();
Following is the Expression generated:
Step by Step process to Create an Expression Tree
On Reviewing the Expression Tree, following points can be understood:
We create a Func delegate of type typeof(Func<Foo,String>)
Outer Node Type for Expression is Lambda Type
Just needs one parameter Expression of typeof(Foo)
In Arguments it needs, MethodInfo of string.Format
In arguments to Format method, it needs following Expressions
a.) Constant Expression - {0}-{1}
b.) MemberExpression for Id field
c.) MemberExpression for Description field
Viola and we are done
Using the Steps above following is the simple code to create Expression:
// Create a ParameterExpression
var parameterExpression = Expression.Parameter(typeof(Foo),"f");
// Create a Constant Expression
var formatConstant = Expression.Constant("{0}-{1}");
// Id MemberExpression
var idMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Id"));
// Description MemberExpression
var descriptionMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Description"));
// String.Format (MethodCallExpression)
var formatMethod = Expression.Call(typeof(string),"Format",null,formatConstant,idMemberAccess,descriptionMemberAccess);
// Create Lambda Expression
var lambda = Expression.Lambda<Func<Foo,string>>(formatMethod,parameterExpression);
// Create Func delegate via Compilation
var func = lambda.Compile();
// Execute Delegate
func(foo).Dump(); // Result "1-Test"

Linq sorting with multiple column names in c#

I want to sort multiple columns with Linq
I used this link for reference, which is used for sorting of single column by column name.
I am trying to use this method for sorting of multiple columns with column names.
Here is what i am doing so far
public static IQueryable<T> OrderByMultipleFields<T>(this IQueryable<T> q, Dictionary<string, bool> fieldsToSort)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, fieldsToSort.First().Key);
var exp = Expression.Lambda(prop, param);
string methodAsc = "OrderBy";
string methodDesc = "OrderByDescending";
string method=string.Empty;
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = q.Expression;
int count = 0;
foreach (var fieldName in fieldsToSort)
{
method = fieldName.Value ? methodAsc : methodDesc;
prop = Expression.Property(param, fieldName.Key);
exp = Expression.Lambda(prop, param);
types = new Type[] { q.ElementType, exp.Body.Type };
if (count == 0) {
mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
} else {
mce = Expression.Add(mce, Expression.Call(typeof(Queryable), method, types, q.Expression, exp));
}
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
count++;
}
return q.Provider.CreateQuery<T>(mce);
}
I am getting following error -
The binary operator Add is not defined for the types
'System.Linq.IOrderedQueryable1[SortDemo.Data.User]' and
'System.Linq.IOrderedQueryable1[SortDemo.Data.User]'.
what is the proper way to achieve this or is there any alternate approach or method for this.
thanks.
It's not clear why you're trying to call Add in the first place. There are no addition operations in normal sorting code, so there wouldn't be in the expression tree form either.
I suspect you just want to replace this:
if (count == 0)
{
mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
}
else {
mce = Expression.Add(mce, Expression.Call(typeof(Queryable), method, types, q.Expression, exp));
}
with:
mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
I'd also suggest moving the declarations of method, types, exp and props to inside the loop, as well... and removing count. That would leave you with:
string methodAsc = "OrderBy";
string methodDesc = "OrderByDescending";
var mce = q.Expression;
foreach (var fieldName in fieldsToSort)
{
var method = fieldName.Value ? methodAsc : methodDesc;
var prop = Expression.Property(param, fieldName.Key);
var exp = Expression.Lambda(prop, param);
var types = new Type[] { q.ElementType, exp.Body.Type };
mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
return q.Provider.CreateQuery<T>(mce);
I haven't tried it but it's more likely to worth than what you had before.
Another thing to consider is not building up the whole expression tree like this, but instead calling the appropriate Queryable methods directly yourself via reflection. You'd then only need to build up a single property-access expression tree at any point.

Call Ignore Case for Contains Method using a generic LINQ Expression

I am using below code for Generic Filter, any search text passed but the contains method is Case sensitive, how can I write to ignore case.
public static class QueryExtensions
{
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string search)
{
var properties = typeof(T).GetProperties().Where(p =>
/*p.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmScalarPropertyAttribute),true).Any() && */
p.PropertyType == typeof(String));
var predicate = PredicateBuilder.False<T>();
foreach (var property in properties )
{
predicate = predicate.Or(CreateLike<T>(property,search));
}
return query.AsExpandable().Where(predicate);
}
private static Expression<Func<T,bool>> CreateLike<T>( PropertyInfo prop, string value)
{
var parameter = Expression.Parameter(typeof(T), "f");
var propertyAccess = Expression.MakeMemberAccess(parameter, prop);
var like = Expression.Call(propertyAccess, "Contains", null, Expression.Constant(value,typeof(string)));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
}
Instead of calling String.Contains, call String.IndexOf with a case insensitive StringComparison parameter. Then compare its result with 0, with the Expression.GreaterThanOrEqual expression. You need to provide the extra parameter in your Expression.Call as an Expression.Constant.
You can decide to hardcode one of the case-insensitive StringComparison options, or export it as a parameter of the Filter method, allowing users to decide whether they want case-insensitive search or not.
You can do something like this:
private static Expression<Func<T, bool>> CreateLike<T>(PropertyInfo prop, string value)
{
var parameter = Expression.Parameter(typeof(T), "f");
var propertyAccess = Expression.MakeMemberAccess(parameter, prop);
var indexOf = Expression.Call(propertyAccess, "IndexOf", null, Expression.Constant(value, typeof(string)),Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
var like=Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
or, with the StringComparison parameter
private static Expression<Func<T, bool>> CreateLike<T>(PropertyInfo prop,
string value,
StringComparison comparison=StringComparison.InvariantCultureIgnoreCase)
{
var parameter = Expression.Parameter(typeof(T), "f");
var propertyAccess = Expression.MakeMemberAccess(parameter, prop);
var indexOf = Expression.Call(propertyAccess, "IndexOf", null,
Expression.Constant(value, typeof(string)),
Expression.Constant(comparison));
var like=Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
By using a default value for comparison you avoid creating two overloads for the same job.
You could try using String.IndexOf instead.
string x,y = string.Empty;
x.IndexOf(y,0,x.Length, StringComparison.CurrentCultureIgnoreCase) > -1
As it has a StringComparison parameter.
This would return an integer
var like = Expression.Call(propertyAccess, "IndexOf", null, Expression.Constant(value, typeof(string)), Expression.Constant(StringComparison.CurrentCultureIgnoreCase,typeof(StringComparison)));
Please refer to the following code if you want to filter or search for a value from the list. In addition, it is a generic method that will help you filter any type of class or object from the list. It is working as a like clause in SQL such as (column1 like '%abc%' or column2 like '%abc%').
public static class Filter<T>
{
public static Expression<Func<T, bool>> FilterExpression(string searchValue)
{
Expression finalExpression = Expression.Constant(false);
var parameter = Expression.Parameter(typeof(T), "x");
PropertyInfo[] propertyInfos = typeof(T).GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.PropertyType == typeof(string))
{
var propertyExpn = Expression.Property(parameter, propertyInfo.Name.Trim().ToLower());
var containsExpn = Expression.Call(propertyExpn, "Contains", null, Expression.Constant(searchValue, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
var nullCheckExpn = Expression.NotEqual(propertyExpn, Expression.Constant(null, typeof(string)));
var andAlsoExpn = Expression.AndAlso(nullCheckExpn, containsExpn);
finalExpression = Expression.Or(finalExpression, andAlsoExpn);
}
}
var rowFilterLambdaExpression = Expression.Lambda<Func<T, bool>>(finalExpression, new ParameterExpression[] { parameter });
return rowFilterLambdaExpression;
}
}
Usage E.g.,
var result =
dataItems.Where(Filter<T>.FilterExpression(model.FilterValue).Compile()).ToList();
It's probably simplest to convert both parameters to upper case first (upper case conversions are optimized better than lower case ones in .NET). You can then do your comparison.
Upper case conversion can be done like this:
var expression = Expression.Call(property, typeof(string).GetMethod("ToUpperInvariant", System.Type.EmptyTypes));

Categories