2011-12-27 13 views
5

Mam dwie następujące metody, które pobierają dane z mojej bazy danych i zwracają wypełniony obiekt SelectList (w tym wartość opcji "Wszystkie"), które następnie przekazuję do mojego widoku. Problem polega na tym, że są prawie identyczne, z wyjątkiem tego, że oba mają dostęp do różnych obiektów repozytorium i mają różne nazwy identyfikatorów (StatusId i TeamId). Wydaje mi się, że istnieje sposobność, aby je przekształcić w jedną metodę, która akceptuje repozytorium jako parametr i w jakiś sposób określa, jaka powinna być nazwa identyfikatora, być może za pomocą refleksji lub pewnego rodzaju wyrażenia lambda, ale nie wiem jak aby to osiągnąć.Refactor dwie metody, które generują SelectList w jedną metodę

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

Czy ktoś może pomóc w ustaleniu, w jaki sposób można je przekształcić w jedną metodę?

+1

Czy możesz edytować te klasy, np. Dodawać interfejs? –

+0

Tak ... zarówno _memberTeamRepostiory, jak i _memberStatusRepository implementują interfejs IRepository. Ten interfejs ma wszystkie metody interakcji z bazą danych, w tym metody takie jak IQueryable All(), która po prostu zwraca _dbSet danej TEntity. – bigmac

+0

jak o MemberTeam i MemberStatus - czy możesz modyfikować je bezpośrednio lub przy pomocy klas częściowych? – foson

Odpowiedz

2

można spróbować wykonać następujące czynności:

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

To rozwiązanie nie jest idealne i opiera się na pewnych konwencji (na przykład wszystkie elementy powinny mieć Name właściwość). Jednak wydaje się, że nie jest to zły sposób na początek. Można go jeszcze ulepszyć, używając wyrażeń zamiast nazw właściwości - co pozwoliłoby na zmianę nazw właściwości podczas sprawdzania czasu kompilacji.

+0

Dzięki za kod. To wydaje się działać, ale mam dla ciebie dwa pytania. Najpierw usunąłem 3. i 4. wiersz kodu i po prostu użyłem "list.Insert (0, firstItem)" zamiast tego. Czy są z tym jakieś problemy? Po drugie, nie wiem, co oznacza "where T: new()" w metodzie podpisu. Czy możesz dać mi znać, co to robi? – bigmac

+0

Tworzę nową listę z argumentu, ponieważ inaczej istniejąca lista zostanie zmodyfikowana (przekazujesz istniejącą listę i funkcja wstawia do niej nowy element). To nie może być problem, ale co, jeśli ta oryginalna lista jest używana gdzie indziej poza tą metodą? Odnośnie nowego ograniczenia - po prostu pozwala to zrobić 'new T()'. Więcej informacji można znaleźć na stronie http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx. –

+1

To jest najczystsze rozwiązanie moich bezpośrednich potrzeb, więc dziękuję the_joric! – bigmac

3

Cóż, jest to najbardziej ogólna może wymyślić, ale będzie to wymagało, że MemberStatus i MemberTeam wdrożyć IIdentifiable, które nie wiem, czy można zastosować do sprawy. Jeśli tak, to byłaby to droga.

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

I kod interfejsu

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

Dzięki Tomislav. Jedna sprawa jednak, moje obiekty POCO dla MemberTeam i MemberStatus są bardzo proste i oba mają właściwość Name, ale każda ma unikalną nazwę dla właściwości Id (StatusId i TeamId). Czy istnieje sposób, aby zachować te właściwości o nazwie tak jak jest i nadal implementować interfejs, który opisujesz? Zazwyczaj lubię mieć nazwę bardziej opisową dla projektu DB, ale można go przekonać, aby używał bardziej ogólnych pól Id, jeśli jest to zalecane. – bigmac

+0

Oczywiście, po prostu zamapuj właściwość 'IIdentifiable.Id' na' StatusId' i 'TeanId' w swoich klasach. 'int Id {get {return StatusId; } ustaw {StautsId = wartość; }} 'w twojej klasie' MemberStatus'. Czy to nie działa? –

+1

