Combining multiple expression trees

I get the following error

The 'p' parameter was not bound in the specified LINQ objects by the query expression.

I understand the problem (the same instance of ParameterExpression should be used with all expressions in the tree) and tried to use the solutions that I found online, but with no luck.

This is my method

 private void SeedEntity<TEntity>(DatabaseContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class { Expression<Func<TEntity, bool>> allExpresions = null; var parameters = identifierExpressions.SelectMany(x => x.Parameters).GroupBy(x => x.Name).Select(p => p.First()).ToList(); foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions) { Func<TEntity, object> vv = identifierExpression.Compile(); object constant = vv(entity); ConstantExpression constExp = Expression.Constant(constant, typeof(object)); BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp); Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, parameters); if (allExpresions == null) { allExpresions = equalExpression2; } else { BinaryExpression bin = Expression.And(allExpresions.Body, equalExpression2.Body); allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, parameters); } } TEntity existingEntity = null; if (allExpresions != null) { existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions); } if (existingEntity == null) { context.Set<TEntity>().Add(entity); } else { entity = existingEntity; } } 

It generates an expression to search for an object based on several properties.

It works fine for one expression, an error occurs only when passing in several.

Called as follows:

 SeedEntity(context, ref e, p=> p.Name);//Works SeedEntity(context, ref e, p=> p.Name, p=> p.Age);//Fails 

It generates something similar to me by doing the following:

 context.Set<TEntity>().FirstOrDefault(p=>p.Name == e.Name && p.Age == e.Age); 

Replacing e.Name && e.Age with ConstantExpression

In the above method, you can see all the unique parameters and save them in the parameters at the top, and then use the same variable. This is the beginning, but then I need to replace the instances parameter in each of the Expression<Func<TEntity, bool>> passed as an array of params , this is where I fail.

I tried to list the expressions and use the .Update() method passing in the parameters

I also tried the solution using ExpressionVisitor

 public class ExpressionSubstitute : ExpressionVisitor { public readonly Expression from, to; public ExpressionSubstitute(Expression from, Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression node) { if (node == from) return to; return base.Visit(node); } } public static class ExpressionSubstituteExtentions { public static Expression<Func<TEntity, TReturnType>> RewireLambdaExpression<TEntity, TReturnType>(Expression<Func<TEntity, TReturnType>> expression, ParameterExpression newLambdaParameter) { var newExp = new ExpressionSubstitute(expression.Parameters.Single(), newLambdaParameter).Visit(expression); return (Expression<Func<TEntity, TReturnType>>)newExp; } } 
+5
source share
1 answer

You are really there. I do not see the point of your parameters variable. Grouping by name is a mistake. Why not just pass parameters from the expression? Then visit if necessary. Your visitor code is ok.

  private static void SeedEntity<TEntity>(DbContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class { Expression<Func<TEntity, bool>> allExpresions = null; foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions) { Func<TEntity, object> vv = identifierExpression.Compile(); object constant = vv(entity); ConstantExpression constExp = Expression.Constant(constant, typeof(object)); BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp); Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, identifierExpression.Parameters); if (allExpresions == null) { allExpresions = equalExpression2; } else { var visitor = new ExpressionSubstitute(allExpresions.Parameters[0], identifierExpression.Parameters[0]); var modifiedAll = (Expression<Func<TEntity,bool>>)visitor.Visit(allExpresions); BinaryExpression bin = Expression.And(modifiedAll.Body, equalExpression2.Body); allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, identifierExpression.Parameters); } } TEntity existingEntity = null; if (allExpresions != null) { existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions); } if (existingEntity == null) { context.Set<TEntity>().Add(entity); } else { entity = existingEntity; } } 
+4
source

Source: https://habr.com/ru/post/1236330/


All Articles