Domanda

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

  

Quando si collegano componenti con un IoC   container nel codice (al contrario   alla configurazione tramite xml )   generalmente necessitano di progetti / build espliciti   riferimenti a tutte le assemblee?

Perché gli assiemi separati? Perché:


  

" Classi astratte che risiedono in a   montaggio separato dal loro calcestruzzo   le implementazioni sono un ottimo modo per   ottenere tale separazione. " - Quadro   Linee guida per la progettazione p.91


Esempio:

Diciamo che ho PersonBase.dll e Bob.dll

Bob eredita dalla classe astratta PersonBase . Sono entrambi nello spazio dei nomi Person . Ma in assemblee diverse .

Sto programmando su PersonBase , non su Bob .

Nel mio codice principale, ho bisogno di una persona. StructureMap può eseguire la scansione di assiemi. Bene, chiederò a StructureMap per uno!

Ora, nel mio codice principale, mi riferisco ovviamente solo a PersonBase , non a Bob . In realtà non voglio che il mio codice sappia nulla su Bob . Nessun riferimento al progetto, nessun 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<PersonBase>().Count == 0

Nessuna fortuna. Ciò che funziona è essere 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<Bob>().Count == 1 //there he is!

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

Posso evitare questa situazione usando la configurazione Spring + Xml. Ma poi sono tornato alla configurazione Spring + Xml ...!

  

Mi sto perdendo qualcosa con l'utilizzo   StructureMap o come generale   principio, fai (fluente) IoC   le configurazioni richiedono riferimenti espliciti   a tutte le assemblee?

Eventualmente domanda correlata: StructureMap e scan assembly

È stato utile?

Soluzione

Ho finalmente risolto questo problema. Sembra così:

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

con gli assiemi

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

Per ottenerlo 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<PersonBase>()
        .TheDefault.Is.OfConcreteType(type));
    }
  }
} 

Quindi il mio codice principale è simile a:

ObjectFactory.Configure(x => x.Scan (
  scan =>
  {
    scan.AssembliesFromPath(Environment.CurrentDirectory 
    /*, filter=>filter.You.Could.Filter.Here*/);

    //scan.WithDefaultConventions(); //doesn't do it

    scan.With<MyScanner>();
  }
));

ObjectFactory.GetAllInstances<PersonBase>()
 .ToList()
  .ForEach(p => 
  { Console.WriteLine(p.FirstName); } );

Altri suggerimenti

Puoi eseguire la configurazione xml anche con StructureMap. Puoi anche mescolarli se vuoi.

Esistono anche attributi StructureMap che è possibile inserire nella classe Bob per indicare a StructureMap come caricare l'assembly. DefaultConstructor è quello che finisco di usare di volta in volta.

L'opzione di scansione automatica funziona solo quando si mantengono le convenzioni di denominazione, assemblaggio e spazio dei nomi. Puoi configurare manualmente la strutturemap con un'interfaccia fluida. Esempio:

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

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

Abbiamo le interfacce che definiscono i 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 sono utilizzati per facilitare il test dell'interfaccia utente con dipendenze da altri servizi e database aziendali, ecc.

Il front-end " client " stessa applicazione (nel nostro caso, app ASP.NET MVC) fa riferimento a App.Core .

All'avvio dell'app, utilizziamo Assembly.Load per caricare i servizi " appropriati " DLL di implementazione, basata su 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<ServiceInfo> GetServices();
}

... l'applicazione trova questo ServiceRegistry tramite reflection ed enumera attraverso queste istanze di ServiceInfo e le registra sul contenitore. Per noi, questo registro di tutti i servizi vive nell'applicazione Web, ma è possibile (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 "just-this-once" soluzioni alternative in cui l'applicazione finisce a seconda di un riferimento diretto al codice dell'infrastruttura. Evitiamo inoltre di dover avere un riferimento al contenitore in ogni implementazione dei Servizi.

Una cosa davvero importante se lo stai facendo: assicurati che sia sicuro di avere dei test per verificare che sia possibile creare ogni "top-level" digitare (nel nostro caso, controller ASP.NET MVC) con ogni potenziale configurazione del contenitore IOC.

Altrimenti, è abbastanza facile dimenticare di implementare un'interfaccia e spezzare enormi sezioni dell'applicazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top