Riferimenti a IoC, DLL e analisi di assieme

Sebbene questa domanda sia correlata a StructureMap, la mia domanda generale è:

Quando si collegano i componenti con un contenitore IoC in codice (al contrario della configurazione tramite xml ) generalmente sono necessari riferimenti espliciti di progetto / compilazione a tutti gli assiemi?

Perché gli assemblaggi separati? Perché:


“Le classi astratte che risiedono in un’assemblea separata dalle loro implementazioni concrete sono un ottimo modo per ottenere tale separazione”. – Linee guida per la progettazione di strutture p.91


Esempio:

Diciamo che ho PersonBase.dll e Bob.dll

Bob eredita dalla class astratta PersonBase . Sono entrambi nel namespace Person . Ma in diverse assemblee .

Sto programmando su PersonBase , non su Bob .

Tornato nel mio codice principale, ho bisogno di una persona. StructureMap può analizzare assiemi. Fantastico, chiederò a StructureMap!

Ora, nel mio codice principale, mi riferisco ovviamente solo a PersonBase , non a Bob . In realtà non voglio che il mio codice sappia qualcosa su Bob . Nessun riferimento al progetto, niente nuthin. Questo è il punto.

Quindi voglio dire:

//Reference: PersonBase.dll (only) using Person; ... //this is as much as we'll ever be specific about Bob: Scan( x=> { x.Assembly("Bob.dll"); } //Ok, I should now have something that's a PersonBase (Bob). But no ? ObjectFactory.GetAllInstances().Count == 0 

Senza fortuna. Che cosa è il lavoro esplicito che voglio Bob:

 //Reference: PersonBase.dll and Bob.dll using Person; ... Scan( x => {x.Assembly("Bob.dll"); } //If I'm explicit, it works. But Bob's just a PersonBase, what gives? ObjectFactory.GetAllInstances().Count == 1 //there he is! 

Ma ora ho dovuto fare riferimento a Bob.dll nel mio progetto che è esattamente quello che non volevo.

Posso evitare questa situazione usando la configurazione di Spring + Xml. Ma poi torno alla configurazione di Spring + Xml …!

Mi manca qualcosa usando StructureMap, o come principio generale, le configurazioni IoC (fluenti) hanno bisogno di riferimenti esplicativi a tutti gli assiemi?

Possibile domanda correlata: StructureMap e gli assiemi di scansione

Finalmente ho risolto questo problema. Sembra questo:

IoC Uml http://img396.imageshack.us/img396/1343/iocuml.jpg

con le assemblee

  • core.exe
  • PersonBase.dll ( tempo di compilazione referenziato da Core.exe)
  • Bob.dll ( tempo di esecuzione caricato tramite StructureMap Scan)
  • Betty.dll ( caricamento del tempo di esecuzione tramite StructureMap Scan)

Per averlo con StructureMap, avevo bisogno di un “ITypeScanner” personalizzato per supportare la scansione degli assiemi:

 public class MyScanner : ITypeScanner { public void Process(Type type, PluginGraph graph) { if(type.BaseType == null) return; if(type.BaseType.Equals(typeof(PersonBase))) { graph.Configure(x => x.ForRequestedType() .TheDefault.Is.OfConcreteType(type)); } } } 

Quindi il mio codice principale sembra:

 ObjectFactory.Configure(x => x.Scan ( scan => { scan.AssembliesFromPath(Environment.CurrentDirectory /*, filter=>filter.You.Could.Filter.Here*/); //scan.WithDefaultConventions(); //doesn't do it scan.With(); } )); ObjectFactory.GetAllInstances() .ToList() .ForEach(p => { Console.WriteLine(p.FirstName); } ); 

Puoi anche fare una configurazione xml con StructureMap. Puoi anche mescolarli se vuoi.

Ci sono anche attributi StructureMap che puoi inserire nella tua class Bob per dire a StructureMap come caricare l’assembly. DefaultConstructor è uno che uso di volta in volta.

L’opzione di scansione automatica funziona solo quando si mantengono le convenzioni di denominazione, assemblaggio e spazio dei nomi. È ansible configurare manualmente structuremap con un’interfaccia fluente. Esempio:

 ObjectFactory.Initialize(initialization => initialization.ForRequestedType() .TheDefault.Is.OfConcreteType()); 

Cosa facciamo sul mio attuale progetto (che usa AutoFac, non StructureMap, ma penso che non dovrebbe fare la differenza):

Abbiamo le interfacce che definiscono servizi esterni che l’applicazione utilizza in un assembly principale, diciamo App.Core (come il tuo PersonBase).

Quindi abbiamo le implementazioni di queste interfacce in Services.Real (come Bob.dll).

Nel nostro caso abbiamo anche Service.Fake , che vengono utilizzati per facilitare i test dell’interfaccia utente con dipendenze su altri servizi e database aziendali, ecc.

L’applicazione “client” front-end stessa (nel nostro caso, app ASP.NET MVC) fa riferimento a App.Core .

All’avvio dell’app, utilizziamo Assembly.Load per caricare la DLL di implementazione “Servizi” appropriata, in base a un’impostazione di configurazione.

Ognuna di queste DLL ha un’implementazione di IServiceRegistry che restituisce un elenco dei servizi implementati:

 public enum LifestyleType { Singleton, Transient, PerRequest} public class ServiceInfo { public Type InterfaceType {get;set;} public Type ImplementationType {get;set;} // this might or might not be useful for your app, // depending on the types of services, etc. public LifestyleType Lifestyle {get;set;} } public interface IServiceRegistry { IEnumerable GetServices(); } 

… l’applicazione trova ServiceRegistry tramite riflessione e enumera attraverso queste istanze di ServiceInfo e le registra sul contenitore. Per noi, questo registro-tutti i servizi risiede nell’applicazione Web, ma è ansible (e preferibile in molti casi) averlo in un assembly separato.

In questo modo possiamo isolare la logica del dominio dal codice dell’infrastruttura e impedire che le soluzioni “just-this-once” in cui l’applicazione finisca dipendono da un riferimento diretto al codice dell’infrastruttura. Evitiamo anche di avere un riferimento al contenitore in ogni implementazione di Servizi.

Una cosa davvero importante se si sta facendo questo: assicurarsi di avere dei test che verificano che è ansible creare ogni tipo “di primo livello” (nel nostro caso, Controller MVC ASP.NET) con ogni potenziale configurazione del contenitore IOC.

Altrimenti, è abbastanza facile dimenticare di implementare un’interfaccia e interrompere enormi sezioni della tua applicazione.