String Contains in lambda reflections Expression - c#

I'm trying to use this example to dinamically pass table field names to queries and avoid repeating frequent query operations for each table field.
The code contained in the example and edited as I need is the following:
Expression<Func<T, bool>> GreatherThan<T>(string field, string value)
{
ParameterExpression B = Expression.Parameter(typeof(T));
return Expression.Lambda<Func<T, bool>>(
Expression.GreaterThan(
Expression.PropertyOrField(B, field),
Expression.Constant(value)),
B);
}
It works fine with numeric fields, as it has under Expression every operation that I need for a numeric field (equal, not equal, greater/less than, etc...).
But it seems there is no operation for strings, as Contains or similar. (Except Equal and NotEqual, of course.)
How can I achieve something like the following? (which by now is syntactically wrong but it's for example purpose)
Expression<Func<T, bool>> Contains<T>(string field, string value)
{
ParameterExpression B = Expression.Parameter(typeof(T));
return Expression.Lambda<Func<T, bool>>(
Expression.Contains(
Expression.PropertyOrField(B, field),
Expression.Constant(value)),
B);
}

You have to call the method on the string object:
var s = Expression.Parameter(typeof(string), "s");
var term = Expression.Parameter(typeof(string), "term");
var contains = Expression.Call(s, typeof(string).GetMethod("Contains"), term);
Func<string, string, bool> lambda = Expression.Lambda<Func<string,string,bool>>(contains, s, term).Compile();
Console.WriteLine(lambda("test", "e")); //True

Related

Use lambda expression in another lambda expression

I need to combine two lambda expressions, in a way that the second expression "contains" the first.
I searched a lot but did not find any clear answer...
What i am trying to do is the following :
A first expression "expression1" is passed as a parameter to a method, and is simply used to define on which field or property the second lambda must operate.
Schematically, I am trying to do the following :
// simple field selector :
Expression<Func<T, string>> expression1 = obj => obj.field;
// trying to use the field selector in 2nd expression :
Expression<Func<T, bool>> e2 = obj => [[Result of expression1]].Equals("myValue");
In other words, I would like to get :
Expression<Func<T, bool>> e2 = obj => obj.field.Equals("myValue");
I need to do it this way because it is not always the Equals() method that will be called, but many different methods.
I tried to compile expression1 to a Func<T, string> in order to invoke this Func in expression2, but as I am using this with LinQ, I get an exception because LinQ does not support Invoke node types.
So my question is : is there a way to just combine the bodies of the two expressions, and how please ?
Thanks by advance !
Well, this should do the trick:
void Main()
{
var execute = Create<TestClass>(
first => first.MyStringField, // field selector
second => second.Equals("1234") // method selector
);
Console.WriteLine(execute(new TestClass("1234"))); // true
Console.WriteLine(execute(new TestClass("4321"))); // false
}
class TestClass
{
public string MyStringField;
public TestClass(string val){
MyStringField = val;
}
}
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> fieldSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of fieldSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var compiledFieldSelector = fieldSelector.Compile();
var compiledMethodSelector = methodSelector.Compile();
return T => compiledMethodSelector(compiledFieldSelector(T));
}
Note, due the nature of lambda expressions, you need to validate the field selector and method selector, otherwise it's possible to do some very weird things.
Alternatively, the next approach creates lambda that does not "compose" things, in the sense, this is better since LinqToEntities shouldn't have problems interpreting the query.
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input)
.Compile();
}
Chris Eelmaa's 2nd answer is ok, just remove the .Compile() call to avoid having the Exception with Invoke :
static Expression<Func<TSource, bool>> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input);
}
In my case this is then used like this :
(selector is an expression like u => u.id, and
request is a IQueryable<T>, both received as arguments)
Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression));
IQueryable<T> result = request.Where(contains);

LINQ to SQL dynamically select column in SqlMethods.Like() where condition

