Convert Linq Expression "Obj =≫ Obj.Prop" into "Parent =≫ Parent.Obj.Prop"

Convert Linq expression obj = obj.Prop into parent = parent.obj.Prop

What you're looking for is the ability to compose expressions, just as you can compose functions:

public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
this Expression<Func<T, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
return Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
}

This relies on the following method to replace all instances of one expression with another:

public class ReplaceVisitor:ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}

public override Expression Visit(Expression ex)
{
if(ex == from) return to;
else return base.Visit(ex);
}
}

public static Expression Replace(this Expression ex,
Expression from,
Expression to)
{
return new ReplaceVisitor(from, to).Visit(ex);
}

You can now take an expression selecting a property:

Expression<Func<Customer, object>> propertySelector = cust => cust.Name;

And an expression selecting that object from the model:

Expression<Func<CustomerModel, Customer>> modelSelector = model => model.Customer;

and compose them:

Expression<Func<Customer, object> magic = modelSelector.Compose(propertySelector);

Trying to use parent property as parameter in child collection expression; LinqKit throws Unable to cast MethodCallExpressionN to LambdaExpression

OK, so what you are trying to do (the transformation from a function that takes a single argument, that returns another function that takes a single argument f(x)(y) into a function that takes two arguments f(x, y)) is known as uncurrying. Look it up! :)

Now, the issue that you have in your code is that, in the expression returned by BuildExpressionToCheckTuple, there is a method call to BuildExpressionToCheckStringLength, which is not resolved. And you cannot resolve it because it takes an argument that is embedded in the tuple parameter.

The solution is, instead of using a method call, to use a lambda expression that will be equivalent to that method call.

That is:

public Expression<Func<int, Func<string, bool>>> ExpressionToCheckStringLengthBuilder() {
return minLength =>
str => str.Length > minLength;
}

public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)

// Putting builder into a variable so that the resulting expression will be
// visible to tools that analyze the expression.
var builder = ExpressionToCheckStringLengthBuilder();

return tuple => builder.Invoke(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}

How to combine two C# Expressions using API?

You can replace parameter of mainExpression with a body of aToB (for example using ReplacingExpressionVisitor available since EF Core 3.0 or write your own one if you are using earlier version) and build a new expression tree:

Expression<Func<B, string>> mainExpression = b => b.SomeValue; 
Expression<Func<A, B>> aToB = a => a.B;
var visit = new ReplacingExpressionVisitor(new[] {mainExpression.Parameters[0]}, new[] {aToB.Body})
.Visit(mainExpression.Body);
var result = Expression.Lambda<Func<A, string>>(visit, aToB.Parameters);

How do I compose Linq Expressions? ie Func Exp Func X, Y , Exp Func Y, Z , Exp Func X, Z

The equivalent of Haskell's function composition operator

(.) :: (b->c) -> (a->b) -> (a->c)
f . g = \ x -> f (g x)

would in C# probably be something like

static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<B, C>> f,
Expression<Func<A, B>> g)
{
var x = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(f, Expression.Invoke(g, x)), x);
}

Is this what you're looking for?

Example:

Compose<int, int, string>(y => y.ToString(), x => x + 1).Compile()(10); // "11"

Creating a dynamic EF filter that builds a LINQ Where equals/contains statement for any string entity property

This is much easier to do by composing expressions than it is by trying to manually construct the expressions every single time. It's faster to write, so much less error prone, and actually ends up with code you can actually read at the end of it. All you need to do is write the code for how you use the value in the composed expression, which you already have from your original code.

public static IQueryable<TEntity> Search<TEntity>(
this IQueryable<TEntity> query,
Expression<Func<TEntity, string>> fieldExpression,
string searchValue,
bool exactSearch = true,
bool useStartsWithOverContains = false)
{
if (string.IsNullOrWhiteSpace(searchValue))
return query;

searchValue = searchValue.Trim();

if (exactSearch)
{
return query.Where(fieldExpression.Compose(field => field == searchValue));
}
else if (useStartsWithOverContains)
{
return query.Where(fieldExpression.Compose(field => field.StartsWith(searchValue.ToLower())));
}
else
{
return query.Where(fieldExpression.Compose(field => field.Contains(searchValue.ToLower())));
}
}

