2011-12-27 58 views
5

我有以下兩種方法從我的數據庫中獲取數據,並返回一個填充的SelectList對象(包括一個「All」選項值),然後傳遞到我的視圖。問題是它們幾乎與它們都訪問不同的存儲庫對象並且它們具有不同的ID名稱(StatusId和TeamId)的例外幾乎相同。我認爲有機會將它們重構爲一個單一的方法,接受存儲庫作爲參數,並以某種方式找出ID名稱應該是什麼,可能通過使用反射或某種lambda表達式,但我不知道如何來完成這一點。重構生成SelectList到單個方法的兩種方法

private SelectList GetStatusSelectList(int selectedStatusId) 
{ 
    List<MemberStatus> statusList = _memberStatusRepository.All().ToList(); 
    statusList.Insert(0, new MemberStatus {StatusId = 0, Name = "All"}); 
    var statusSelectList = new SelectList(statusList, "StatusId", "Name", selectedStatusId); 
    return statusSelectList; 
} 

private SelectList GetTeamSelectList(int selectedTeamId) 
{ 
    List<MemberTeam> teamList = _memberTeamRepository.All().ToList(); 
    teamList.Insert(0, new MemberTeam { TeamId = 0, Name = "All" }); 
    var teamSelectList = new SelectList(teamList, "TeamId", "Name", selectedTeamId); 
    return teamSelectList; 
} 

任何人都可以幫助弄清楚如何將這些重構成單一的方法嗎?

+1

您是否可以編輯這些類,即添加接口? – 2011-12-27 20:43:00

+0

是的...... _memberTeamRepostiory和_memberStatusRepository實現一個IRepository接口。該接口具有與數據庫交互的所有方法,包括像IQueryable All()這樣的方法,該方法只返回給定TEntity的_dbSet。 – bigmac 2011-12-27 20:47:39

+0

MemberTeam和MemberStatus怎麼樣 - 你可以直接修改它們還是使用部分類來修改它們? – foson 2011-12-27 20:50:19

回答

2

你可以嘗試以下方法:

private SelectList GetStatusSelectList(int selectedStatusId) 
{ 
    return GetGenericSelectList<MemberStatus>(selectedStatusId, _memberStatusRepository.All().ToList(), "StatusId"); 
} 

private SelectList GetTeamSelectList(int selectedTeamId) 
{ 
    return GetGenericSelectList<MemberTeam>(selectedTeamId, _memberTeamRepository.All().ToList(), "TeamId"); 
} 

private SelectList GetGenericSelectList<T>(int selectedTeamId, List<T> list, string idFieldName) where T : new() 
{ 
    var firstItem = new T(); 
    (firstItem as dynamic).Name = "All"; 
    var l = new List<T>(list); 
    l.Insert(0, firstItem); 
    return new SelectList(l, idFieldName, "Name", selectedTeamId); 
} 

該解決方案是不理想的,並且依賴於一些約定(如所有項目應該有Name屬性)。不過,這似乎是一個不錯的開始。通過使用表達式而不是屬性名稱可以進一步改進 - 這將允許通過編譯時檢查來更改屬性名稱。

+0

感謝您的代碼。這似乎工作,但我有兩個問題給你。首先,我刪除了第三行和第四行代碼,並簡單地使用'list.Insert(0,firstItem)'代替。這有什麼問題嗎?其次,我不知道方法簽名中'where T:new()'的含義。你能告訴我這是幹什麼嗎? – bigmac 2011-12-27 21:59:22

+0

我從參數中創建一個新列表,因爲否則現有列表將被修改(您傳遞一個現有列表並且函數向其中插入一個新元素)。這可能不是一個問題,但如果在這種方法旁邊的其他地方使用了原始列表呢?關於新的約束 - 它只是允許做'新的T()'。有關更多詳細信息,請參閱http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx。 – 2011-12-27 22:05:24

+1

這是我最迫切需要的最乾淨的解決方案,所以謝謝the_joric! – bigmac 2011-12-27 22:25:29

3