Is there any way to dynamically select which column this runs on? I don't need the comparison, just the column dynamically selected.
where SqlMethods.Like(s.GradeLevel, grade)
You can build up the query dynamically using the Expression. Here's an example I wrote to be able to dymically choose colums to filter.
private static Expression GetStringCompareExpression<TEntity>(Expression parameters, string method, FilterContainer filter, Type type)
{
Expression expression;
MemberExpression field = Expression.PropertyOrField(parameters, filter.field);
Expression constant = Expression.Constant(filter.value.ToLower().Trim(), type);
Expression lowercase = Expression.Call(field, "ToLower", null, null);
Expression trim = Expression.Call(lowercase, "Trim", null, null);
expression = AddNotNullExpression(field, Expression.Call(trim, method, null, constant));
return expression;
}
This one does the following:
Creates a field expression
Creates constant expression (the value to compare in this case)
Creates a call expression to call the ToLower() on the value in the expression
Creates a call expression to call the Trim() on the value in the expression
The 'AddNotNullExpression' add a test for 'not null' for the value
It returns the expression
Here's the 'AddNotNullExpression' method:
private static Expression AddNotNullExpression(MemberExpression field, Expression expression)
{
if(field.Type.IsNullable())
{
// String mag niet null zijn
Expression nullConstant = Expression.Constant(null, field.Type);
Expression notNull = Expression.NotEqual(field, nullConstant);
expression = Expression.AndAlso(notNull, expression);
}
return expression;
}
Call it like so:
Expression expression = GetStringCompareExpression<TEntity>(parameters, "Contains", filter, propertyInfo.PropertyType);
And finally return the IQueryable<> like so:
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> query, FilterContainer filters)
{
Expression expression = GetSingleExpression<TEntity>(filters, parameters);
var lambda = Expression.Lambda<Func<TEntity, bool>>(expression, parameters);
return query.Where(lambda);
}
It is not completely what you ask because this one was written to translate a filter expression coming from a grid in the view (which is in the FilterContainer) but it gives an idea on how you can write your 'Like' yourself
If your "dynamically" depends on a certain condition, they yes. Build your linq query without this condition first. Then add if statements and add appropriate condition.
var query = ....;
if (value1 = condition1) {
query = from q in query
where SqlMethods.Like(s.GradeLevel, grade);
}
if (value2 == condition2) {
query = from q in query
where SqlMethods.Like(s.Name, name);
}
The query will be build on the very last possible moment, not in advance, so you can safely use this approach.

Complex LINQ sorting using Lambda Expressions

Does anyone have/know of an IQueryable.OrderBy extension that takes an Expression (retrieved, for example, by Reflection)? I believe the function would look something like this:
public static IQueryable<TEntity> OrderBy<TEntity>
(this IQueryable<TEntity> source, Expression sortExpression)
Expression would be assumed to be an Expression<Func<TEntity, T>> where TEntity is the same object being sorted on, and T is a type that needs to be determined in order to create the new IQueryable.
I've found many examples of extensions that take a string, including Dynamic Linq, like this:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source, string sortExpression)
If it's possible to take the string and use Reflection to look up the type from the object in question, it should also be possible to take the Expression, and get the value type which is right there IN the Expression.
Following is a detailed explanation of why I'd like to have this, which you may or may not need.
I have a rather large list of complex records to sort. Because the list is so long, I prefer to have the sorting done on the database side. To handle more complex properties, I've created Expressions that provide the sorting functionality, like so:
if (model.sortExpression == "PlannedValue")
{
Expression<Func<BDopp, decimal>> sorter = BDopp.PlannedValueSorter;
if (model.sortDirection == "DESC")
opps = opps.OrderByDescending(sorter).AsQueryable();
else
opps = opps.OrderBy(sorter).AsQueryable();
}
BDOpp.PlannedValueSorter retrieves a static expression from the object which allows sorting to be done without opps are still of type IQueryable:
public static Expression<Func<BDopp, decimal>> PlannedValueSorter
{
get
{
return z => z.BudgetSchedules
.Where(s => s.Type == 1)
.Sum(s => s.Value * s.Workshare * z.valueFactor / 100 / 100);
}
}
Sorting for simple properties is done with Extension methods that use Reflection to build an expression based on the name of the property passed as a string.
This works well, but for the complex types, I still need branching logic, and I'd rather not do that. What I'd rather do is check for a static property containing the expression, and then simply apply it. I can get the expression like this:
PropertyInfo info = typeof(BDopp).GetProperty(model.sortExpression + "Sorter",
BindingFlags.Static | BindingFlags.Public);
Expression expr = (Expression)info.GetValue(null, null);
For the PlannedValue property, this gets me the expression sorted in PlannedValueSorter, which I already know works.
Update:
Various digging around has gotten me what I think might be some progress:
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression)
{
var unary = sortExpression.Body as UnaryExpression;
Type actualExpressionType = unary.Operand.Type;
actualExpressionType is in fact the return type of the Expression (for this particular property, it's decimal).
Unfortunately I'm mostly just working by trial and error, since I haven't yet wrapped my brain around how all this works, so my attempt to update the query like so is not working:
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
"OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression, sortExpression);
return source.Provider.CreateQuery<TEntity>(resultExp);
It compiles okay, but the following error is thrown at runtime:
No generic method 'OrderBy' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
As I understand it you have an Expression and want to order/filter by it. It might surprise you how simple it is:
Expression<Func<TEntity, T>> sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy(sortExpr);
OrderBy always uses an Expression so you can directly use it. If you only have a variable of type Expression (which points to an object of type Expression<Func<TEntity, T>>) and don't know statically what T is, you can do this:
Expression sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy((dynamic)sortExpr);
dynamic will figure this out at runtime using reflection. No need to do reflection yourself. All that is needed here is overload resolution (performed by the C# runtime binder).
Okay, I've got a solution:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression,
bool descending)
{
var unary = sortExpression.Body as UnaryExpression;
var operand = unary.Operand;
Type actualExpressionType = operand.Type;
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable),
descending? "OrderByDescending" : "OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression,
Expression.Lambda(operand, sortExpression.Parameters));
return source.Provider.CreateQuery<TEntity>(resultExp);
}
The bool descending is to allow for the standard OrderBy and OrderByDescending overloads. Two major breakthroughs here, at least for me:
Getting the Operand out of the expression.
Using Expression.Call and Expression.Lambda to create the new expression - this allows me to use an actual "Type" variable, whereas the Expression<Func<TEntity, T>> syntax requires you to use a type that's known at compile time.
Try this:
public static IEnumerable<T> OrderBy<T>(
this IEnumerable<T> collection,
string columnName
//, SortDirection direction
)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
Expression property = Expression.Property(param, columnName); // x.ColumnName
Func<T, object> lambda = Expression.Lambda<Func<T, object>>( // x => x.ColumnName
Expression.Convert(property, typeof(object)),
param)
.Compile();
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = (c, f) => c.OrderBy(f); // here you can use OrderByDescending basing on SortDirection
IEnumerable<T> sorted = expression(collection, lambda);
return sorted;
}
Usage:
IEnumerable<T> collection = ...
IEnumerable<T> ordered = collection.OrderBy("PropName");
See Code Project and sandbox.