@Tomisław ... dziękuję za to. Jak wspomniałem do firmy foson, która opublikowała coś podobnego, zamierzam użyć twojej metody do bardziej solidnego refaktoryzacji mojego modelu domeny, ale odpowiedź na to pytanie jest najbardziej zwięzła dla zadanego przeze mnie pytania, więc przyjmuję jego wpis . Ale znowu, dziękuję za wkład i pokazałeś mi kilka nowych wskazówek, aby kontynuować budowanie tego projektu! – bigmac

1

Z tego co widzę, główne przeszkody na drodze refactoring to do jednej metody są new MemberStatus i zaproszeń new MemberTeam, oprócz ustalenia prawa repozytorium do użycia.

Aby wymyślić eleganckie rozwiązanie, potrzebowałbyś trochę więcej skonfigurowanej infrastruktury - w zasadzie musisz rozwiązać właściwe repozytorium na podstawie typu i chciałbyś mieć jakąś fabrykę budującą instancję obiektu.

Następujące będzie byłaby kodu do jednej metody, ale nie jest (moim zdaniem) lepiej niż odrębnych metod masz już:

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

Mogłeś wtedy nazwać tak:

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

Ethan, próbuję również twojego kodu, ale mam trudny czas dowiedzieć się, jak nazwać twoją metodę. Zakładam, że to wymaga wyrażenia lambda, ale skoro jestem dla nich nowy, czy możesz mi podać wskaźnik tego, co przekazuję dla parametrów Func <>? – bigmac

+0

@bmccleary Dodałem przykład jak używać. Funkcje w tym przykładzie nie pobierają parametrów, więc składnia jest tak prosta, jak dla składni delegatów. W przeciwnym razie stają się trochę brzydsi. –

+0

dziękuję za przykładowy kod i wyjaśnienie. Na razie przyjmuję odpowiedź the_joric, ponieważ jest to trochę czystsze dla moich bezpośrednich potrzeb, ale próbowałem wymyślić, jak przekazywać delegatów na metody przez jakiś czas, a twoja próbka dostarcza mi wskazówek, jak to zrobić dla inne obszary mojego kodu, więc dziękuję bardzo! – bigmac

0

Jeśli IRepository doda kilka "funkcji", otrzymasz czystszy kod.

Zamiast All() stosuje się metodę SingleRecordsWithAllRecord(), która obsługuje pierwsze dwa wiersze. Następnie repozytorium określ swoje własne DataValueField i DataTextField.

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

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

Austin, podoba mi się twój wzorzec myślenia i myślę, że spróbuję, jak będę kontynuował kod, ale na razie odpowiedź the_joric była najbardziej bezpośrednia dla moich bezpośrednich potrzeb. Dzięki za wejście! – bigmac

1

Można pójść trochę szalona interfejs i wykonaj następujące czynności:

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

     } 
    } 

} 

Byłoby miło, jeśli można określić właściwości statyczne w interfejsie, ale skoro nie mogę polegać na tworzeniu fikcyjnego obiektu zamiast.

+0

@foson ... WOW! Dziękuję za cały kod. Widzę, dokąd zmierzasz, i myślę, że mógłbym popracować nad wprowadzeniem trochę refaktoryzacji mojej klasy domeny, by przyjąć twoją metodologię, ale na razie odpowiedź the_joric była najbardziej podstawowym sposobem na zaspokojenie moich bezpośrednich potrzeb, więc jestem akceptując to. Ale znowu trzymam twój kod jako punkt odniesienia, dzięki czemu mogę w przyszłości pracować nad bardziej ciężkim refaktoryzowaniem, więc bardzo dziękuję za poświęcony czas i szczegóły! – bigmac

+0

NP. Jak już mówiłem, moje rozwiązanie to trochę szalony interfejs - zdecydowanie bardziej skomplikowany/mniej czytelny niż dynamiczny. Gdybym używał dynamiki lub refleksji, osobiście byłbym pewien, że wykonam test perfekcyjny, aby upewnić się, że rozwiązanie mieści się w moich akceptowalnych oczekiwaniach dotyczących perfekcji. – foson

Powiązane problemy