2012-03-17 1 views
1

У меня есть комплекс LINQ запрос какПеребор в результате 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,}; 

Теперь этот результирующий набор будет иметь только ONEProcessBases, каждый ProcessBase будет иметь НЕСКОЛЬКИХInspectorArticles и каждый InspectorArticle будет есть MULTIPLEInspectorSamples. Поэтому, когда я Переберите набора результатов, я хочу Переберите каждого InspectorArticle, а затем цикл по каждому InspectorSample, который принадлежит к той InspectorArticle, что-то вроде:

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

     } 

Но так как я назвал .First() на мой результат установить, очевидно, я получаю это исключение:

оператор Еогеасп не может работать с переменными типа х, поскольку х это не содержит публичное определение «GetEnumerator»

Итак, как я могу проходить через каждый экземпляр InspectorArticle, а затем прокручивать число InspectorSamples для этой статьи?

Спасибо.

ответ

4

Поскольку вы присоединяетесь записи вместе, это утверждение не верно:

Теперь этот результирующий набор будет иметь только один ProcessBases, каждый ProcessBase будет иметь несколько InspectorArticles и каждый InspectorArticle будет иметь несколько 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, вам нужно будет использовать что-то вроде кода belo ш:

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 

Недостатком этого подхода является то, что вы испытываете, чтобы перечислить список результатов нескольких раз. Если вы знаете, что этот список всегда будет небольшим, то это не так уж и важно. Если список будет очень большим, тогда вам может потребоваться лучший способ вернуть данные.

EDIT

Чтобы сделать код более эффективным, вы могли бы группировать по 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.

+0

Большое вам спасибо :) –

+0

Рад, что я мог помочь! – rsbarro

+1

@ClunkyCoder Подумал об этом немного больше и разместил более эффективный метод, если у вас есть более длинные списки. – rsbarro