Note you should probably go with an enum for "Comparison" or something like that, rather than having two booleans. For example, right now someone can say that they don't want an exact sure but that they do want to use starts with. Just have one parameter with the three options.

Reusable expression

You can't directly call the other expression f2(b.Reference). And it would be futile to create an expression that compiles and invokes f2.

What you actually want to do is compose the expressions. Make a new expression that represents one expression chained to the other. The expression you're missing is actually just the argument selector that gets an A from a B like this: b => b.Reference;

Here's a handy Compose method (similar to this one) to help chain them together.

class A
{
public int Prop1 = 1;
public int Prop2 = 2;
}
class B
{
public A Reference;
}

class Program
{
static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<A, B>> fAB, Expression<Func<B, C>> fBC)
{
var arg = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(fBC, Expression.Invoke(fAB, arg)), arg);
}

static void Main(string[] args)
{
int val1 = 1;
int val2 = 2;

Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
Func<B, bool> g1 = b => f1(b.Reference);

Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
Expression<Func<B, A>> argSelect = b => b.Reference;
var g2 = Compose<B, A, bool>(argSelect, f2);

A objA = new A();
B objB = new B() { Reference = objA };
var g2Compiled = g2.Compile();
Console.WriteLine(g2Compiled.Invoke(objB));

// Demonstrate that it's connected to our local variable
val2 = 3;
Console.WriteLine(g2Compiled.Invoke(objB));
}
}

How to flatten nested objects with linq expression


myBooks.SelectMany(b => b.Chapters
.SelectMany(c => c.Pages
.Select(p => b.Name + ", " + c.Name + ", " + p.Name)));

How to update an existing object's properties using LINQ to Entities?


Can I use Entity Framework to update the values of an existing object?

No - the Select method is used for projection not updating - what your doing is the correct approach. Alternatively you may find it more efficient to change your Person class to a struct as it consumes less memory.

I think you need to elaborate a little more on "It hurts the GC".

Update

It appears Linq to Entities does not support projecting onto struct's (seems like a limitation to me) - best possible approach then would be to just project onto an anonymous type & then you can map your results onto your Person class using your pooling mechanism e.g.

var users = (from u in context.User
select new {
Name = u.Name,
Parent = u.Parent.Name
}).ToList().Select(u => People.Take(u.Name, u.Parent));

Incorporate partial expression in LINQ/SQL query

You can write a method to construct a new GreaterThanOrEqual expression from two operands

The idea behind this is the following. You want to construct a new expression which looks like this:

td => td.DateShipped >= startDate.Value

using the body of your existing expression td => td.DateShipped, but important thing here is that td in the result expression and td in your GetDateExpression are different things, so if you just will write the greaterThanOrEqual expression without replacing you will get something like this:

td1 => td2.DateShipped >= startDate.Value

Therefore you need to replace td2 with td1 so that the expression looks like I wrote in the beginning. Therefore all the replacer does is replace each ParameterExpression it finds in the expression tree with our filterParam.

You can take a look at the following answers to read about this a bit more:

  • Why we need replacer in the first place
  • Implementation of ParameterReplacer used in my answer
  • Great answer with different solutions for different cases
    Expression<Func<T, bool>> GreaterThanOrEqual(Expression<Func<T, DateTime?>> operand1, Expression<Func<T, DateTime?>> operand2)
{
var filterParam = Expression.Parameter(typeof(T));
var greaterThanOrEqual = Expression.GreaterThanOrEqual(operand1.Body, operand2.Body);
greaterThanOrEqual = (BinaryExpression) new ParameterReplacer(filterParam).Visit(greaterThanOrEqual);

return Expression.Lambda<Func<T, bool>>(greaterThanOrEqual, filterParam);
}

internal class ParameterReplacer : ExpressionVisitor {
private readonly ParameterExpression _parameter;

protected override Expression VisitParameter(ParameterExpression node) {
return base.VisitParameter(_parameter);
}

internal ParameterReplacer(ParameterExpression parameter) {
_parameter = parameter;
}
}

Then use it like

    Expression<Func<T, DateTime?>> getDateExpression = m => m.Date;
Expression<Func<MyClass, bool>> combined = GreaterThanOrEqual(getDateExpression, td => startDate.Value);

query = query.Where(combined);


Related Topics



Leave a reply



Submit