好了,這是最常用的,我可以想出,但它會要求你MemberStatusMemberTeam實施IIdentifiable,我不知道是否可以應用到你的情況。如果是這樣,這將是要走的路。

private SelectList GetList<T>(IRepository repository, int id, string name) 
    where T : IIdentifiable, new() 
{ 
    List<IIdentifiable> list = repository.All().ToList(); 
    list.Insert(0, new T() { Name = name, Id = id }); 
    var statusSelectList = new SelectList(list, "Id", "Name", id); 
} 

和接口代碼

interface IIdentifiable 
{ 
    int Id { get; set; } 
    string Name { get; set; } 
} 
+0

謝謝托米斯拉夫。但是,我擔心MemberTeam和MemberStatus的POCO對象非常簡單,它們都有一個Name屬性,但每個對象都有一個唯一的Id屬性名稱(StatusId和TeamId)。有沒有辦法讓這些屬性保持原樣並仍然實現你描述的接口?我傾向於讓名稱更具描述性,但如果推薦的話,可以說服他們使用一個更普通的ID字段。 – bigmac 2011-12-27 21:14:30

+0

當然,只需將'IIdentifiable.Id'屬性映射到您的類中的'StatusId'和'TeanId'。 'int id {get {return StatusId; } set {StautsId = value; }}'在你的'MemberStatus'類中。這不工作嗎? – 2011-12-27 21:24:54

+1

@Tomislav ...謝謝你。正如我向foson提到的那樣,我發佈了類似的東西,我將使用你的方法對我的領域模型進行更強大的重構,但是the_joric的回答對於我發佈的問題來說是最簡潔的,所以我要接受他的文章。但是,再次感謝您的意見,並且當我繼續構建此項目時,您向我展示了一些新的方向! – bigmac 2011-12-27 22:30:43

1

從我所看到的,在重構到一個單一的方法這種方式的主要障礙是new MemberStatusnew MemberTeam電話,除了確定正確儲存庫使用。

要想出一個優雅的解決方案,您需要更多的基礎架構配置 - 基本上,您需要根據類型解析正確的存儲庫,並且您希望有某種工廠構建對象實例。

下面將重構代碼到一個單一的方法,而不是(在我看來)比任何單獨的方法更好,你已經有了:

private SelectList GetSelectList<T>(int selectedId, Func<List<T>> repoAllFunc, Func<T> typeNewFunc, string idName) 
{ 
    List<T> list = repoAllFunc(); 
    list.Insert(0, typeNewFunc()); 
    var selectList = new SelectList(list, idName, "Name", selectedId); 
    return selectList; 
} 

然後,您可以這樣稱呼它:

var memberStatusSelectList = 
    GetSelectList<MemberStatus>(
     id, 
     () => _memberStatusRepository.All().ToList(), 
     () => new MemberStatus {StatusId = 0, Name = "All"}); 
+0

Ethan,我也在嘗試你的代碼,但是很難搞清楚如何調用你的方法。我認爲它需要一個lambda表達式,但是因爲我是新手,你能給我一個關於我爲Func <>參數傳入的指針嗎? – bigmac 2011-12-27 22:02:57

+0

@bmccleary我加了一個如何使用的例子。這個例子中的funcs沒有參數,所以語法和委託語法一樣簡單。否則,他們會有點醜陋。 – 2011-12-27 22:14:19

+0

感謝您提供示例代碼和解釋。我現在接受the_joric的答案,因爲它對於我的直接需求來說是一個更清潔的方法,但我一直在試圖弄清楚如何將代表傳遞給方法一段時間,並且您的示例爲我提供了一些指導,我的代碼的其他領域,非常感謝你! – bigmac 2011-12-27 22:24:43

0

如果IRepository增加了一些「功能」,你會得到一些更乾淨的代碼。

而不是All()有一個SingleRecordsWithAllRecord()方法處理前兩行。然後讓存儲庫定義自己的DataValueFieldDataTextField

private SelectList GetSelectList(IRepository repo, int selectedId) 
{ 
    var selectListAll = repo.SingleRecordsWithAllRecord().ToList(); 

    return new SelectList(selectListAll, 
         repo.DataValueField, 
         repo.DataTextField, 
         selectedId); 
} 
+0

