简体   繁体   English

如何在LINQ中动态创建Joins?

[英]How to dynamically create Joins in LINQ?

I'm trying to dynamically create Joins in LINQ. 我正在尝试在LINQ中动态创建Joins。 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?时,我遇到了问题int? ( int works). int作品)。 When it's an int? 什么时候是int? it will trigger an exception. 它将触发异常。

I've made a simple LINQ to Objects example to show the problem. 我制作了一个简单的LINQ to Objects示例来显示问题。 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. 为了运行我的示例,您将需要安装动态linq Nuget软件包,因为我将其用于DynamicExpression.ParseLambda方法。

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? 为了使以上代码正常工作 ,只需替换两次出现的int? by int . 通过int Everything will work fine. 一切都会正常。 The problem lies in the fact that Address.ContactId is int? 问题在于Address.ContactIdint? .

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: 

The problem is that the type of your key is int? 问题是您的密钥类型是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. ,但是用于创建键选择器的ParseLambda方法正在为外键选择器创建类型为Expression<Func<Contact, int>> It's not mapping the result to a nullable int. 它没有将结果映射到可为null的int。 That function is then not compatible with the signature of the Join method, so it throws an exception. 然后,该函数与Join方法的签名不兼容,因此将引发异常。

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 : 在查看ParseLambda的API时,您应该使用第二个参数指定lambda的返回类型,因此请不要将其保留为null

DynamicExpression.ParseLambda(typeof(Contact), typeof(int?),
    "Id", new object[0]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM