2011-11-04 10 views
6

Sto usando l'esempio DSL del registro per configurare structuremap. Ma facendo questo, tutti i miei tipi registrati sono disponibili in tutti i livelli della mia applicazione, dove aggiungo una referenza alla mappa della struttura. Non voglio che il mio livello aziendale conosca qualcosa sul mio livello di accesso ai dati e viceversa. Come faccio a ottenere structuremap per registrare solo tipi specifici per ciascuno dei miei livelli?Structuremap - Come registrare determinati tipi in determinati layer

Ecco il codice nel mio file Global.asax:

ObjectFactory.Initialize(x => 
{ 
    x.AddRegistry<RegistryIOC>(); 
}); 

E qui è la mia classe RegistryIOC:

public class RegistryIOC : SMRegistry 
{ 

    public RegistryIOC() 
    { 
     For<IProfileService>.Use<ProfileService>(); 
     For<IProctorService>().Use<ProctorService>(); 

     //Business Logic Objects 
     For<IQual>().Use<Qual>(); 
     For<ITest>().Use<Test>(); 
     For<IBoldface>().Use<Boldface>(); 
     For<ITrainingPlan>().Use<TrainingPlan>(); 
     For<IUnit>().Use<Unit>(); 

     //Data Transfer Objects 
     For<IGenericDTO>().Use<GenericDTO>(); 
     For<IProfileDTO>().Use<ProfileDTO>(); 
     For<IQualDTO>().Use<QualDTO>(); 
     For<IPermissionDTO>().Use<PermissionDTO>(); 

     //Repository Objects 
     For<IProctorRepository>().Use<ProctorRepository>(); 
     For<IQualsRepository>().Use<QualsRepository>(); 
     For<ITestRepository>().Use<TestRepository>(); 
     For<IUnitRepository>().Use<UnitRepository>(); 
     For<IUserRepository>().Use<UserRepository>(); 
    } 

} 

Grazie per l'aiuto.

+0

Di che tipo di strati stai parlando? Processi diversi? Macchine diverse?Se sono tutti in esecuzione nello stesso processo, probabilmente fai quello che il tuo livello aziendale conosce * un po '* sul tuo livello dati, in particolare, la sua interfaccia. Non è chiaro quale problema stai cercando di risolvere. Cosa c'è di sbagliato nella tua classe RegistryIOC? –

+0

Abbiamo un livello di servizio, BLL e DAL che sono tutti progetti separati. Ogni progetto fa riferimento a StructureMap. Il livello di servizio conosce gli altri due livelli ma BLL e DAL non si conoscono l'un l'altro. Non voglio che altri sviluppatori facciano uso di Business Objects all'interno del DAL e viceversa. Non voglio che gli sviluppatori utilizzino gli Oggetti del repository all'interno della BLL. Il Service Layer orchestra tutto ciò. Quindi utilizzare la registrazione di tutti i tipi in questo modo rende tutti gli oggetti disponibili su tutti i nostri livelli (progetti). –

+0

Bene, * non * aggiungere un riferimento a StructureMap da qualsiasi altro livello rispetto alla Composizione Root ... –

risposta

3

Utilizzo la riflessione per eseguire questa (e altre) attività. Lasciami mostrare come funziona.

La prima cosa da fare è quello di definire un'interfaccia che permette di identificare le classi che svolgono attività di inizializzazione:

public interface IConfigurationTask 
{ 
    void Configure(); 
} 

Successivamente, creare una o più classi che implementano questa interfaccia. Queste lezioni saranno distribuite su tutti i tuoi progetti, il che è un altro modo per dire che puoi metterli "dove appartengono".

public class RepositoryInitializer : IConfigurationTask 
{ 
    public void Configure() 
    { 
     // code that does relevant initialization goes here 
    } 
} 

L'ultimo pezzo del puzzle è quello di trovare le classi che implementano l'interfaccia IConfigurationTask, creare un'istanza di loro ed eseguire il metodo di configurazione. Questo è lo scopo del ConfigurationTaskRunner:

public static class ConfigurationTaskRunner 
{ 
    public static void Execute(params string[] assemblyNames) 
    { 
     var assemblies = assemblyNames.Select(Assembly.Load).Distinct().ToList(); 
     Execute(assemblies); 
    } 

