Search code examples
c#linqexpression-trees

Expression tree yields Argument exception


I have the following piece of code, all works well until I get to the very last line where it fails with the following exception:

Method 'Boolean Contains(System.Linq.Expressions.ConstantExpression)' declared on type 'System.Collections.Generic.List`1[System.Linq.Expressions.ConstantExpression]' cannot be called with instance of type 'System.Guid'

var filter = new List<SomeObj> { new SomeObj { Id = "<guid-string>" }};

var lookupExpression = filter.SetOperand.Select(x => Expression.Constant(Guid.Parse(x.Id))).ToList();
var arrayOfValues = Expression.NewArrayInit(typeof(Guid), lookupExpression);
var arrayType = lookupExpression.GetType();
var containsMethod = arrayType.GetMethod("Contains");

var right = Expression.Call(dataProperty, containsMethod, arrayOfValues);

I think the problem is that dataProperty is read from a dynamically constructed expression which would always be a Guid so when the method executes, it sees this object as a Guid while the method and list are both List. Is there some other way around this?


Solution

  • I don't quite understand what are you trying to do or why, but here is my guess how to fix your code:

    1. You don't want to check that your collection of expressions of GUIDs (lookupExpression) contains a given GUID, you want to check that your expression of collection of GUIDs (arrayOfValues) does. This means that arrayType is wrong, it should be: var arrayType = arrayOfValues.Type;.
    2. If arrayOfValues is actually meant to be array, you can't use a Contains instance method, because array doesn't have one.

      You can either use LINQ Contains, or change arrayOfValues to be an expression that represents List<Guid> instead of Guid[]. I have chosen LINQ.

      To get the LINQ Contains method, you can use LINQ:

      var containsMethod = typeof(Enumerable).GetMethods()
          .Single(m => m.Name == "Contains" && m.GetParameters().Length == 2)
          .MakeGenericMethod(typeof(Guid));
      
    3. Your Call() is in the wrong order even for List.Contains(). For LINQ Contains, the correct order is:

      Expression.Call(containsMethod, arrayOfValues, dataProperty)