2012-10-18 14 views
5

La mia applicazione comprende alcuni gruppi principali e diversi gruppi di estensioni/plugin. Affinché MEF possa conoscere tutte le parti che i plug-in hanno da offrire, devo caricare questi assembly anche se non userò mai nessuna delle loro parti. Ciò rende l'applicazione impiegare più tempo per l'avvio (se ho intenzione di caricare tutti gli assembly all'avvio) e aumenta anche l'ingombro della memoria.Caricamento lento di assiemi/cataloghi

Idealmente, non avrò bisogno di caricare gli assiemi finché non ne avrò effettivamente bisogno. Carico solo i dati di esportazione dei plug-in e, quando effettivamente devo importare una parte, MEF carica il gruppo e fornisce la parte.

Ho scoperto che c'è qualcosa che fa praticamente tutto quello che ho appena scritto, ma dopo averlo chiesto in MEF CachedAssemblyCatalog - Lazy Loading of Assemblies, ho capito che questo codice non è considerato stabile e non viene gestito dal team MEF, quindi ho deciso di non usarlo.

mia domanda è come allora posso ottenere questo comportamento:

  • Essere in grado di accedere ai metadati di esportazione assemblee plugin senza caricare tutto il loro assemblaggio.
  • Integrazione trasparente con il codice che importa le parti; utilizzare le importazioni come di consueto - qualcun altro (un catalogo specializzato?) si occuperà di caricare gli assemblaggi se necessario e di fornire la parte richiesta.
  • Non perdere alcuna funzionalità MEF esistenti, come ricomposizione, tipi pigri, ecc

Io sono completamente bene con una soluzione che richiede l'analisi dei plug-in anticipo per creare un assembly di metadati, file XML o roba del genere.

risposta

2

Se si è appena dopo il ritardo di caricamento degli assiemi, è possibile utilizzare parte delle soluzioni su this question. Non avresti bisogno di prendere tutte le informazioni che vengono recuperate in quella soluzione. Probabilmente solo il nome del contratto, il nome dell'assemblato e se la parte sta esportando o importando il contratto. Poi si può scrivere un catalogo che carica gli assembly quando ne hai bisogno, per esempio come questo:

public sealed class DelayLoadingCatalog : ComposablePartCatalog 
{ 
    // List containing tuples which have the 'contract name' 
    // and the 'assembly name' 
    private readonly List<Tuple<string, string>> m_Plugins 
     = new List<Tuple<string, string>>(); 
    private readonly Dictionary<string, AssemblyCatalog> m_Catalogs 
     = new Dictionary<string, AssemblyCatalog>(); 

    public DelayLoadingCatalog(IEnumerable<Tuple<string, string>> plugins) 
    { 
     m_Plugins.AddRange(plugins); 
    } 

    public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition) 
    { 
     var partsToLoad = m_Plugins 
      .Where(t => t.Item1.Equals(definition.ContractName)); 
     foreach (var part in partsToLoad) 
     { 
      if (!m_Catalogs.ContainsKey(part.Item2.Name)) 
      { 
       var assembly = Assembly.Load(new AssemblyName(part.Item2.Name)); 
       m_Catalogs.Add(part.Item2.Name, new AssemblyCatalog(assembly)); 
      } 
     } 

     return m_Catalogs.SelectMany(p => p.Value.GetExports(definition)); 
    } 

    public override IQueryable<ComposablePartDefinition> Parts 
    { 
     get 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 

che è quindi possibile utilizzare come questo

class Program 
{ 
    public void Init() 
    { 
     var domainSetup = new AppDomainSetup 
     { 
      ApplicationBase = Directory.GetCurrentDirectory(), 
     }; 

     var scanDomain = AppDomain.CreateDomain(
      "scanDomain", 
      null, 
      domainSetup); 
     var scanner = scanDomain.CreateInstanceAndUnwrap(
      typeof(MyScanner).Assembly.FullName, 
      typeof(MyScanner).FullName) as MyScanner; 
     var plugins = scanner.Scan(myPluginsPath); 

     // Make sure we don't have the assemblies loaded anymore ... 
     AppDomain.Unload(scanDomain); 

     var catalog = new DelayLoadingCatalog(plugins); 
     var container = new CompositionContainer(catalog); 

     container.ComposeParts(this); 
    } 

    [Import("MyCoolExport")] 
    public object MyImport 
    { 
     get; 
     set; 
    } 
} 

L'esempio DelayLoadCatalog non è molto intelligente come continuerà a cercare nell'elenco delle tuple. L'ottimizzazione del codice non dovrebbe essere troppo difficile. Ad esempio, è possibile controllare se tutti gli assemblaggi sono stati caricati e interrompere la ricerca attraverso tale elenco in quel punto.

+0

Hey Petrik, grazie mille per il vostro aiuto! Mi sono imbattuto nella domanda che hai collegato qualche tempo fa e mi ha aiutato molto a risolvere questo problema. Quello che ho finito per fare era qualcosa di simile a quello che hai descritto lì: ho serializzato i dati su un file e creato un nuovo catalogo che consuma quel file. In questo modo, tutte le esportazioni, compresi i metadati, sono disponibili e utilizzando l'API 'ReflectionModelServices', tutto è stato caricato pigramente. Questo sembra il modo migliore per soddisfare le mie esigenze. Spero di avere un po 'di tempo per rispondere a questa domanda da solo con quello che ho fatto. Grazie ancora! –

0

Se qualcuno è interessato, ho implementato una soluzione da solo e recentemente l'ho reso disponibile su GitHub. La soluzione LazyAssemblyLoading consente a un utente di serializzare le informazioni di una parte dell'assieme e successivamente utilizzarla per inizializzare uno LazyAssemblyCatalog che carica l'assieme solo quando una delle sue parti è effettivamente richiesta, consentendo comunque di utilizzare i suoi metadati come al solito mentre l'assembly non è caricato.

Problemi correlati