2010-03-10 6 views
2

est-il possible d'écrire ce foreach dans LINQ ou d'une autre meilleure façon,C# LINQ question au sujet foreach

int itemNr = -1; 
foreach(ItemDoc itemDoc in handOverDoc.Assignment.Items) { 

    itemNr++; 
    foreach(ItemDetailDoc detail in itemDoc.Details) { 
     int eventDocNr = -1; 

     foreach(EventDoc eventDoc in detail.Events) { 
      eventDocNr++; 

      if(!eventDoc.HasEAN) { 
       HideShowPanels(pMatch); 
       txt_EAN.Text = String.Empty; 

       lbl_Match_ArtName.Text = itemDoc.Name; 
       lbl_ArtNr.Text = itemDoc.Number; 
       lbl_unitDesc.Text = eventDoc.Description; 

       m_tempItemNr = itemNr; 
       m_tempEventNr = eventDocNr; 
       txt_EAN.Focus(); 

       return; 
      } 
     } 
    } 
} 

Je pense que ce n'est pas la bonne façon de l'écrire. s'il vous plaît donnez votre avis.

Répondre

1

vous pouvez essayer la LINQ suivante:..

var nonEANs = from ItemDoc itemDocs in itemDocList 
       from ItemDetailDoc itemDetailDocs in itemDocs.Details 
       from EventDoc eventDocs in itemDetailDocs.Events 
       where !eventDocs.HasEAN 
       select eventDocs; 


foreach (var i in nonEANs) 
{ 
    System.Diagnostics.Debug.WriteLine(i.HasEAN); 
} 

doit retourner 7 faux EANs: Je vous ai recréé des structures imbriquées comme ce

List<ItemDoc> itemDocList = new List<ItemDoc>() 
{ 
    new ItemDoc() 
    { 
     Details = new List<ItemDetailDoc>() 
     { 
      new ItemDetailDoc() 
      { 
        Events = new List<EventDoc>() 
        { 
         new EventDoc() 
         {HasEAN=false}, 
         new EventDoc() 
         {HasEAN=false} 
        } 
      }, 
      new ItemDetailDoc() 
      { 
        Events = new List<EventDoc>() 
        { 
         new EventDoc() 
         {HasEAN=true}, 
         new EventDoc() 
         {HasEAN=false} 
        } 
      } 
     } 
    }, 
    new ItemDoc() 
    { 
     Details = new List<ItemDetailDoc>() 
     { 
      new ItemDetailDoc() 
      { 
        Events = new List<EventDoc>() 
        { 
         new EventDoc() 
         {HasEAN=false}, 
         new EventDoc() 
         {HasEAN=false} 
        } 
      }, 
      new ItemDetailDoc() 
      { 
        Events = new List<EventDoc>() 
        { 
         new EventDoc() 
         {HasEAN=false}, 
         new EventDoc() 
         {HasEAN=false} 
        } 
      } 
     } 
    } 
}; 
+0

Pas la même chose. Notez comment il a besoin de l'index dans les boucles pour d'autres opérations. – TomTom

+1

Vous n'auriez pas accès au nom et à la description dans itemDoc car seul eventDoc est sélectionné. – Cornelius

+1

Ne permet pas d'accéder au nom et à la description de ItemDoc, par conséquent, il ne fonctionnera pas avec le code à l'intérieur de la boucle. – AxelEckenberger

1

Je pense que vous êtes coincé avec le pour chaque boucle que vous avez besoin de itemNr et eventDocNr. Vous pouvez utiliser les boucles for pour éviter d'augmenter les valeurs itemNr et eventDocNr, mais cela ne réduit pas le nombre de boucles.

Edit:

Et si vous avez besoin le itemNr et eventDocNr essayez ceci:

