7

Sto utilizzando MVC3, Entity Framework v4.3 Code First e SimpleInjector. Ho diversi semplici classi che assomigliano a questo:Come ridurre il numero di dipendenze iniettate sul controller

public class SomeThing 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

ho un altro soggetto che assomiglia a questo:

public class MainClass 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual AThing AThingy { get; set; } 
    public virtual BThing BThingy { get; set; } 
    public virtual CThing CThingy { get; set; } 
    public virtual DThing DThingy { get; set; } 
    public virtual EThing EThingy { get; set; } 
} 

Ogni Thingy (attualmente) ha la propria classe Manager, in questo modo:

public class SomeThingManager 
{ 
    private readonly IMyRepository<SomeThing> MyRepository; 

    public SomeThingManager(IMyRepository<SomeThing> myRepository) 
    { 
     MyRepository = myRepository; 
    } 
} 

mio MainController segue di conseguenza:

public class MainController 
{ 
    private readonly IMainManager MainManager; 
    private readonly IAThingManager AThingManager; 
    private readonly IBThingManager BThingManager; 
    private readonly ICThingManager CThingManager; 
    private readonly IDThingManager DThingManager; 
    private readonly IEThingManager EThingManager; 

    public MainController(IMainManager mainManager, IAThingManager aThingManager, IBThingManager bThingManager, ICThingManager cThingManager, IDThingManager dThingManager, IEThingManager eThingManager) 
    { 
     MainManager = mainManager; 
     AThingManager = aThingManager; 
     BThingManager = bThingManager; 
     CThingManager = cThingManager; 
     DThingManager = dThingManager; 
     EThingManager = eThingManager; 
    } 

    ...various ActionMethods... 
} 

In realtà, ci sono il doppio delle dipendenze iniettate in questo controller. Puzza. L'odore è peggiore quando sai anche che esiste un OtherController con tutte o quasi le stesse dipendenze. Voglio refactoring.

So già abbastanza su DI per sapere che l'iniezione di proprietà e il localizzatore di servizi non sono buone idee.

Non riesco a dividere il mio MainController, perché è una singola schermata che richiede che tutte queste cose siano visualizzate e modificabili con il clic di un singolo pulsante Salva. In altre parole, un singolo metodo di post-azione salva tutto (anche se sono disposto a cambiarlo se ha senso, purché sia ​​ancora un singolo pulsante Salva). Questa schermata è costruita con Knockoutjs e salva con i post Ajax se questo fa la differenza.

Mi rallegro dell'uso di un ambiente ambient, ma non sono sicuro che sia la strada giusta da percorrere. Ho anche incoraggiato l'uso di iniettare una facciata. Mi chiedo anche se dovrei implementare un'architettura di comando a questo punto. (Non è possibile spostare l'odore da qualche altra parte?)

Infine, e forse indipendente dai tre approcci precedenti, dovrei invece avere un singolo, per esempio, LookupManager con metodi espliciti come GetAThings() , GetAThing (id), GetBThings(), GetBThing (id) e così via? (Ma poi quel LookupManager avrebbe bisogno di diversi repository iniettati, o un nuovo tipo di repository.)

Le mie riflessioni a parte, la mia domanda è, per ribadire: qual è un buon modo per refactoring questo codice per ridurre il numero pazzo di dipendenze iniettate?

+0

