【问题标题】:LINQ 3 Inner Joins with 1 Left Outer JoinLINQ 3 内连接和 1 个左外连接
【发布时间】:2019-08-19 13:46:01
【问题描述】:

想知道为什么 LINQ 没有 Left Join 方法。我一直试图用无数关于 SO 的例子来解决这个问题,但没有这样的运气。其他示例显示了一个连接的简单示例。如果我对连接进行分组,那么我只会在 select 语句中获得对 TradeCountries 表的引用。

作为 LINQ 的新手,我可以在 4 小时前用一个简单的 SELECT 语句完成这项工作,但在这里我试图弄清楚为什么 LeftJoin 方法被排除在 LINQ 之外。

需要更改带有“LeftJoin”的行以使其正常工作?

/* 
* GetTop5Distributors 
@param  int array of series IDs
*/
public List<TopDistributors> Get5TopDistributors(IEnumerable<int> seriesIds)
{
    _context = new MySQLDatabaseContext();
    var result = _context.TradesTrades
.Join(_context.TradesSeries, tt => tt.SeriesId, ts => ts.Id, (tt, ts) => new { tt, ts })
.Join(_context.TradesTradeDistributors, tsd => tsd.tt.Id, ttd => ttd.TradeId,
    (tsd, ttd) => new { tsd, ttd })
.Join(_context.TradesOrganisations, tsdto => tsdto.ttd.DistributorId, to => to.Id,
    (tsdto, to) => new { tsdto, to })
.LeftJoin(_context.TradesCountries, tsdc => tsdc.to.CountryId, tc => tc.Id, 
    (tsdc, tc) => new {tsdc, tc})
.Where(x => seriesIds.Contains(x.tsdc.tsdto.tsd.tt.SeriesId))
.Where(x => x.tsdc.tsdto.tsd.tt.FirstPartyId == null)
.Where(x => x.tsdc.tsdto.tsd.tt.Status != "closed")
.Where(x => x.tsdc.tsdto.tsd.tt.Status != "cancelled")
.GroupBy(n => new { n.tsdc.tsdto.tsd.tt.SeriesId, n.tsdc.tsdto.ttd.DistributorId })
.Select(g =>
    new TopDistributors
    {
        SeriesId = g.Key.SeriesId,
        DistributorName = g.Select(i => i.tsdc.to.Name).Distinct().First(),
        IsinNickname = g.Select(i => i.tsdc.tsdto.tsd.ts.Nickname).Distinct().First(),
        CountryName = g.Select(i => i.tc.Name).Distinct().First(),
        CommissionTotal = Math.Ceiling(g.Sum(i => i.tsdc.tsdto.ttd.Commission))
    }
)
.OrderByDescending(x => x.CommissionTotal)
.Take(5)
.ToList();

    return result;
}

这是一个相当简单的 select 语句,它需要几个数量级或数量级的时间来转换为 LINQ。

SELECT
trades_trades.series_id,
trades_organisations.`name`,
trades_series.nickname,
trades_countries.name as Country_Name,
SUM(trades_trade_distributors.commission) as Commission_Total
FROM
trades_trades
JOIN trades_series
ON trades_series.id = trades_trades.series_id
JOIN trades_trade_distributors
ON trades_trades.id = trades_trade_distributors.trade_id
JOIN trades_organisations
ON trades_trade_distributors.distributor_id = trades_organisations.id
LEFT JOIN trades_countries
ON trades_organisations.country_id = trades_countries.id
WHERE trades_trades.series_id   IN (
    17,
    18)
    AND trades_trades.first_party_id IS NULL
    AND trades_trades.status <> 'closed'
    AND trades_trades.status <> 'cancelled'
GROUP BY trades_trades.series_id, trades_trade_distributors.distributor_id
ORDER BY Commission_Total DESC

【问题讨论】:

  • 您需要GroupJoin。也许这可能有用:Perform left outer joins
  • LEFT OUTER JOIN in LINQ的可能重复
  • 我看过这些例子。其他示例显示了一个连接的简单示例。如果我对连接进行分组,那么我只会在 select 语句中获得对 TradeCountries 表的引用。
  • 使用方法链接语法并以这种方式传递变量非常繁琐,因为您需要使用new {}(匿名对象)语法传递它们。也许您可以使用查询语法重写您的查询。它还使它更具可读性。
  • 也许我的SQL to LINQ Recipe 可以帮助你?

标签: c# asp.net linq


【解决方案1】:

按照我的方法,这里是一个或多或少直接将 SQL 转换为 LINQ 的方法。我将where 移动到接近其约束的位置,并使用letSum 创建一个方便的名称,因为LINQ 不允许您转发引用匿名对象成员。

var ans = from tt in trades_trades
          where new[] { 17, 18 }.Contains(tt.series_id) && tt.first_party_id == null &&
                tt.status != "closed" && tt.status != "cancelled"
          join ts in trades_series on tt.series_id equals ts.id
          join ttd in trades_trade_distributors on tt.id equals ttd.trade_id
          join to in trades_orginizations on ttd.distributor_id equals to.id
          join tc in trades_countries on to.country_id equals tc.id into tcj
          from tc in tcj.DefaultIfEmpty() // GroupJoin -> left join
          group new { tt, ts, ttd, to, tc } by new { tt.series_id, ttd.distributor_id } into tradeg
          let Commission_Total = tradeg.Sum(trade => trade.ttd.commission)
          orderby Commission_Total descending
          select new {
            tradeg.Key.series_id,
            tradeg.First().to.name,
            tradeg.First().ts.nickname,
            Country_Name = tradeg.First().tc == null ? null : tradeg.First().tc.name,
            Commission_Total
          };

【讨论】:

  • 有趣。这绝对是我还没见过的顺序。出现错误:System.NullReferenceException:对象引用未设置为对象的实例。在 lambda_method(Closure, IGrouping`2) 国家名称可以为空,这就是为什么左连接首先,所以我需要空记录,但我不确定如何返回这些记录并避免此错误。跨度>
  • @Intrepid2020 你可以试试tradeg.First()?.tc.name 吗?您使用的是 LINQ to SQL 还是 EF 或 EF Core?
  • 那行不通。我正在使用 EF Core。抱歉,我应该在之前把它包括在内。
  • @Intrepid2020 你能确认Country_Name 是错误发生的地方吗(注释掉该成员)?假设这是问题所在,我更新了我的答案以尝试使用条件测试。
  • 是的,这行得通。我想知道为什么 null 合并功能或 null 传播运算符在 Core 中都不起作用。无论如何,我有很多关于 LINQ 的知识要学习。您的示例和您的 SQL to LINQ 配方是我将用来构建的很好的资源。感谢您对此的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-31
  • 2017-10-01
  • 2011-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多