【问题标题】:How to implement left join in JOIN Extension method如何在 JOIN 扩展方法中实现左连接
【发布时间】:2010-09-25 07:20:12
【问题描述】:

我正在尝试对p.Person 表的这种查询实现外连接。 我该怎么做?

本例取自http://ashishware.com/DSLinqExample.shtml

var onlyinfo = p.Person
    .Where(n => n.FirstName.Contains('a'))
    .Join(p.PersonInfo,
        n => n.PersonId,
        m => m.PersonId,
        (n, m) => m)
    .ToArray<Persons.PersonInfoRow>();

【问题讨论】:

    标签: c# linq extension-methods


    【解决方案1】:

    LINQ 中的左连接通常使用组连接建模,有时与DefaultIfEmptySelectMany 结合使用:

    var leftJoin = p.Person.Where(n => n.FirstName.Contains("a"))
                           .GroupJoin(p.PersonInfo, 
                                      n => n.PersonId,
                                      m => m.PersonId,
                                      (n, ms) => new { n, ms = ms.DefaultIfEmpty() })
                           .SelectMany(z => z.ms.Select(m => new { n = z.n, m }));
    

    这将给出一个对 (n, m) 的序列,其中 n 是来自 p.Person 的条目,m 是来自 p.PersonInfo 的条目,但如果没有匹配项,m 将为空.

    (它完全未经测试,顺便说一句 - 但无论如何应该给你这个想法:)

    【讨论】:

    • 第一个匿名投影不起作用 - 编译器说'无效的匿名类型成员声明器。匿名类型成员必须使用成员赋值、简单名称或成员访问来声明
    • 使用 SQL 分析器,我看到两个单独的查询,每个表一个。这是否意味着左连接是在内存中执行的,而不是由 SQL 服务器执行的? TIA。
    • @Sнаđошƒаӽ:我会期望这一切都在 SQL Server 中完成,但由于无法自己进行实验,我不想肯定地说。 (我们没有任何关于这是否是 EF、Linq to SQL 等等的上下文。)我建议你问一个新问题。
    【解决方案2】:

    对于左外连接尝试以下查询。这是经过测试的

    var leftJoin = Table1
                    .GroupJoin(
                                   inner: Table2,
                        outerKeySelector: t1 => t1.Col1,
                        innerKeySelector: t2 => t2.Col2,
                          resultSelector: ( t1, t2Rows ) => new { t1, t2Rows.DefaultIfEmpty() }
                    )
                    .SelectMany( z =>
                        z.t2Rows.Select( t2 =>
                            new { t1 = z.t1, t2 = t2 }
                        )
                    );
    

    【讨论】:

      【解决方案3】:

      如果有人遇到此问题并希望使用扩展方法来完成此问题,我使用与其他答案相同的方法创建了一个。它与常规连接扩展方法具有相同的签名。

          public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
              IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
              Func<TOuter, TInner, TResult> resultSelector)
          {
              return outer
                  .GroupJoin(inner, outerKeySelector, innerKeySelector, (outerObj, inners) =>
                  new 
                  {
                      outerObj,
                      inners= inners.DefaultIfEmpty()
                  })
              .SelectMany(a => a.inners.Select(innerObj => resultSelector(a.outerObj, innerObj))); 
          }
      

      【讨论】:

      • 在我的测试中,这不会发出 SQL 左连接查询,而是执行两个相互独立的单独查询,然后执行左连接在内存中。但是当我在原地做同样的事情(即不使用扩展方法)时,它会按预期发出left outer join
      【解决方案4】:

      我在我的项目中使用了以下扩展方法,它可能对你有所帮助。

      /// <summary>
      /// Performs a left outer join on two collections.
      /// </summary>
      /// <typeparam name="TLeft">Type of left IEnumerable collection</typeparam>
      /// <typeparam name="TRight">Type of left IEnumerable collection</typeparam>
      /// <typeparam name="TKey">The type of the key returned by the key selector functions.</typeparam>
      /// <typeparam name="TResult">The type of the result elements.</typeparam>
      /// <param name="left"> The left IEnumerable collection of the join operation.</param>
      /// <param name="right"> The right IEnumerable collection of the join operation.</param>
      /// <param name="leftKeySelector">Function that projects the key given an element from <paramref name="left"/>.</param>
      /// <param name="rightKeySelector">Function that projects the key given an element from <paramref name="right"/>.</param>
      /// <param name="resultSelector">Function that projects the result given 
      ///     an element from <paramref name="left"/> and 
      ///     an element from <paramref name="right"/>
      ///     that match on a common key.</param>
      /// <returns>A sequence containing results projected from a left outer join of the two input collections.</returns>
      
      public static IQueryable<TResult> LeftJoin<TLeft, TRight, TKey, TResult>(
          this IQueryable<TLeft> left,
          IQueryable<TRight> right,
          Expression<Func<TLeft, TKey>> leftKeySelector,
          Expression<Func<TRight, TKey>> rightKeySelector,
          Expression<Func<TLeft, TRight, TResult>> resultSelector)
      {
          if (left == null) throw new ArgumentNullException(nameof(left));
          if (right == null) throw new ArgumentNullException(nameof(right));
          if (leftKeySelector == null) throw new ArgumentNullException(nameof(leftKeySelector));
          if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector));
          if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
      
          return left
              .AsExpandable() // LinqKit to convert everything into an expression tree.
              .GroupJoin(
                  right,
                  leftKeySelector,
                  rightKeySelector,
                  (leftItem, rightItem) => new { leftItem, rightItem })
              .SelectMany(
                  joinResult => joinResult.rightItem.DefaultIfEmpty(),
                  (joinResult, rightItem) =>
                      resultSelector.Invoke(joinResult.leftItem, rightItem));
      }
      

      这就是它的名字

      base.context.Users
          .LeftJoin(
              base.context.Workers
              user => user.Userid,
              worker => worker.Workerid,
              (user, worker) => new
              {
                  User = user,
                  Worker = worker
              })
      

      在这里得到这个答案,Trying to implement a LeftJoin extension method to work with EF Core 2.0

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-04
        相关资源
        最近更新 更多