2011-12-27 8 views
5

Ho i seguenti due metodi che ottengono i dati dal mio DB e restituiscono un oggetto PopLista popolato (incluso un valore di opzione "Tutto") che poi passerò sulla mia vista. Il problema è che sono quasi identici con l'eccezione che entrambi accedono a diversi oggetti del repository e hanno nomi ID diversi (StatusId e TeamId). Penso che ci sia un'opportunità per rifattorizzarli in un singolo metodo che accetta il repository come parametro e in qualche modo capisce quale dovrebbe essere il nome dell'ID, magari usando il reflection o qualche tipo di espressione lambda, ma non so come per realizzare questo.Refactor due metodi che generano una SelectList in un unico metodo

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

Qualcuno può aiutare a capire come refactoring questi in un unico metodo?

+1

Sei in grado di modificare quelle classi, ad esempio aggiungere un'interfaccia? –

+0

Sì ... sia il _memberTeamRepostiory che il _memberStatusRepository implementano un'interfaccia IRepository. Tale interfaccia ha tutti i metodi per interagire con il DB, inclusi metodi come IQueryable All() che restituisce semplicemente il _dbSet del dato TEntity. – bigmac

+0

come su MemberTeam e MemberStatus - puoi modificarli direttamente o con classi parziali? – foson

risposta

2

si può provare il seguente:

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

Questa soluzione non è ideale e si basa su alcune convenzioni (ad esempio, tutti gli oggetti devono avere Name proprietà). Tuttavia sembra essere un modo non male per iniziare. Potrebbe essere ulteriormente migliorato utilizzando le espressioni anziché i nomi di proprietà, il che consentirebbe di modificare i nomi delle proprietà con il controllo del tempo di compilazione.

+0

Grazie per il codice. Questo sembra funzionare, ma ho due domande per te. Per prima cosa ho rimosso la 3a e la 4a riga del codice e ho semplicemente usato "list.Insert (0, firstItem)". C'è qualche problema con questo? In secondo luogo, non so cosa significhi "dove T: new()" nella firma del metodo. Puoi farmi sapere cosa sta facendo? – bigmac

+0

Creo un nuovo argomento dall'argomento, perché altrimenti l'elenco esistente verrà modificato (si passa un elenco esistente e la funzione inserisce un nuovo elemento in esso). Questo potrebbe non essere un problema, ma cosa succede se quell'elenco originale è usato altrove accanto a questo metodo? Riguardo al nuovo vincolo - permette solo di fare 'nuovo T()'. Vedi http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx per maggiori dettagli. –

+1

Questa è la soluzione più pulita per i miei bisogni immediati, quindi grazie a the_joric! – bigmac

3

Bene, questo è il più generico che possa venire, ma richiederà che il tuo MemberStatus e MemberTeam implementino IIdentifiable, che non so se possa applicarsi al tuo caso. Se è così, questa sarebbe la strada da percorrere.

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

E il codice di interfaccia

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

Grazie Tomislav. Una preoccupazione però, i miei oggetti POCO per MemberTeam e MemberStatus sono molto semplici e hanno entrambi una proprietà Name, ma ognuno ha un nome univoco per la proprietà Id (StatusId e TeamId). C'è un modo per mantenere queste proprietà così come sono e implementare ancora l'interfaccia che descrivi? Tendo ad avere il nome più descrittivo per la progettazione del DB, ma potrei essere persuaso ad usare un campo più generico di Id giusto se consigliato. – bigmac

+0

Ovviamente, basta mappare la proprietà 'IIdentifiable.Id' a' StatusId' e 'TeanId' nelle classi. 'int Id {get {return StatusId; } set {StautsId = valore; }} 'nella tua classe' MemberStatus'. Non funzionerebbe? –

+1

@Tomislav ... grazie per questo. Come ho accennato a Foson, che ha pubblicato qualcosa di simile, userò il tuo metodo per un refactoring più solido del mio modello di dominio, ma la risposta di the_joric è la più concisa per la domanda che ho postato, quindi accetterò il suo post . Ma ancora una volta, grazie per l'input e mi hai mostrato alcune nuove indicazioni da seguire mentre continuo a costruire questo progetto! – bigmac

1

Da quello che vedo, i principali ostacoli nel modo di refactoring questo in un unico metodo sono i new MemberStatus ei new MemberTeam chiamate, oltre a determinare il diritto repository da usare.

Per trovare una soluzione elegante, è necessario configurare un po 'più di infrastruttura: in pratica è necessario risolvere il repository corretto in base al tipo e si vorrebbe avere una sorta di factory build up dell'istanza dell'oggetto.

Di seguito vi refactoring del codice in un unico metodo, ma non è (a mio parere) meglio rispetto ai metodi separati avete già:

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

È quindi possibile chiamare in questo modo:

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

Ethan, sto provando anche il tuo codice, ma mi sto divertendo a capire come chiamare il tuo metodo. Presumo che ci voglia un'espressione lambda, ma visto che sono nuovo a questi, puoi darmi un puntatore su cosa passo per i parametri Func <>? – bigmac

+0

@bmccleary Ho aggiunto un esempio di come usare. Le funzioni in questo esempio non richiedono parametri, pertanto la sintassi è semplice quanto la sintassi del delegato. Altrimenti, diventano un po 'più brutti. –

+0

grazie per il codice di esempio e la spiegazione. Per ora accetterò la risposta di the_joric perché è un po 'più pulito per i miei bisogni immediati, ma ho cercato di capire come passare i delegati in metodi per un po', e il tuo campione mi fornisce qualche indicazione su come farlo altre aree del mio codice, quindi grazie mille! – bigmac

0

Se IRepository aggiunge alcune "caratteristiche", otterrete un codice più pulito.

Invece di All() ha un metodo SingleRecordsWithAllRecord() che gestisce le prime due righe. Quindi fare in modo che il repository definisca il proprio DataValueField e DataTextField.

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

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

Austin, mi piace il tuo schema di pensiero qui, e penso che lo proverò mentre continuo con il codice, ma per ora la risposta di the_joric era più diretta ai miei bisogni immediati. Grazie per l'input! – bigmac

1

Si può andare un po 'pazzo e un'interfaccia effettuare le seguenti operazioni:

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

     } 
    } 

} 

Sarebbe bello se si potesse definire le proprietà statiche in un'interfaccia, ma dal momento che non posso contare sulla creazione di un oggetto fittizio anziché.

+0

@foson ... WOW! Grazie per tutto il codice. Posso vedere dove ti stai dirigendo qui, e penso che potrei lavorare su un piccolo refactoring della mia classe di dominio per adottare la tua metodologia, ma per ora la risposta di the_joric era il modo più semplice per rispondere ai miei bisogni immediati, quindi sono accettandolo. Ma ancora, sto mantenendo il tuo codice come riferimento in modo da poter lavorare su un refactoring più pesante nel prossimo futuro, quindi ti ringrazio molto per il tuo tempo e i dettagli! – bigmac

+0

NP. Come ho detto, la mia soluzione è una piccola interfaccia pazzesca - decisamente più complicata/meno leggibile rispetto all'utilizzo dinamico. Se stavo usando la dinamica o la riflessione, personalmente sarei sicuro di fare alcuni test di perf per garantire che la soluzione fosse entro le mie aspettative perfettibili accettabili. – foson

Problemi correlati