1

I am trying to use this expression (dbIdGetter):

f => f.FormTemplateId

And use that in a "Contains" expression:

IEnumerable<ApiEntity> batch = apiEntities.Take(batchSize);
IEnumerable<IdType> batchIds = batch.Select(apiIdGetterCompiled).ToList();

var parameter = Expression.Parameter(typeof(DbEntity), "f");

var batchIdsConst = Expression.Constant(batchIds);
var containsMethod = typeof(List<IdType>).GetMethods().FirstOrDefault(y => y.Name == "Contains");
var body = Expression.Call(Expression.Constant(batchIds), containsMethod, dbIdGetter.Body);

var expr = Expression.Lambda<Func<DbEntity, bool>>(
    body, 
    parameter
);

// Retrieve all the ApiEntity that already exist inside the db to update
IEnumerable<DbEntity> dbBatch = await dbSet.Where(expr).ToListAsync();

"expr" has a debug string of:

{f => value(System.Collections.Generic.List`1[System.Int32]).Contains(f.FormTemplateId)}

This call:

IEnumerable<DbEntity> dbBatch = await dbSet.Where(expr).ToListAsync();

Errors out with this:

System.InvalidOperationException: 'The LINQ expression 'DbSet<SomeClass>
.Where(f => List<int> { 2406369, 2487609, 1997152, 1781014, 2239271, 2256035, 2256334, 2360990, 2256541, 2256652, 2360051, 2256568, 2256600, 2256482, 2256057, 2256088, 2239292, 1934309, }.Contains(f.FormTemplateId))' 

What am I doing wrong?

  • 2
    Can't check right now, but using lambda body with new parameter looks suspicious. Worth trying `var parameter = dbIdGetter.Parameters[0];` – Ivan Stoev Feb 26 '20 at 07:40
  • You are my hero, that was it. Thank you. If I had a more dynamic case than this, I would have to use an ExpressionVisitor to make sure all parameters are the same by reference? – Maitland Marshall Feb 26 '20 at 07:50
  • 2
    Indeed. I usually use a small parameter replace helper like this https://stackoverflow.com/questions/51497089/combine-binaryexpression-and-expressionfuncdynamic-bool-in-c-sharp/51526191#51526191 – Ivan Stoev Feb 26 '20 at 07:54
  • 2
    This is a classic problem. Yes, parameter objects are compared by reference in expression trees. C# does not allow nested lambdas to conflict in their parameter names, but the expression tree library does, and reference equality lets you disambiguate between conflicting parameters without imposing a scoping rule that might not be what you want. Unfortunately I could not come up with a good diagnostic mechanism to detect this common error given the extremely tight schedule constraints for C# 3. – Eric Lippert Feb 27 '20 at 00:16

1 Answers1

1

Thank you Ivan Stoev, his comment resolved the issue for me:

Using lambda body with new parameter looks suspicious. Worth trying var parameter = dbIdGetter.Parameters[0];

I changed

var parameter = Expression.Parameter(typeof(DbEntity), "f");

to

var parameter = dbIdGetters.Parameters[0]