var query = handOverDoc.Assignment.Items 
         .SelectMany(
          (x, i) => x.Details.SelectMany(
           (d, di) => d.Events.Where(x => x.HasEAN).Select(
            (e, ei) => new { 
             ItemIndex = di, 
             EventIndex = ei, 
             Detail = d, 
             Event = e 
            } 
           ) 
          ) 
         ); 
foreach (var eventInfo in query) { 
    HideShowPanels(pMatch); 
    txt_EAN.Text = String.Empty; 

    lbl_Match_ArtName.Text = eventInfo.Detail.Name; 
    lbl_ArtNr.Text = eventInfo.Detail.Number; 
    lbl_unitDesc.Text = eventInfo.Event.Description; 

    txt_EAN.Focus(); 

    return;  
} 

Si vous avez besoin que le premier événement avec EAN vous pouvez également utiliser les éléments suivants sur la requête ci-dessus:

var item = query.FirstOrDefault(); 
if (item != null) { 
    // do you stuff here 
} 
+0

Mais si je ne le besoin itemNr et eventDocNr plus. Y a-t-il une meilleure façon d'écrire ceci? – Tan

+0

@Tan: voir modifier. – AxelEckenberger

+0

Select a une surcharge qui fournit l'index, voir ma réponse. –

2

Non, je ne pense qu'il y a une meilleure façon de le faire. LINQ concerne les requêtes, vous faites beaucoup de traitement là-dedans. Sauf si vous avez un raccourci qui n'est pas évident ici .... cela semble t obe le seul moyen. Si vous pouviez commencer à partir de l'eventDoc - vous pouvez filtrer ceux qui n'ont pas EAN et ensuite aller de l'arrière, mais je ne peux pas dire comment c'est possible car le modèle complet me manque (comme dans: peut-être vous n'avez pas de retour , de sorte que vous seriez coincé esprit hthe eventDoc un dcoul dnot se lever à l'article

Regardez d'abord qui ressemble bien

+0

Bonjour TomTom, vous pouvez également utiliser LINQ pour interroger les structures d'objet en mémoire ainsi que LINQ to SQL/Entities. –

+0

Indifférent - je n'y suis pas allé. Le problème est que si vous avez une liste de pointeurs eventDoc WITOUT retournés à l'élément, l'interrogation de cette liste ne vous donnera pas les mêmes choses que vous avez avec cette requête. – TomTom

+1

Je crains que je ne sois pas d'accord que c'est bien. Même s'il n'est pas réécrit en tant que linq, il pourrait encore être amélioré de manière significative en séparant plusieurs méthodes pour séparer les deux actions qu'il fait en un seul bloc. –

3

Si itemNr et eventDocNr n'est pas nécessaire que vous pouvez utiliser:

var item = 
      (from itemDoc in handOverDoc.Assignment.Items 
      from detail in itemDoc.Details 
      from eventDoc in detail.Events 
      where !eventDoc.HasEAN 
      select new 
       { 
        Name = itemDoc.Name, 
        Number = itemDoc.Number, 
        Description = eventDoc.Description 
       }).FirstOrDefault(); 

if (item != null) 
{ 
    HideShowPanels(pMatch); 
    txt_EAN.Text = String.Empty; 

    lbl_Match_ArtName.Text = item.Name; 
    lbl_ArtNr.Text = item.Number; 
    lbl_unitDesc.Text = item.Description; 

    txt_EAN.Focus(); 
} 
0

Vous pouvez obtenir l'index dans LINQ assez facilement, par exemple: -

var = itemDocs handOverDoc.Assignment .Items.Select ((h, i) => nouveau {item = h, itemindex = i})

Vous pouvez également répéter ce processus pour vos boucles internes et je suppose que vous pourriez alors utiliser SelectMany() pour le simplifier même furt sa.

0

Vous essayez de faire deux choses différentes ici. Premièrement, vous essayez de trouver un document, et deuxièmement, vous essayez de changer les choses en fonction de cela. La première étape du processus consiste simplement à clarifier le code que vous avez déjà, par ex.

(Notez que cela prend en compte les commentaires précédents que les index calculés dans le code original ne sont pas nécessaires.) Le même type de scission en deux méthodes pourrait être fait que les index calculés soient ou non requis. améliorer le code original.)