Unique Column Values Linq To Entities Dynamically (Expression Tree)

I realize there have been a ton of posts related to this and I've researched extensively and can't seem to figure this out. It should be super simple. I simply need to generate a column domain with a dynamic column name. Something like
public IEnumerable<ColumnEntity> GetColumnDomain(string column)
{ List<ColumnEntity> columnEntities = new List<ColumnEntity>();
var query = db.CITATIONs.Select(m => m."column").Distinct();
....
}
Where "column" is the dynamic parameter value. I started building and expression tree to dynamically generate the query expression
ParameterExpression pe = Expression.Parameter(typeof(CITATION), "c");
Expression theColumn = Expression.Property(pe, typeof(string).GetProperty(column));
But that is about it. Thanks in advance
Use the Expression.PropertyOrField() method to generate the member access. You'll also need to know the type of the column as well or it just won't work.
This could all be generalized into this generic method:
public static Expression<Func<TSource, TResult>>
GenerateSelector<TSource, TResult>(string propertyOrFieldName)
{
var parameter = Expression.Parameter(typeof(TSource));
var body = Expression.Convert(
// generate the appropriate member access
Expression.PropertyOrField(parameter, propertyOrFieldName),
typeof(TResult)
);
var expr = Expression.Lambda<Func<TSource, TResult>>(body, parameter);
return expr;
}
Then you could just do:
public IEnumerable<ColumnEntity> GetColumnDomain<TColumn>(string column)
{
var query = db.CITATIONs
.Select(GenerateSelector<CITATION, TColumn>(column))
.Distinct();
// ...
}
So not entirely sure what you're after. But simply put:
public PropertyInfo GetPropertyInfo<T>(Expression<Func<T, Object>> expression)
{
MemberExpression memberData = (MemberExpression)expression.Body;
return (PropertyInfo)memberData.Member;
}
this much will give you property info about the member, so if you have:
PropertyInfo info = GetPropertyInfo<FileInfo>(file => file.FullName);
Console.WriteLine(info.Name);
It will show the 'FullName' corresponding to the property you put in the expression.

Combining Expressions in an Expression Tree

