2009-09-12 65 views
1

我在一個應用程序中有幾個區域正在構建,看起來我可能不得不違反DRY(不要重複自己)原則中的生活日光。我真的很喜歡保持乾爽,不會感到困惑,並想知道是否有人能夠給我一個雨披。爲了背景,我使用C#/ .NET 3.51 SP1,Sql Server 2008和Linq-to-Sql。C#泛型問題

基本上,我的情況圍繞以下情況。我需要能夠從數據庫中幾乎任何表格中檢索過濾項目列表,或者我需要能夠從數據庫中的任何表中檢索單個項目,並給定主鍵的ID。我非常肯定,解決這些問題的最佳解決方案將涉及到大量的仿製藥和/或反思。

以下是兩個挑戰的更深入一點。 (請原諒的詳細程度。)

  1. 給定一個表名(或者一個多元化的表名),我想能夠檢索表中的元素的過濾列表。具體而言,該功能將與查找表一起使用。 (在這個數據庫中有大約50個查找表,經常會添加和/或刪除額外的表)。當前的查找表都實現了一個名爲IReferenceData的接口(我的),並且具有ID(PK),Title,Description和活躍。

對於這些查找表中的每一個,我都需要返回所有記錄的列表。其他時候,我只需要返回活動記錄。任何Linq-to-Sql數據上下文都會自動爲每個TableName包含一個List屬性。不幸的是,我不相信我可以使用它的原始形式,因爲它是未經過濾的,我需要在IsActive屬性上應用過濾器。

一個選項是爲所有50個表編寫類似於以下代碼的代碼。育!

public List<AAA> GetListAAA(bool activeOnly) 
{ 
    return AAAs.Where(b => b.IsActive == true || b.IsActive == activeOnly).OrderBy(c => c.Title).ToList(); 
} 

這不會太難,但它確實增加了維護的負擔。

注意:當返回列表時,我維護基礎數據類型非常重要。這些查找表中的記錄可能會被修改,我必須適當地應用更新。

  1. 對於我的150個表中的每一個,我需要能夠通過主鍵ID檢索單個記錄(FirstOrDefault或SingleOrDefault)。再次,我寧願不要多次寫這個相同的代碼。我寧願有一種方法可以用於我的所有表格。

我不確定最好的方法會在這裏。我想到的一些可能性包括以下幾點。 (我沒有關於它們實現的具體想法,我只是將它們列爲思考的食物)。

A.在數據上下文中有一個像GetTableNameItemByID(Guid id)這樣的方法。 (好) B.在數據上下文中有一個像GetItem這樣的擴展方法(this,string tableName,Guid id)。 (更好) C.有一個像GetItem(this,Table,Guid id)的泛型方法或擴展方法。 (我甚至不知道這是否可能,但是這將是最乾淨的使用。)(最佳)

其他注意事項

由於各種各樣的原因,我已經創建了一個局部類我的數據上下文。如果方法作爲常規方法包含在該部分類中,或者在擴展方法的單獨靜態類中包含,那肯定是可以接受的。

+0

你有沒有考慮使用不同的ORM? LinqToSql在其對真實生活場景的支持方面很不錯(至少部分表現爲圍繞它創建的工具的數量,使其更加豐富)。你可以隨時推出自己的產品,有時你必須去做,但這似乎並不是這些情況之一。 – 2009-09-12 11:20:50

回答

6

既然你已經有部分實現數據的上下文,您可以添加:

public IQueryable<T> GetList<T>(bool activeOnly) where T : class, IReferenceData 
{ 
    return this.GetTable<T>() 
       .Where(b => !activeOnly || b.isActive) 
       .OrderBy(c => c.Title); 
} 

保留的的IQueryable的字符數據將推遲查詢的執行,直到您準備好實現它。請注意,您可能希望省略默認排序,或者有單獨的方法,有和沒有命令允許您根據需要應用不同的排序。如果將它作爲IQueryable保留,這可能更有價值,因爲如果您願意,可以將它與分頁結合使用以減少實際返回的數據量(每個查詢)。

+0

是的,我相信這非常接近。但是,GetTable返回一個ITable,我不知道如何將其轉換爲IQueryable。如果我們能做到這一點,它肯定能解決一個挑戰。 – 2009-09-12 03:49:01

+0

如果你使用'GetTable '(添加'T:class'),那麼它應該更乾淨地工作。 – 2009-09-12 08:40:06

+0

@Marc - 我認爲你是對的,我已經更新。我實際上並沒有試圖編譯它。 – tvanfosson 2009-09-12 12:59:50

2

你有沒有考慮使用一個代碼生成工具?看看CodeSmith。使用這樣的工具或T4將允許您自動生成過濾器功能,並且應該使它們相當容易維護。

我不知道提供T4最好的鏈接,但你可以用this video開始。

+0

謝謝丹尼斯!我過去曾經使用過CodeSmith,現在有些使用T4。這些都是CodeGen的絕佳解決方案,並且可以正常工作。我只是想盡量減少我的整體代碼。感謝提示。 – 2009-09-12 03:50:10

2

這會滿足您的需求?

public static IEnumerable<T> GetList<T>(this IEnumerable<IReferenceData> items, bool activeOnly) 
{ 
    return items.Where(b => b.IsActive == true || b.IsActive == activeOnly).OrderBy(c => c.Title).Cast<T>().ToList(); 
} 

你可以使用這樣的:

IEnumerable<IReferenceData> yourList; 
List<DerivedClass> filtered = yourList.GetList<DerivedClass>(true); 
+0

謝謝約翰。讓我看看我是否明白。你建議我創建一個擴展方法並通過未過濾的列表。然後允許擴展方法執行過濾和排序。這聽起來像是列表問題的一個很好的解決方案。讓我試試看,回到你身邊。 – 2009-09-12 04:14:10

+0

您不會傳入未過濾的列表,因爲這是擴展方法中的「this」參數。 – 2009-09-12 23:24:14

+0

謝謝約翰!這很有幫助! – 2009-09-24 01:14:34

3

有一種叫做「Generic Repository」的設計模式。使用這種模式,你將得到一個IQueryable,而不是你的實體的真實列表,它可以讓你在你的查詢時做一些其他的事情。就是讓業務層以任何通用的方式滿足需求。

你可以找到一個例子here

+1

我看過這個人的帖子,看了他的代碼。我只能說「哇!」。這太棒了!它並沒有讓我得到我想要完成的所有事情,但它肯定有很大幫助。它也回答了我一直在琢磨但沒有問過的其他一些設計問題。非常感謝你的這個鏈接。我已經下載了他的代碼,稍微修改了一下,並將其添加到我的vs項目中。 – 2009-09-12 05:11:11

1

爲了做這樣的事情,而不要求接口等,你可以使用動態Expression秒;是這樣的:

public static IList<T> GetList<T>(
    this DataContext context, bool activeOnly) 
    where T : class 
{ 
    IQueryable<T> query = context.GetTable<T>(); 
    var param = Expression.Parameter(typeof(T), "row"); 
    if(activeOnly) 
    { 
     var predicate = Expression.Lambda<Func<T, bool>>(
      Expression.Equal(
       Expression.PropertyOrField(param, "IsActive"), 
       Expression.Constant(true,typeof(bool)) 
      ), param); 
     query = query.Where(predicate); 
    } 
    var selector = Expression.Lambda<Func<T, string>>(
     Expression.PropertyOrField(param, "Title"), param); 
    return query.OrderBy(selector).ToList(); 
} 
+0

非常感謝Mark! – 2009-09-24 01:15:13