奧斯汀,我喜歡你的思維模式,我想我會在繼續使用代碼的時候嘗試一下,但是現在,the_joric的答案是最直接的。感謝您的輸入! – bigmac 2011-12-27 22:26:23

1

你可以去一點點瘋狂的接口並執行以下操作:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ConsoleApplication3 
{ 

    public class MemberStatus : IDefault<MemberStatus> 
    { 
     public int StatusId { get; set; } 
     public string Name { get; set; } 

     public MemberStatus Default 
     { 
      get { return new MemberStatus() { StatusId = 0, Name = "All" }; } 
     } 

     public string IdName 
     { 
      get { return "StatusId"; } 
     } 
    } 

    public class MemberTeam : IDefault<MemberTeam> 
    { 
     public int TeamId { get; set; } 
     public string Name { get; set; } 

     public MemberTeam Default 
     { 
      get { return new MemberTeam() { TeamId = 0, Name = "All" }; } 
     } 

     public string IdName 
     { 
      get { return "TeamId"; } 
     } 
    } 

    public interface IDefault<T> 
    { 
     T Default { get; } 
     string IdName { get; } 
    } 

    public interface IRepository<T> 
    { 
     IEnumerable<T> All(); 
    } 

    public class MemberStatusRepository : IRepository<MemberStatus> 
    { 
     public IEnumerable<MemberStatus> All() 
     { 
      return new[] { 
       new MemberStatus(), 
       new MemberStatus() 
      }; 
     } 
    } 
    public class MemberTeamRepository : IRepository<MemberTeam> 
    { 
     public IEnumerable<MemberTeam> All() 
     { 
      return new[] { 
       new MemberTeam(), 
       new MemberTeam() 
      }; 
     } 
    } 

    public class DataAccessLayer 
    { 
     IRepository<MemberStatus> _memberStatusRepository; 
     IRepository<MemberTeam> _memberTeamRepository; 
     public DataAccessLayer() 
     { 
      _memberStatusRepository = new MemberStatusRepository(); 
      _memberTeamRepository = new MemberTeamRepository(); 
     } 


     public SelectList<TResult> GetTeamSelectList<TRepository, TResult>(TRepository repo, int selectedTeamId) 
      where TRepository : IRepository<TResult> 
      where TResult : IDefault<TResult>, new() 
     { 
      List<TResult> teamList = repo.All().ToList(); 
      var dummyobj = new TResult(); 
      teamList.Insert(0, dummyobj.Default); 
      var teamSelectList = new SelectList<TResult>(teamList, dummyobj.IdName, "Name", selectedTeamId); 
      return teamSelectList; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var dal = new DataAccessLayer(); 
      SelectList<MemberStatus> results = dal.GetTeamSelectList<IRepository<MemberStatus>, MemberStatus>(new MemberStatusRepository(), 5); 
      Console.WriteLine(); 
      Console.Read(); 
     } 
    } 

    public class SelectList<TResult> 
    { 
     public SelectList(List<TResult> teamList, string p, string p_2, int selectedTeamId) 
     { 

     } 
    } 

} 

這將是很好,如果你可以定義一個接口的靜態性質,但因爲你我不能靠創建一個虛擬對象代替。

+0

@foson ... WOW!感謝您的所有代碼。我可以看到你在哪裏,我想我可能會對我的領域類進行一些重構來採用你的方法,但是現在,the_joric的答案是解決我眼前需求的最基本的方法,所以我接受它。但是,我仍然將代碼作爲參考,以便在不久的將來可以進行更重的重構,所以非常感謝您的時間和細節! – bigmac 2011-12-27 22:28:29

+0

NP。就像我說的,我的解決方案是一個瘋狂的界面 - 絕對比使用動態更復雜/更不可讀。如果我使用動態或反思,我個人會確保做一些性能測試,以確保解決方案在我可接受的性能預期範圍內。 – foson 2011-12-28 15:00:40