GroupJoin()之后的SelectMany()

时间:2014-02-12 15:28:29

标签: c# linq

基本上,我想要做的是将两个表外部连接并将它们呈现在一个平坦的结果中。为简单起见,我的两个表看起来像这样:

tot["nameA", "nameB", "nameC"]
critItg["nameA", "nameB"]

我希望左外连接后的结果如下所示:

leftName, rightName
"nameA", "nameA"
"nameB", "nameB"
"nameC", empty/null

我设法通过以下方式执行左外连接:

var res = tot.GroupJoin
                (
                    critITG,
                    left => left.totName,
                    right => right.critITGName,
                    (left, right) => new
                    {
                        tot = left,
                        critITG = right.FirstOrDefault()
                    }
                );

但是,结果分组如下:

{ tot = { totName = "nameA" }, { critITG = "nameA"} }
{ tot = { totName = "nameB" }, { critITG = "nameB"} }
{ tot = { totName = "nameC" }, { critITG = null} }

我希望结果看起来像这样:

{ totName = "nameA", critITG = "nameA" }

我已经读过,展平左外连接结果的解决方案是SelectMany(),但是我在上面的结果集上实现它时遇到了麻烦。以下结果集是“对象引用未设置为对象的实例:

var res = tot.GroupJoin
                (
                    critITG,
                    left => left.totName,
                    right => right.critITGName,
                    (left, right) => new
                    {
                        tot = left,
                        critITG = right.FirstOrDefault()
                    }
                )
                .SelectMany
                (
                    right => right.critITG.critITGName.DefaultIfEmpty(),
                    (left, right) => 
                        new 
                        { 
                            leftName = left.tot.totName, 
                            rightName = right 
                        }
                );

感谢您的帮助!

4 个答案:

答案 0 :(得分:3)

看起来你已经陷入了lambda结构的技术细节,这可能导致代码可维护性随着时间的推移而降低。我经常发现查询语法比连接的lambda语法更简单,更易于维护。请考虑以下事项:

var tots = new string[] {"nameA", "nameB", "nameC"};
var critItgs = new string[] {"nameA", "nameB"};

var query = from tot in tots
      join critItg in critItgs on tot equals critItg into joined
      from row in joined.DefaultIfEmpty()
      select new {totName = tot, critItg = row};
query.Dump();

答案 1 :(得分:2)

不确定这是否是您需要的......但您是否尝试过选择每个字段的名称?

var res = tot.GroupJoin
            (
                critITG,
                left => left.totName,
                right => right.critITGName,
                (left, right) => new
                {
                    tot = left.totName,
                    critITG = right.FirstOrDefault() == null ? null : right.critITGName
                }
            );

编辑: 我也同意@Jim Wooley,有时候不使用lambda表达式会更容易。 请注意,请记住您需要将totName和critITGName添加到他的代码中。

  var res = from totElement in tot
  join critItgItem in critItgon totElement.Name equals critItgItem.Name into joined
  from row in joined.DefaultIfEmpty()
  select new {totName = tot.Name, critItg = (row == null ? String.Empty : row.Name) };

答案 2 :(得分:1)

此处您不需要SelectMany。您可以使用Select,但只需在传递给GroupJoin的代理中选择所需的数据:

var res = tot.GroupJoin
                (
                    critITG,
                    left => left.totName,
                    right => right.critITGName,
                    (left, right) => {
                        var mr = right.FirstOrDefault();
                        return new
                        {
                            totName = left.totName,
                            critITG = mr == null ? null : mr.critITGName
                        };
                    }
                );

答案 3 :(得分:1)

嗨与Lee的例子类似但有些不同,这里我在最终的匿名类型中使用了一个select语句:

var res = tot.GroupJoin(critItg,
            left => left.Name,
            right => right.Name,
            (left, right) => new
            {
                tot = left.Name,
                critItg = right.Select(x => x.Name).FirstOrDefault()
            });