2010-10-27 6 views
6

Ho un enum chiamato OrderStatus, e contiene vari stati che un ordine può essere in:Come usare LINQ Contiene() per trovare un elenco di enumerazioni?

  • Creato
  • attesa
  • attesa
  • valido
  • attivo
  • trasformati
  • Completato

Quello che voglio fare è creare una dichiarazione LINQ che mi dirà se l'OrderStaus sia valido, attivo, trasformati o Completato.

In questo momento ho qualcosa di simile:

var status in Order.Status.WHERE(status => 
     status.OrderStatus == OrderStatus.Valid || 
     status.OrderStatus == OrderStatus.Active|| 
     status.OrderStatus == OrderStatus.Processed|| 
     status.OrderStatus == OrderStatus.Completed) 

che funziona, ma è molto "prolisso". C'è un modo per convertire questo a una dichiarazione Contains() e accorciare un po '?

risposta

14

Certo:

var status in Order.Status.Where(status => new [] { 
     OrderStatus.Valid, 
     OrderStatus.Active, 
     OrderStatus.Processed, 
     OrderStatus.Completed 
    }.Contains(status.OrderStatus)); 

Si potrebbe anche definire un metodo di estensione In() che avrebbe accettato un oggetto e una matrice params, e in fondo avvolge la Contiene funzione: si

public static bool In<T>(this T theObject, params T[] collection) 
{ 
    return collection.Contains(theObject); 
} 

Questo permette di specificare la condizione in un modo più SQL-ish:

var status in Order.Status.Where(status => 
    status.OrderCode.In(
     OrderStatus.Valid, 
     OrderStatus.Active, 
     OrderStatus.Processed, 
     OrderStatus.Completed)); 

Unders tand che non tutti i provider di Linq amano i metodi di estensione personalizzati nei loro lambda. NHibernate, per esempio, non si tradurrà correttamente la funzione In() senza codice aggiuntivo per estendere il parser di espressioni, ma contiene() funziona bene. Per gli oggetti Linq 2, nessun problema.

+0

Fantastico, grazie! –

0

Assumnig che l'enumerazione è definito nell'ordine specificato nella domanda, si potrebbe ridurre questo utilizzando un confronto integer.

var result = 
    Order.Status.Where(x => 
    (int)x >= (int)OrderStatus.Valid & 
    & (int)x <= (int)OrderStatus.Completed); 

Questo tipo di confronto però può essere considerato traballante. Un semplice riordinamento dei valori di enumerazione interromperà silenziosamente questo confronto. Preferirei mantenere la versione più verbale e probabilmente ripulirla rimodellando il confronto con un metodo separato.

+0

Buona risposta per ora, ma potrebbe diventare più fragile dopo. La comprensione di questo codice richiederebbe il controllo dell'enum per conoscere TUTTI i codici che rientrano nell'intervallo definito e lo sviluppatore deve sempre essere in grado di definire un intervallo continuo contenente esattamente i codici desiderati (se è stata posta una costante Spedito tra Elaborato e Completato che il l'utente non voleva nei loro risultati, questo codice si rompe). – KeithS

+0

@KeithS, sì, l'ho coperto nell'ultimo paragrafo della mia risposta. – JaredPar

+0

Sì, d'accordo, un po 'troppo traballante per la mia applicazione, tuttavia mi piace molto come l'hai fatto, in quanto rende il codice un po' più flessibile. Ad esempio, potremmo facilmente aggiungere un nuovo stato senza rompere il codice. –

2

Ho usato questa estensione:

public static bool IsIn<T>(this T value, params T[] list) 
    { 

        return list.Contains(value);   
    } 

è possibile utilizzare questo come condizione:

Where(x => x.IsIn(OrderStatus.Valid, ...) 
+0

+1 per il metodo di estensione! Spero di refactoring per utilizzare LINQ all'interno pure. –

+0

Sì, ho sempre voluto farlo ma ho dimenticato :) – Aliostad

+0

Sì, ottima idea. Potrei usare anche qualcosa del genere, molto carino –

0

Si potrebbe mettere questi in una collezione, e l'uso:

OrderStatus searchStatus = new[] { 
       OrderStatus.Valid, 
       OrderStatus.Active, 
       OrderStatus.Processed, 
       OrderStatus.Completed }; 

var results = Order.Status.Where(status => searchStatus.Contains(status)); 
1

Se questo insieme di stati ha un significato, ad esempio quelli sono stati per gli ordini accettati, è possibile definire un metho di estensione d sul tuo enum e usalo nella tua query linq.

public static class OrderStatusExtensions 
{ 
    public static bool IsAccepted(this OrderStatuses status) 
    { 
     return status == OrderStatuses.Valid 
      || status == OrderStatuses.Active 
      || status == OrderStatuses.Processed 
      || status == OrderStatuses.Completed; 
    } 
} 

var acceptedOrders = from o in orders 
        where o.Status.IsAccepted() 
        select o; 

Anche se non si poteva dare il metodo un nome semplice, si potrebbe ancora usare qualcosa come IsValidThroughCompleted. In entrambi i casi, sembra trasmettere un significato un po 'più in questo modo.