2013-01-08 131 views
7

我經常需要用領域,如publishStartpublishEnd限制進行選擇,activeLINQ的擴展方法

我有幾個不同的表這些字段。因此,只有行應該被選中,是

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

因此,舉例來說:

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

這是一個有點冗長,所以我不知道是否有可能創建一個 - 方法(擴展名?)像:

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

其中isActive()提供了3行以上的片段。

我該怎麼做? 有沒有更好的辦法來清理代碼?

+0

這些都在單獨的表中,對嗎?上下文中沒有顯示單個表?我問,因爲你在序言中指出,屬性在單獨的表格上,而在過濾器中只使用一個上下文。如果他們都在同一張桌子上,這很容易,如果他們在不同的桌子上,它會大大改變正確的答案。 – casperOne

+0

在這種情況下,您需要在**'SingleOrDefault'之前調用'IsActive' **,以便它可以在只取一個之前過濾活動項目,除非只有一個項目具有該ID。 – Servy

回答

13

要定義一個擴展,你需要一個靜態類。你可以把它放在你喜歡的任何命名空間中,只要記住將它包含在你的使用中。

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

通知YourEntityType在那裏。這是用來確保方法是知道的publishStartpublishEndactive存在。這應該是實現這些字段的類或定義它們的合同(接口)。

你可以這樣調用這個像這樣:

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

更多擴展方法在這裏:http://msdn.microsoft.com/en-us/library/bb383977.aspx


由於有大量的評論彈出所有的地方,我要去這裏要補充的接口解決方案的簡要說明...

它在這個問題目前還不清楚是否有是三個過濾F A共同實施還是一個界面來定義它們。如果不是,爲了上面的工作,你不會:

  1. 一個基類實現這些領域。在這種情況下,你會與YourBaseEntityType取代YourEntityType
  2. 的接口來定義的字段。在這種情況下,您需要讓您的類實現這些字段。如果這些類是自動生成的(例如,首先是實體框架模型/ db),那麼您可以實現部分類,讓它們實現接口。在這種情況下,你會與IYourContract取代YourEntityType
+0

是否有理由使用'IQueryable '而不是'IEnumerable '? – Default

+0

這對於OP要求的'SingleOrDefault'調用不起作用。 – casperOne

+1

@默認。我更喜歡使用'IQueryable ',因爲我在'IEnumerable '過去曾出現多態性問題。鑑於「IEnumerable 」和「IQueryable 」都存在擴展,因此返回「IEnumerable 」可導致在鏈中進一步調用錯誤的擴展,導致枚舉而不是延遲查詢。 –

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

只要定義一個接口這樣

public interface IHaveAActivityPeriod 
{ 
    Boolean active { get; } 

    DateTime? publishStart { get; } 

    DateTime? publishEnd { get; } 
} 

,並把它添加到所有相關的課程。

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

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

現在你可以實現IHaveAActivityPeriod每個實例使用這個擴展方法

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

var foo = new Foo(); 

var isFooActive = foo.IsActive(); 

var bar = new Bar(); 

var isBarActive = bar.IsActive(); 

我完全錯過來構建執行序列,而不是在一個單一的實體,在一次尋找的濾波擴展方法的可能性。只需從flem的回答中獲取擴展方法,然後在類型約束中拋出接口。

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

這不會轉換爲sql,因此需要對過濾進行全面評估。 –

+0

確實如此,但是沒有真正好的解決方案可以轉換成SQL(沒有自定義提供程序),因此決定提供一個LINQ to Objects解決方案。 –