2012-04-03 15 views
6

Czy istnieje sposób "LINQ" na warunkowy wybór danych, tj. Wybór z innego źródła, jeśli pierwszy był pusty? Jednym z przykładów jest sytuacja, gdy masz strukturę drzewiastą przedmiotów i chcesz uzyskać jakiś zasób z katalogu głównego, lub jeśli jest pusty, z jego elementów potomnych.Wybór warunkowy w LINQ (wybierz zamiast pustego)

Mam następujący przykład:

IEnumerable<Item> items = ...; 
// Item has a Assets property that returns IEnumerable<Asset> 
// Item has a SubItems property that returns IEnumerable<Item> 
// i.e. other items with assets in them 

// getting assets from a "main" item 
var assets = item.Assets.Where(a => HasRelevantAsset(a)); 

// if there were no relevant assets in the "main" item 
if (!assets.Any()) { 
    // then reselect from "subitems" assets instead 
    assets = item.SubItems.SelectMany(item => 
     item.Assets.Where(a => HasRelevantAsset(a))); 
} 

// HasRelevantAsset(Asset) is a static method that returns 
// true if it is the asset that is needed 
+0

Wydaje się, że jest to całkiem dobry użytek dla? operator - 'var assets = something ?? something_else'. To nie zadziała, ale byłoby miło, gdyby tak się stało. – zmbq

Odpowiedz

1

Uważam, że sposób LINQ będzie wyglądać trochę brzydki

var assets = item.Any(a=>HaRelevantAsset(a)) ? item.Where(a => HasRelevantAsset(a)) : 
        item.SubItems.SelectMany(item => 
          item.Assets.Where(a => HasRelevantAsset(a))); 

wybrałbym innego wariantu, metoda rozszerzenie

public static IEnumerable<Asset> SelectRelevantAssets(this Item item) 
{ 
    var assetsInItemFound = false; 
    foreach (var asset in item.Assets) 
    { 
     if (HasRelevantAsset(asset)) 
     { 
      assetsInItemFound = true; 
      yield return asset; 
     } 
    } 
    if (assetsInItemFound) 
    { 
     yield break; 
    } 
    else 
    { 
     foreach (var subItem in item.SubItems)   
      foreach (var asset in subItem.Assets) 
       if (HasRelevantAsset(asset)) 
        yield return asset; 
    } 
} 





Najpierw chciałem wypróbować rekurencyjne wywołanie SelectRelevantAssets, myślę, że będzie jak

if (!assetsInItemFound) 
     { 
      yield break; 
     } 
     else 
     { 
      foreach (var subItem in item.SubItems)   
       foreach (var asset in SelectRelevantAssets(subItem)) 
         yield return asset; 
     } 

Ale to będzie obejmować aktywa znajdujące się w zbiorach pozycje aktywów podpunkt