LINQ to XML + left join + group by = fail

时间:2009-07-16 05:37:53

标签: c# linq linq-to-xml

我有两张桌子:

block: int id, [other columns...]
blockInstance: int id, int blockId (references Block.Id), [other columns...]

我的目标是生成具有两个属性的块对象的枚举:(1)Id(块的Id)和(2)InstanceCount(块的实例数)。目前,我的XML文件不包含块实例(该表存在但行数为零)。

这是我的(非工作)查询:

var groupedBlocks =
    from
        b in _tableDictionary["block"].Elements()
    join
        bi in _tableDictionary["blockInstance"].Elements()
        on b.Element(_ns + "id").Value equals bi.Element(_ns + "blockId").Value into j
    from
        lj in j.DefaultIfEmpty()
    group
        lj by b.Element(_ns + "id").Value into g
    select new
    {
        Id = g.Key,
        InstanceCount = g.Count(i => i.Element(_ns + "blockId") != null)
    };

问题在于g.Count()的谓词(lambda表达式)。如果我删除谓词并只使用g.Count(),则查询会生成适当的行数,但每行的InstanceCount为1,这是错误的(它应该为零)。使用谓词,查询返回零行,如果我尝试在调试器中查看ResultsView,则会显示“异常:对象引用未设置为对象的实例”。

我最大的困惑是我的lambda表达式中 “i”的确切含义。我知道它是一个XElement,但是当数据是连接的结果时(在这种情况下,左外连接),这个XElement包含什么?

好吧,当我输入这个时我得到了一个更多的想法,它确实有效:-),但我不明白为什么:-(所以我仍然会问这个问题;-)。

如果我将违规代码更改为......

InstanceCount = g.Count(i => i != null)

......它有效。但为什么?!再次,什么是传递到lamba表达式?为什么它是空的?

谢谢!

2 个答案:

答案 0 :(得分:2)

i 在您的群组计数中引用相当于 lj ,这是从枚举 j.DefaultIfEmpty()返回的。

此上下文中的

DefaultIfEmpty()将返回项目类型的默认值,其中该项目与前一个 join..into 声明。

对于引用类型( XElement 是引用类型),默认值始终为null,这就是获取空引用异常的原因,以及为什么先检查null会删除问题

编辑:

使用子查询避免分组的替代方法:

var groupedBlocks = 
    from b in _tableDictionary["block"].Elements() 
    select new 
    { 
        Id = b.Element(_ns + "id").Value, 
        InstanceCount = (from bi in _tableDictionary["blockInstance"].Elements() 
                         where bi.Element(_ns + "blockId").Value == 
                               b.Element(_ns + "id").Value
                         select bi).Count()
    };

答案 1 :(得分:0)

传递给Count lambda的参数与您要分组的参数类型相同。那是因为您在g IGrouping<string, XElement>的上下文中调用了Count。

因此,iXElement

在您的第一个查询中,您实际上是在检查分组XElement的第一个孩子,(访问i.Element方法),这就是为什么它没有用。