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
Validating an Xml Against Referenced Xsd in C#
Compression/Decompression String With C#
Tips For Optimizing C#/.Net Programs
Multiline String Literal in C#
How to Do Joins in Linq on Multiple Fields in Single Join
What Does the @ Symbol Before a Variable Name Mean in C#
How to Easily Check If Access Is Denied For a File in .Net
How to Create Linq Expression Tree to Select an Anonymous Type
Ipc Mechanisms in C# - Usage and Best Practices