2013-01-08 6 views
7

I często trzeba ograniczyć wybiera polami jak publishStart, publishEnd, activemetodę rozszerzenia LINQ

mam tych pól w różnych tabelach. Więc tylko wiersze powinny być zaznaczone, że są

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

tak, na przykład:

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)); 

To jest trochę długi, więc zastanawiam się, czy jest możliwe, aby stworzyć - metody (rozszerzenie?) jak:

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

gdzie isActive() zapewnia 3 linie powyższym fragmencie.

Jak mogę to zrobić? Czy istnieje lepszy sposób na wyczyszczenie kodu?

+0

Wszystkie są w osobnych tabelach, prawda? Nie jedna tabela, która jest odsłonięta przez kontekst? Pytam, ponieważ w preambule wskazano, że właściwości znajdują się na oddzielnych tabelach, podczas gdy w filtrze używa się tylko jednego kontekstu. Jeśli wszystkie są na tym samym stole, łatwo, jeśli są na różnych stołach, bardzo dobrze zmieniają właściwą odpowiedź. – casperOne

+0

W takim przypadku musisz wywołać 'IsActive' ** przed **' SingleOrDefault', aby mógł filtrować aktywne elementy przed zrobieniem tylko jednego, chyba że jest tylko jeden element z tym ID. – Servy

Odpowiedz

13

Aby zdefiniować rozszerzenie, potrzebujesz klasy statycznej. Możesz umieścić to w dowolnej przestrzeni nazw, którą chcesz, ale pamiętaj, aby włączyć ją do swoich zastosowań.

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); 
    } 
} 

Zawiadomienie YourEntityType tam. Jest to używane w celu zapewnienia, że ​​metoda jest świadoma istnienia publishStart, publishEnd i . Powinna to być klasa, która implementuje te pola lub kontrakt (interfejs), który je definiuje.

Będziesz wtedy nazwać tak:

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

Więcej na temat metod rozszerzających tutaj: http://msdn.microsoft.com/en-us/library/bb383977.aspx


Ponieważ istnieje wiele komentarze pojawiały się w każdym miejscu, idę dodać tutaj krótkie wyjaśnienie rozwiązania interfejsu ...

Nie jest jasne, czy istnieje wspólna implementacja dla trzech filtrowanych f tony lub interfejs do ich definiowania. Jeśli nie, aby powyższe zadziałało, nie będzie również:

  1. Klasa podstawowa implementująca te pola. W tym scenariuszu można zastąpić YourEntityType przez YourBaseEntityType.
  2. Interfejs do definiowania pól. W tym scenariuszu konieczne jest, aby klasy zaimplementowały te pola. Jeśli klasy są generowane automatycznie (np. Model struktury encji/db pierwszy), możesz implementować klasy częściowe, wprowadzając interfejs. W takim przypadku należy zastąpić YourEntityType przez IYourContract.
+0

Czy istnieje powód, aby używać 'IQueryable ' zamiast 'IElumerable '? – Default

+0

To nie zadziała w wywołaniu 'SingleOrDefault', o co prosi OP. – casperOne

+1

@Default. Wolę używać 'IQueryable ', ponieważ miałem problemy z polimorfizmem w przeszłości z 'IEnumerable '. Biorąc pod uwagę, że istnieją rozszerzenia dla "IEnumerable " i 'IQueryable ', zwracanie 'IEnumerable ' może spowodować, że złe rozszerzenia będą wywoływane dalej w łańcuchu, powodując wyliczanie zamiast odroczonych zapytań. –

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

Wystarczy zdefiniować interfejs jak ten

public interface IHaveAActivityPeriod 
{ 
    Boolean active { get; } 

    DateTime? publishStart { get; } 

    DateTime? publishEnd { get; } 
} 

i dodać go do wszystkich odpowiednich klas.

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

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

Teraz można użyć tej metody rozszerzenie

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)); 
    } 
} 

na każdej instancji wykonawczego IHaveAActivityPeriod.

Całkowicie przegapiłem możliwość skonstruowania metody rozszerzenia, która wykonuje filtrowanie sekwencji zamiast patrzenia na pojedynczy obiekt na raz. Po prostu weź metodę rozszerzenia z odpowiedzi flem jako rzut w interfejsie jako ograniczenie typu.

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

To nie przekształci się w sql, a zatem wymagałoby pełnej oceny do filtrowania. –

+0

To prawda, ale nie ma naprawdę dobrego rozwiązania, które można przetłumaczyć na SQL (bez niestandardowego dostawcy), dlatego zdecydowaliśmy się przedstawić rozwiązanie LINQ to Objects. –