Question

I'm trying to dynamically create Joins in LINQ. This answer gave me a good insight as to where to start. However, I'm having a problem specifically when the "foreing key" is an int? (int works). When it's an int? it will trigger an exception.

I've made a simple LINQ to Objects example to show the problem. In order to run my example, you will need to install the dynamic linq Nuget package because I use it for the DynamicExpression.ParseLambda method.

This is the code that triggers the exception:

public class Contact
{
   public int Id { get; set; }

   public string Name { get; set; }
}

public class Address
{
   public int Id { get; set; }
   public int? ContactId { get; set; }

   public string Zip { get; set; }
}

class Program
{
   static void Main(string[] args)
   {
       var contacts = new List<Contact>();
       var addresses = new List<Address>();

       LambdaExpression outerSelectorLambda = DynamicExpression.ParseLambda(typeof(Contact), null, "Id", new object[0]);
       LambdaExpression innerSelectorLambda = DynamicExpression.ParseLambda(typeof(Address), null, "ContactId", new object[0]);
       ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(typeof(Contact), "outer"), Expression.Parameter(typeof(Address), "inner") };
       LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda(parameters, null, "outer.Id", new object[0]);

       // BLOWS UP HERE
       var queryExpression = Expression.Call(
           typeof(Queryable), "Join",
           new Type[] { typeof(Contact), typeof(Address), typeof(int?), typeof(int) },
           contacts.AsQueryable().Expression, addresses.AsQueryable().Expression,
           Expression.Quote(outerSelectorLambda), Expression.Quote(innerSelectorLambda), Expression.Quote(resultsSelectorLambda));

       // it will not reach the following line
       var queryable = contacts.AsQueryable().Provider.CreateQuery(queryExpression);
   }
}

To make the above code to work, just replace the 2 occurrences of int? by int. Everything will work fine. The problem lies in the fact that Address.ContactId is int?.

The question is: Why is this happening and how can I fix it?.

Exception information (in portuguese):

System.InvalidOperationException was unhandled
 HResult=-2146233079
 Message=Nenhum método genérico 'Join' no tipo 'System.Linq.Queryable' é compatível com os argumentos e os argumentos de tipo fornecidos. Nenhum argumento de tipo deve ser fornecido se o método for não genérico. 
 Source=System.Core
 StackTrace:
      em System.Linq.Expressions.Expression.FindMethod(Type type, String methodName, Type[] typeArgs, Expression[] args, BindingFlags flags)
      em System.Linq.Expressions.Expression.Call(Type type, String methodName, Type[] typeArguments, Expression[] arguments)
      em problems.Program.Main(String[] args) na d:\POCs\problems\problems\Program.cs:linha 38
      em System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
      em System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
      em Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
      em System.Threading.ThreadHelper.ThreadStart_Context(Object state)
      em System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      em System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      em System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
      em System.Threading.ThreadHelper.ThreadStart()
 InnerException: 
Was it helpful?

Solution

The problem is that the type of your key is int?, but the ParseLambda method that you're using to create the key selector is creating an expression of type Expression<Func<Contact, int>> for your outer key selector. It's not mapping the result to a nullable int. That function is then not compatible with the signature of the Join method, so it throws an exception.

If, for the sake of your example, you use the following expression for your selector, it will work fine:

Expression<Func<Contact, int?>> outerSelectorLambda = c => c.Id;

Looking at the API for ParseLambda, you should be using the second parameter to specify the return type of the lambda, so just don't leave it null:

DynamicExpression.ParseLambda(typeof(Contact), typeof(int?),
    "Id", new object[0]);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top