How can I build an expression tree when parts of the expression are passed as arguments?
E.g. what if I wanted to create expression trees like these:
IQueryable<LxUser> test1(IQueryable<LxUser> query, string foo, string bar)
{
query=query.Where(x => x.Foo.StartsWith(foo));
return query.Where(x => x.Bar.StartsWith(bar));
}
but by creating them indirectly:
IQueryable<LxUser> test2(IQueryable<LxUser> query, string foo, string bar)
{
query=testAdd(query, x => x.Foo, foo);
return testAdd(query, x => x.Bar, bar);
}
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
// how can I combine the select expression with StartsWith?
return query.Where(x => select(x) .. y => y.StartsWith(find));
}
Result:
While the samples didn't make much sense (sorry but I was trying to keep it simple), here's the result (thanks Quartermeister).
It can be used with Linq-to-Sql to search for a string that starts-with or is equal to the findText.
public static IQueryable<T> WhereLikeOrExact<T>(IQueryable<T> query,
Expression<Func<T, string>> selectField, string findText)
{
Expression<Func<string, bool>> find;
if (string.IsNullOrEmpty(findText) || findText=="*") return query;
if (findText.EndsWith("*"))
find=x => x.StartsWith(findText.Substring(0, findText.Length-1));
else
find=x => x==findText;
var p=Expression.Parameter(typeof(T), null);
var xpr=Expression.Invoke(find, Expression.Invoke(selectField, p));
return query.Where(Expression.Lambda<Func<T, bool>>(xpr, p));
}
e.g.
var query=context.User;
query=WhereLikeOrExact(query, x => x.FirstName, find.FirstName);
query=WhereLikeOrExact(query, x => x.LastName, find.LastName);
You can use Expression.Invoke to create an expression that represents applying one expression to another, and Expression.Lambda to create a new lambda expression for the combined expression. Something like this:
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
Expression<Func<string, bool>> startsWith = y => y.StartsWith(find);
var parameter = Expression.Parameter(typeof(T), null);
return query.Where(
Expression.Lambda<Func<T, bool>>(
Expression.Invoke(
startsWith,
Expression.Invoke(select, parameter)),
parameter));
}
The inner Expression.Invoke represents the expression select(x) and the outer one represents calling y => y.StartsWith(find) on the value returned by select(x).
You could also use Expression.Call to represent the call to StartsWith without using a second lambda:
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
var parameter = Expression.Parameter(typeof(T), null);
return query.Where(
Expression.Lambda<Func<T, bool>>(
Expression.Call(
Expression.Invoke(select, parameter),
"StartsWith",
null,
Expression.Constant(find)),
parameter));
}
This Works:
public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector1,
Expression<Func<T, string>> Selector2, string data1, string data2)
{
return Add(Add(query, Selector1, data1), Selector2, data2);
}
public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector, string data)
{
var row = Expression.Parameter(typeof(T), "row");
var expression =
Expression.Call(
Expression.Invoke(Selector, row),
"StartsWith", null, Expression.Constant(data, typeof(string))
);
var lambda = Expression.Lambda<Func<T, bool>>(expression, row);
return query.Where(lambda);
}
You use it like:
IQueryable<XlUser> query = SomehowInitializeIt();
query = Add(query, x => x.Foo, y => y.Bar, "Foo", "Bar");
Usually you don't do that in the way you descirbed (using the IQueryable Interface) but you rather use Expressions like Expression<Func<TResult, T>>. Having said that, you compose higher order functions (such as where or select) into a query and pass in expressions that will "fill in" the desired functionality.
For example, consider the signature of the Enumerable.Where method:
Where<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>)
The function takes a delegate as its second argument that is called on each element. The value you return from that delegate indicates to the higher order function if it shall yield the current element (include it in the result or not).
Now, let's take a look at Queryable.Where:
Queryable.Where<TSource>-Methode (IQueryable<TSource>, Expression<Func<TSource, Boolean>>)
We can observe the same pattern of a higher order function, but instead of an Func<> delegate it takes an Expression. An expression is basically a data representation of your code. Compiling that expression will give you a real (executable) delegate. The compiler does a lot of heavy lifting to build expression trees from lambdas you assign to Expression<...>. Expression trees make it possible to compile the described code against different data sources, such as a SQL Server Database.
To come back to your example, what I think you're looking for is a selector. A selector takes each input element and returns a projection of it. It's signature looks like this: Expression<Func<TResult, T>>. For example you could specify this one:
Expression<Func<int, string>> numberFormatter = (i) => i.ToString(); // projects an int into a string
To pass in a selector, your code would need to look like this:
IQueryable<T> testAdd<T>(IQueryable<T> query, Expression<Func<string, T>> selector, string find)
{
// how can I combine the select expression with StartsWith?
return query.Select(selector) // IQueryable<string> now
.Where(x => x.StartsWith(find));
}
This selector would allow you to project the input string to the desired type. I hope I got your intention corrrectly, it's hard to see what you're trying to achieve.

Categories