我有一个复杂的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,然后遍历属于该InspectorSample的每个InspectorArticle,如下所示:

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

        }


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


  foreach语句无法对类型x的变量进行操作,因为x
  不包含“ GetEnumerator”的公共定义


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

谢谢。

最佳答案

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


  现在,此结果集将只有一个ProcessBase,每个ProcessBase
  将有多个InspectorArticles,每个InspectorArticle将
  有多个InspectorSamples。


执行查询后实际上将拥有一个IEnumerable,其中IEnumerable中的每个对象都包含对ProcessBaseInspectorArticleInspectorSample的引用。例如,在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; }
}


结果:



因此,如果您希望保持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分组,然后创建一个以Dictionary为键的ID,其中包含原始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,所以我将IEqualityComparer设置为GroupBy属性的键。如果您想通过InspectorArticle对象本身作为键,则需要确保将具有相同InspectorArticle的两个不同ID实例视为相等,如果创建IEqualityComparer并通过将其放入GroupBy方法。

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

10-10 14:06