c# - 使用多个联接循环遍历 Linq 结果集

标签 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,};

现在这个结果集将只有一个 ProcessBases,每个ProcessBase将有MULTIPLE InspectorArticles 并且每个 InspectorArticle 将有 MULTIPLE InspectorSamples。因此,当我循环遍历结果集时,我想循环遍历每个 InspectorArticle,然后循环遍历属于该 InspectorArticle 的每个 InspectorSample,像这样:

        foreach (InspectorArticle _iart in results.First().obj2)
        {
          ...          
            foreach (InspectorSample _isamp in results.First().obj3)
            {
               ...
            }

        }

但是由于我在结果集上调用了 .First() ,显然我得到了这个异常:

foreach statement cannot operate on variables of type x because x does not contain a public definition for 'GetEnumerator'

那么如何循环遍历 InspectorArticle 的每个实例,然后循环遍历该文章的 InspectorSamples 数量?

谢谢。

最佳答案

由于您将记录连接在一起,因此此语句不正确:

Now this result set will have only ONE ProcessBases, each ProcessBase will have MULTIPLE InspectorArticles and each InspectorArticle will have MULTIPLE InspectorSamples.

执行查询后,您实际得到的是 IEnumerable其中 IEnumerable 中的每个对象包含对 ProcessBase 的引用,一个InspectorArticle和一个InspectorSample 。例如,在 LinqPad 中使用以下代码将产生 IEnumerable包含以下内容:

代码:

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; }
}

结果:

linqpad results

因此,如果您想按原样保留 Linq 语句并使用多个 foreach 语句循环遍历它,则需要使用类似以下代码的内容:

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 进行分组然后创建一个DictionaryID键入,并包含原始 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);
    }
}

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

请注意,我键入了 Dictionary关闭ID属性,因为我没有提供 IEqualityComparerGroupBy方法。如果您更愿意输入 InspectorArticle对象本身,您需要确保两个不同的 InspectorArticle具有相同 ID 的实例被视为相等,如果您创建 IEqualityComparer 就可以做到这一点并将其传递到 GroupBy方法。

关于c# - 使用多个联接循环遍历 Linq 结果集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9746082/

相关文章:

c# - DB ID 需要一个更小的 GUID 替代品,但 URL 仍然是唯一且随机的

c# - 如何打印出元音字母最多的单词

c# - 在 linq select 语句中使用 func 选择匿名类型的值

c# - LINQ LEFT JOIN where 子句不起作用

linux - 带有 sed 文本文件的 For 循环

c# - 我什么时候知道调用 DoEvents?

c# - 使用 LINQ 交错多个(超过 2 个)不规则列表

java - 几乎同时进行两个循环

Python 分类命令

c# - 如何为 DNN WebAPI 端点提供凭据?