2013-01-08 22 views
7

ho spesso bisogno di limitare SELECT da campi come publishStart, publishEnd, activemetodo di estensione LINQ

Ho questi campi in diversi tavoli diversi. Quindi solo le righe devono essere selezionati, che sono

a: active == true; 
b: publishStart < now; 
c: publishEnd > now; 

Così, ad esempio:

db.myTable.SingleOrDefault(a => (a.ID == _theID 
      //now the active and start-end part:    
         && ((a.publishEnd > DateTime.Now) || (a.publishEnd == null)) 
         && ((a.publishStart <= DateTime.Now) || (a.publishStart == null)) 
         && a.active == true)); 

Questo è un po 'lungo, quindi mi chiedo se è possibile creare un - metodo (estensione?) come:

db.myTable.SingleOrDefault(a => (a.ID == _theID).isActive() 

dove il isActive() fornisce le 3 linee di frammento di sopra.

Come posso fare questo? C'è un modo migliore per ripulire il codice?

+0

Questi sono tutti in tabelle separate, giusto? Non una singola tabella esposta dal contesto? Chiedo perché nel preambolo si indica che le proprietà si trovano su tabelle separate, mentre si utilizza solo un contesto nel filtro. Se sono tutti sullo stesso tavolo, è facile, se sono su tavoli diversi, altera tremendamente la risposta giusta. – casperOne

+0

In tal caso, devi chiamare 'IsActive' ** prima di **' SingleOrDefault' in modo che possa filtrare gli elementi attivi prima di prenderne uno solo, a meno che non ci sia un solo elemento con quell'ID. – Servy

risposta

13

Per definire un'estensione è necessaria una classe statica. Puoi metterlo in qualsiasi spazio dei nomi che ti piace, ricordati di includerlo nei tuoi usi.

public static class Extensions 
{ 
    public static IQueryable<T> Active<T>(this IQueryable<T> source) 
     where T : YourEntityType 
    { 
     return source.Where(a => ((a.publishEnd > DateTime.Now) || (a.publishEnd == null)) 
          && ((a.publishStart <= DateTime.Now) || (a.publishStart == null)) 
          && a.active == true); 
    } 
} 

Avviso YourEntityType lì. Viene utilizzato per garantire che il metodo sia a conoscenza dell'esistenza di publishStart, publishEnd e active. Questa dovrebbe essere la classe che implementa questi campi o un contratto (interfaccia) che li definisce.

Si potrebbe quindi chiamare questo modo:

var item = db.myTable.Active().SingleOrDefault(...); 

Altro su metodi di estensione qui: http://msdn.microsoft.com/en-us/library/bb383977.aspx


Come ci sono un sacco di commenti che dilagano in tutto il posto, sto andando aggiungere qui una breve spiegazione della soluzione di interfaccia ...

Non è chiaro nella domanda se esista o meno un'implementazione comune per i tre filtri f ield o un'interfaccia per definirli. In caso contrario, per il funzionamento di cui sopra, non sarà né:

  1. Una classe base che implementa questi campi. In questo scenario sostituirai YourEntityType con YourBaseEntityType.
  2. Un'interfaccia per definire i campi. In questo scenario è necessario che le classi implementino i campi. Se le classi sono generate automaticamente (ad es. Modello di struttura entità/db prima), allora è possibile implementare classi parziali, avendo loro implementare l'interfaccia. In questo caso sostituirai YourEntityType con IYourContract.
+0

C'è un motivo per usare 'IQueryable ' invece di 'IEnumerable '? – Default

+0

Questo non funzionerà in una chiamata a 'SingleOrDefault', che è ciò che l'OP chiede. – casperOne

+1

@Default. Preferisco usare 'IQueryable ' come ho avuto problemi di polimorfismo in passato con 'IEnumerable '. Dato che esistono estensioni sia per 'IEnumerable ' che 'IQueryable ', restituire un 'IEnumerable ' può comportare che le estensioni errate vengano chiamate più avanti nella catena risultante in enumerazione anziché in query posticipate. –

3
public static class Extensions 
{ 
    public static IEnumerable<MyClass> isActive(this IEnumerable<MyClass> list) 
    { 
     return list.Where(a => 
       ((a.publishEnd > DateTime.Now) || (a.publishEnd == null)) 
       && ((a.publishStart <= DateTime.Now) || (a.publishStart == null)) 
       && a.active == true); 
    } 
} 
4

basta definire un'interfaccia come questo

public interface IHaveAActivityPeriod 
{ 
    Boolean active { get; } 

    DateTime? publishStart { get; } 

    DateTime? publishEnd { get; } 
} 

e aggiungerlo a tutte le classi interessate.

public class Foo : IHaveAActivityPeriod { [...] } 

public class Bar : IHaveAActivityPeriod { [...] } 

Ora è possibile utilizzare questo metodo di estensione

public static class Extensions 
{ 
    public static Boolean IsActive(this IHaveAActivityPeriod item) 
    { 
     var now = DateTime.Now; 

     return item.active && 
       (item.publishStart <= now) 
       (!item.publishEnd.HasValue || (item.publishEnd > now)); 
    } 
} 

in ogni istanza attuazione IHaveAActivityPeriod.

var foo = new Foo(); 

var isFooActive = foo.IsActive(); 

var bar = new Bar(); 

var isBarActive = bar.IsActive(); 

ho completamente perso la possibilità di costruire un metodo di estensione che esegue il filtraggio di una sequenza invece di guardare una singola entità contemporaneamente. Prendi il metodo di estensione dalla risposta di flem e usa l'interfaccia come tipo di vincolo.

public static class Extensions 
{ 
    public IQueryable<T> IsActive<T>(this IQueryable<T> sequence) 
     where T : IHaveAActivityPeriod 
    { 
     return source.Where(item => 
        item.active && 
        (item.publishStart <= now) && 
        (!item.publishEnd.HasValue || (item.publishEnd > now)); 

    } 
} 
+0

Questo non verrà trasformato in sql e pertanto richiederebbe una valutazione completa per il filtro. –

+0

Questo è vero, ma non esiste una soluzione veramente buona che sia traducibile in SQL (senza un fornitore personalizzato) e quindi ha deciso di presentare una soluzione LINQ to Objects. –

Problemi correlati