    public static void Execute(IEnumerable<Assembly> assemblies) 
    { 
     var tasks = new List<IConfigurationTask>(); 
     assemblies.ForEach(a => tasks.AddRange(a.CreateInstances<IConfigurationTask>())); 

     tasks.ForEach(t => t.Configure()); 
    } 
} 

Il codice mostrato qui utilizza un'estensione personalizzata per iterare su tutti gli elementi di un elenco ed eseguire un'azione per ogni elemento (il metodo ForEach). Sto anche usando un reflection library per fare il compito di localizzare e istanziare le istanze di un one-liner (il metodo CreateInstances), ma si potrebbe ottenere lo stesso usando semplicemente la riflessione (come mostrato nel codice qui sotto).

public static IList<T> CreateInstances<T>(this Assembly assembly) 
{ 
    var query = from type in assembly.GetTypes().Where(t => typeof(T).IsAssignableFrom(t) && typeof(T) != t) 
       where type.IsClass && ! type.IsAbstract && type.GetConstructor(Type.EmptyTypes) != null 
       select (T) Activator.CreateInstance(type); 
    return query.ToList(); 
}  

Il pezzo finale del puzzle è di attivare l'esecuzione di ConfigurationTaskRunner. Per esempio, in un'applicazione web questo sarebbe andare in Application_Start in Global.asax:

// pass in the names of the assemblies we want to scan, hardcoded here as an example 
ConfigurationTaskRunner.Execute("Foo.dll", "Foo.Domain.dll"); 

Ho anche trovato utile con un IPrioritizedConfigurationTask derivato (che aggiunge una proprietà Priority) per consentire la corretta ordinamento dei compiti prima li esegui. Questo non è mostrato nel codice di esempio sopra, ma è piuttosto semplice da aggiungere.

Spero che questo aiuti!

+0

In questo esempio (che è fantastico) come proponi di concatenare le dipendenze? Se volessi un'istanza di IProdRepo supportata da ProdCacheRepo che è stata supportata da ProdRepo ... come faresti a fare questo concatenamento? Curioso! –

+0

@AndrewSiemer Suppongo che sarebbe il compito del framework IoC (in questo caso StructureMap), basato sulle configurazioni fornite dall'utente (nei singoli metodi Configure). Il codice che ho fornito è semplicemente un meccanismo per la "configurazione distribuita" del contenitore IoC (o qualsiasi altra cosa che sia necessario inizializzata all'avvio dell'applicazione). –

+0

Non posso fare uso della libreria di riflessioni. Quando provo questo codice tramite la riflessione regolare, a.CreateInstance ("IConfigurationTask"), non ottengo nulla come risultato. Forse mi manca qualcosa da qualche parte ... :-( –

0

È possibile creare e configurare più istanze Container indipendenti e non utilizzare statica ObjectFactory a tutti - see this article. Sarà quindi tua responsabilità fornire contenitori adeguati ai livelli appropriati.

A proposito, come si desidera gestire la comunicazione tra livelli? Non sarebbe in qualche modo difficile? Preferirei dividere i registri (possibilmente per separare gli assembly) e tenerlo disaccoppiato "manualmente" invece di imporre il disaccoppiamento a livello di infrastruttura.

+0

Non mi interessa affatto cambiare la mia attuale implementazione. Se è meglio avere tutti i registri separati per classi su ciascuno dei miei livelli, questo è bello con me. Parte del mio problema è che non capisco completamente come "fornire contenitori adeguati ai livelli appropriati". Cosa faccio esattamente nel caricatore global.asax? –

+0

Bene, in genere devi disporre di punti di ingresso diversi per i diversi livelli e ogni livello deve disporre dei propri metodi di produzione utilizzando un contenitore appropriato: in questo modo avrai grafici di oggetti separati per ogni livello. Ma non vedo davvero come realizzare le interazioni tra questi grafici, quindi questo è il motivo per cui le domande nel secondo paragrafo sono. Infine devi avere una sorta di componenti dello strato superiore X che usano il componente di livello inferiore Y. Se X è il livello intermedio e viene creato dal contenitore del livello intermedio, lo è anche la sua dipendenza Y, anche se proviene dallo strato inferiore . – NOtherDev

+0

Quindi, in generale, la risposta è che è difficile avere grafici di oggetti completamente disaccoppiati. Ecco perché ho suggerito il disaccoppiamento a livello di registro/assembly, invece che a livello di fabbrica dell'oggetto. – NOtherDev

Problemi correlati