possibile duplicato di [Come gestire l'over-injection del costruttore in .NET] (http://stackoverflow.com/questions/4603555/how-to-deal-with-constructor-over-in-injection-in-net) – Steven

risposta

3

L'utilizzo di command architecture è una buona idea, poiché sposta fuori dal controller tutta la logica aziendale e consente di aggiungere cross-cutting concerns senza modifiche al codice. Tuttavia, questo non risolverà il tuo problema di constructor over-injection. La soluzione standard è spostare le dipendenze correlate in un aggregate service. Tuttavia, sono d'accordo con Mark sul fatto che dovresti dare un'occhiata allo unit of work pattern.

+0

Grazie per i riferimenti. Mi piace questo approccio.Mi occuperò del refactoring di un servizio aggregato per le mie necessità immediate e quindi approfondirò l'architettura di comando e l'unità di lavoro. –

+0

@Steven puoi fornire un esempio di come questo potrebbe funzionare con SimpleInjector. Ho seguito il tuo esempio su http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=95 ed è stato un approccio utile. Come posso applicare una facciata o un servizio aggregato. –

+0

@DavidClarke: Non sono sicuro di cosa mostrare. Dai uno sguardo al modello dell'Unità di lavoro; di solito è una classe che avvolge i repository. Per evitare la sovrainiezione del costruttore nell'unità di lavoro, è possibile iniettare una fabbrica che sappia come risolvere i repository. – Steven

4

Avete considerato l'utilizzo di un'unità di modello di progettazione del lavoro? C'è il post a great MSDN su quale unità di lavoro è. Un estratto da tale articolo:

In un certo senso, si può pensare l'unità di lavoro come un luogo per scaricare tutto codice di transazione di gestione. Le responsabilità dell'unità di lavoro sono:

  • Gestire le transazioni.
  • Ordina il database inserisce, elimina e aggiorna.
  • Previene aggiornamenti duplicati. All'interno di un singolo utilizzo di un oggetto Unità di lavoro, diverse parti del codice possono contrassegnare lo stesso oggetto fattura
    come modificato, ma la classe Unità di lavoro emetterà solo un comando UPDATE singolo
    nel database.

Il valore di usare un'unità del modello di lavoro è quello di liberare il resto del codice da queste preoccupazioni in modo da poter concentrarsi sulla altrimenti logica di business.

Ci sono diversi post di blog su questo, ma il migliore che ho trovato è su come implementarlo è here. Ce ne sono altri a cui è stato fatto riferimento da questo sito here e here.

Infine, e forse indipendente dei tre approcci di cui sopra, è devo invece avere un unico, diciamo, LookupManager con espliciti metodi come GetAThings(), GetAThing (id), GetBThings(), GetBThing (id), e così via? (Ma allora che LookupManager avrebbe bisogno diversi repository iniettato in esso, o di un nuovo tipo di repository.)

L'unità di lavoro sarebbe in grado di gestire tutti questi, soprattutto se siete in grado di implementare un repository generico per la maggior parte delle esigenze di gestione del database. Il tag indica che stai utilizzando Entity Framework 4.3, giusto?

Spero che questo aiuti!

+0

[Qui] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=84) è (ancora) un'altra variazione del modello dell'unità di lavoro. – Steven

+0

Creo solo una classe UnitOfWork di tipo giungla utilizzata da tutti i controller? Ho già un repository generico . Quella classe farebbe riferimento ad ogni repository? –

+0

Sì, è esattamente questo che farebbe. Questa singola unità gestirà anche la simultaneità degli oggetti quando vengono estratti da tutti i diversi repository, oltre a garantire di avere solo un contesto condiviso tra loro. –

0

Penso che il tuo problema principale sia troppi livelli di astrazione. Stai utilizzando Entity Framework, quindi hai già un livello di astrazione attorno ai tuoi dati, aggiungendo altri due livelli (uno per entità) tramite un repository e un'interfaccia di Manager ha portato al gran numero di interfacce da cui il controller dipende. Non aggiunge molto valore e inoltre, YAGNI.

Rifarei, eliminando i livelli del repository e del gestore e utilizzare un "contesto ambientale".

Quindi, esaminare i tipi di query che il controller richiede ai livelli del gestore. Dove questi sono molto semplici, non vedo problemi a interrogare il tuo 'contesto ambientale' direttamente nel tuo controller - questo è quello che farei. Laddove sono più complicati, riformattali in una nuova interfaccia, raggruppando le cose logicamente (non necessariamente una per Entity) e usa il tuo IOC per questo.

Problemi correlati