循环通过具有多个联接的Linq结果集

时间:2012-03-17 00:14:17

标签: c# linq loops join enumeration

我有一个复杂的LINQ查询,如

        var results = from obj1 in context.ProcessBases
                      join obj2 in context.InspectorArticles
                      on obj1.ID equals obj2.ProcessBaseID
                      join obj3 in context.InspectorSamples
                      on obj2.ID equals obj3.InspectorArticleID
                      where obj1.ID == _processBaseID
                      select new {obj1, obj2, obj3,};

现在,此结果集只有 ONE ProcessBases,每个ProcessBase都有 MULTIPLE InspectorArticles,每个{{1}将有 MULTIPLE InspectorArticle。因此,当我循环遍历结果集时,我想循环遍历每个InspectorSamples,然后遍历属于InspectorArticle的每个InspectorSample,类似于:

InspectorArticle

但是因为我在我的结果集上调用 foreach (InspectorArticle _iart in results.First().obj2) { ... foreach (InspectorSample _isamp in results.First().obj3) { ... } } 显然我得到了这个例外:

  

foreach语句不能对x类型的变量进行操作,因为x确实如此   不包含'GetEnumerator'的公共定义

那么如何循环遍历.First()的每个实例,然后遍历该文章的InspectorArticle个数?

感谢。

1 个答案:

答案 0 :(得分:4)

由于您一起加入记录,因此该陈述不正确:

  

现在这个结果集只有一个ProcessBase,每个ProcessBase   将有MULTIPLE InspectorArticles和每个InspectorArticle   拥有MULTIPLE InspectorSamples。

执行查询后您实际拥有的是IEnumerable,其中IEnumerable中的每个对象都包含对ProcessBaseInspectorArticle和{的引用{1}}。例如,使用LinqPad中的以下代码将产生InspectorSample,其中包含以下内容:

<强>代码:

IEnumerable

<强>结果:

linqpad results

因此,如果您希望保持Linq语句不变并使用多个foreach语句遍历它,则需要使用类似下面的代码:

void Main()
{
    var processBases = new List<ProcessBase>();
    var inspectorArticles = new List<InspectorArticle>();
    var inspectorSamples = new List<InspectorSample>();
    processBases.Add(new ProcessBase { ID = 1 });
    processBases.Add(new ProcessBase { ID = 2 });
    inspectorArticles.Add(new InspectorArticle { ID = 3, ProcessBaseID = 1 });
    inspectorArticles.Add(new InspectorArticle { ID = 4, ProcessBaseID = 1 });
    inspectorArticles.Add(new InspectorArticle { ID = 5, ProcessBaseID = 2 });
    inspectorSamples.Add(new InspectorSample { ID = 6, InspectorArticleID = 3 });
    inspectorSamples.Add(new InspectorSample { ID = 7, InspectorArticleID = 3 });
    inspectorSamples.Add(new InspectorSample { ID = 8, InspectorArticleID = 3 });
    inspectorSamples.Add(new InspectorSample { ID = 9, InspectorArticleID = 4 });
    inspectorSamples.Add(new InspectorSample { ID = 10, InspectorArticleID = 5 });
    inspectorSamples.Add(new InspectorSample { ID = 11, InspectorArticleID = 5 });

    var processBaseID = 1;
    var results =   from obj1 in processBases
                    join obj2 in inspectorArticles on obj1.ID equals obj2.ProcessBaseID
                    join obj3 in inspectorSamples on obj2.ID equals obj3.InspectorArticleID
                    where obj1.ID == processBaseID
                    select new { obj1, obj2, obj3 };
    Console.WriteLine(results);
}

public class ProcessBase
{
    public int ID { get; set; }
}

public class InspectorArticle
{
    public int ID { get; set; }
    public int ProcessBaseID { get; set; }
}

public class InspectorSample
{
    public int ID { get; set; }
    public int InspectorArticleID { get; set; }
}

使用此代码(并继续上面的示例)可以获得输出:

foreach(var article in results.GroupBy(g => g.obj2.ID))
{
    Console.WriteLine("Article ID: #{0}", article.Key);
    foreach(var sample in results
                          .Where(s => s.obj3.InspectorArticleID == article.Key)
                          .Select(s => s.obj3))
    {
        Console.WriteLine("\tSample ID: #{0}", sample.ID);
    }
}

这种方法的缺点是你必须多次枚举结果列表。如果你知道这个列表总是很小,那就不是什么大问题了。如果列表非常大,那么您可能想要一种更好的方法来返回数据。

修改

为了提高代码效率,您可以按Article ID: #3 Sample ID: #6 Sample ID: #7 Sample ID: #8 Article ID: #4 Sample ID: #9 进行分组,然后创建一个由InspectorArticle.ID键入的Dictionary,其中包含原始ID和相关{ {1}}。

InspectorArticle

上面的代码会产生与我的第一个示例相同的结果,但是对于更长的文章和样本列表会更有效率,因为它只会在构建InspectorSamples时枚举整个列表一次。

请注意,我将var articles = results.GroupBy(g => g.obj2.ID) .ToDictionary(k => k.Key, v => new { InspectorArticle = v.Select(s => s.obj2).First(), InspectorSamples = v.Select(s => s.obj3) }); foreach(var article in articles.OrderBy(a => a.Key).Select(kv => kv.Value)) { Console.WriteLine("Article ID: #{0}", article.InspectorArticle.ID); foreach(var sample in article.InspectorSamples) { Console.WriteLine("\tSample ID: #{0}", sample.ID); } } 的{​​{1}}键入了Dictionary,因为我没有向Dictionary方法提供ID。如果您希望通过IEqualityComparer对象本身键入,则需要确保将具有相同GroupBy的两个不同InspectorArticle实例视为相等,这可以在您创建时完成InspectorArticle并将其传递给ID方法。