public void FindAndDisplayEventDocWithoutEAN(HandOverDoc handOverDoc) 
{ 
    var eventDoc = FindEventDocWithoutEAN(handOverDoc); 
    if (eventDoc != null) 
    { 
     Display(eventDoc); 
    } 
} 

public EventDoc FindEventDocWithoutEAN(HandOverDoc handOverDoc) 
{ 
    foreach(ItemDoc itemDoc in handOverDoc.Assignment.Items) 
     foreach(ItemDetailDoc detail in itemDoc.Details) 
      foreach(EventDoc eventDoc in detail.Events) 
       if(!eventDoc.HasEAN) 
        return eventDoc; 
    return null; 
} 

public void Display(EventDoc eventDoc) 
{ 
    HideShowPanels(pMatch); 
    txt_EAN.Text = String.Empty; 

    lbl_Match_ArtName.Text = itemDoc.Name; 
    lbl_ArtNr.Text = itemDoc.Number; 
    lbl_unitDesc.Text = eventDoc.Description; 

    m_tempItemNr = itemNr; 
    m_tempEventNr = eventDocNr; 
    txt_EAN.Focus(); 
} 

Une fois que vous avez fait cela, vous devriez être en mesure de voir qu'une méthode est une requête sur le document principal, et l'autre est une méthode pour afficher les résultats de la requête. C'est ce qu'on appelle le principe de la responsabilité unique, où chaque méthode fait une chose, et est nommé d'après ce qu'elle fait.

La transformation des boucles foreach imbriquées en une requête linq est maintenant presque triviale. Comparez la méthode ci-dessous avec la méthode ci-dessus, et vous pouvez voir à quel point il est mécanique de traduire des boucles foreach imbriquées dans une requête linq.

public EventDoc FindEventDocWithoutEAN(HandOverDoc handOverDoc) 
{ 
    return (from itemDoc in handOverDoc.Assignment.Items 
      from detail in itemDoc.Details 
      from eventDoc in detail.Events 
      where !eventDoc.HasEAN 
      select eventDoc).FirstOrDefault(); 
} 
+0

Et d'où provient-il itemNr et eventDocNr? –

+0

@Hightechrider - J'ai déjà expliqué que je ne les ai pas inclus en fonction de ses commentaires à d'autres réponses, et que la principale transformation en deux méthodes est valable dans les deux cas. La définition de l'état du niveau de module dans les requêtes est presque toujours la mauvaise approche, donc je suppose que cette partie va être améliorée pour ne pas avoir besoin de ces index non plus. –

0

encore un autre tour ...

var query = from itemDocVI in handOverDoc.Assignment 
            .Items 
            .Select((v, i) => new { v, i }) 
      let itemDoc = itemDocVI.v 
      let itemNr = itemDocVI.i 
      from detail in itemDoc.Details 
      from eventDocVI in detail.Events 
            .Select((v, i) => new { v, i }) 
      let eventDoc = eventDocVI.v 
      let eventDocNr = eventDocVI.i 
      where eventDoc.HasEAN 
      select new 
      { 
       itemDoc, 
       itemNr, 
       detail, 
       eventDoc, 
       eventDocNr 
      }; 

var item = query.FirstOrDefault(); 
if (item != null) 
{ 
    HideShowPanels(pMatch); 
    txt_EAN.Text = String.Empty; 

    lbl_Match_ArtName.Text = item.itemDoc.Name; 
    lbl_ArtNr.Text = item.itemDoc.Number; 
    lbl_unitDesc.Text = item.eventDoc.Description; 

    m_tempItemNr = item.itemNr; 
    m_tempEventNr = item.eventDocNr; 
    txt_EAN.Focus(); 
}