2009-11-05 13 views
20

Sto attraversando un periodo molto difficile nel tentativo di accedere a una sezione di configurazione personalizzata nel mio file di configurazione.Sezione di configurazione personalizzata: Impossibile caricare il file o l'assemblaggio

Il file di configurazione viene letto da un dll caricato come plug-in. Ho creato la configurazione e il codice necessario usando l'addin VS Configuration Section Designer.

Lo spazio dei nomi è 'ImportConfiguration'. La classe ConfigurationSection è "ImportWorkflows". L'assembly è ImportEPDMAddin.

Il xml:

<configSections> 
    <section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/> 
    </configSections> 

Ogni volta che provo a leggere nella configurazione, ottengo l'errore:

Si è verificato un errore durante la creazione del gestore della sezione di configurazione per importWorkflows: Impossibile caricare il file o l'assembly ' ImportEPDMAddin.dll 'o una delle sue dipendenze. Il sistema non trova il file specificato.

La DLL non risiederà nella stessa directory dell'eseguibile poiché il software che carica il plugin posiziona la DLL e le sue dipendenze nella propria directory. (Non posso controllare quello.)

Ho modificato il codice per l'istanza Singleton al seguente:

string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase; 
path = path.Replace("file:///", ""); 
System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(path); 
return configuration.GetSection(ImportWorkflowsSectionName) as ImportConfiguration.ImportWorkflows; 

Inoltre ho provato utilizzando un semplice NameValueFileSectionHandler pure, ma ottengo un'eccezione dicendo che non può caricare file o assembly 'Sistema'.

Ho letto numerosi post e articoli sul blog e sembra che sia possibile leggere un file di configurazione in una DLL, ma non riesco a farlo funzionare. Qualche idea? Grazie.

+0

Avete copiato 'ImportEPDMAddin.dll.config' nella stessa posizione troppo? – ephemient

+0

La configurazione è sicura, poiché ho provato a utilizzare DictionarySectionHandler da un'altra classe e questo funziona. – ehcanadian

risposta

34

Purtroppo, è necessario né avere il ImportEPDMAddin assemblaggio residente nella stessa cartella del file eseguibile, residente nella cartella NET Framework relativo al framework .Net in uso (ad esempio, C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727) o registrati nella Global Assembly Cache.

L'unica altra opzione è, se si conosce il percorso per l'assembly che contiene classe che definisce il gestore di configurazione, è possibile caricare senza un riferimento con qualcosa di simile:

//Class global 
private Assembly configurationDefiningAssembly; 

protected TConfig GetCustomConfig<TConfig>(string configDefiningAssemblyPath, 
    string configFilePath, string sectionName) where TConfig : ConfigurationSection 
{ 
    AppDomain.CurrentDomain.AssemblyResolve += new 
     ResolveEventHandler(ConfigResolveEventHandler); 
    configurationDefiningAssembly = Assembly.LoadFrom(configDefiningAssemblyPath); 
    var exeFileMap = new ExeConfigurationFileMap(); 
    exeFileMap.ExeConfigFilename = configFilePath; 
    var customConfig = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap, 
     ConfigurationUserLevel.None); 
    var returnConfig = customConfig.GetSection(sectionName) as TConfig; 
    AppDomain.CurrentDomain.AssemblyResolve -= ConfigResolveEventHandler; 
    return returnConfig; 
} 

protected Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args) 
{ 
    return configurationDefiningAssembly; 
} 

Assicurati di gestire la AssemblyResolve event, in quanto ciò genererebbe un'eccezione senza di esso.

+0

Grazie mille, molto. Questo ha funzionato al 100% !! – ehcanadian

+0

Questo ha funzionato come un incantesimo per accedere e trasmettere il tipo di una sezione personalizzata in un file T4. Grazie! – Matt

+0

@ AJ. Spiacente, puoi spiegarmi per favore cosa è 'configDefiningAssemblyPath' .. è il file .exe? – Ciccio

0

Hai fatto in modo che la DLL sia caricata prima? Forse con Assembly.LoadFile("PATH")?

Se non è possibile far funzionare correttamente le classi in System.Configuration, è sempre possibile utilizzare XmlDocument per analizzare manualmente il file di configurazione. Usa XPath per rendere più facile ottenere i dati. Per esempio (supponendo che la variabile percorso sopra):

var document = new XmlDocument(); 
document.Load(path); 
var node = document.SelectSingleNode("configuration/importWorkflows/add[@name='KEY']"); 
// Do whatever with node 
+0

Questo metodo funziona, ma speravo di mantenere le classi di configurazione. Se tutto il resto fallisce, dovrò andare con il tuo suggerimento. – ehcanadian

1

Potrebbe verificare che i percorsi di sondaggio sono configurato correttamente nel file di configurazione dell'applicazione host? È possibile che un riferimento necessario non venga caricato nel dominio dell'applicazione corrente.

Assembly Binding ->Probing

5

Nel file delle applicazioni di configurazione principale, aggiungere il seguente (in cui i plugin è la cartella per l'assembly da caricare dal. È possibile utilizzare più percorsi e virgola separati.

<runtime> 
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     <probing privatePath=".;.\Plugins"/> 
    </assemblyBinding> 
</runtime> 

Tratto da http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx

+1

Si noti che l'uso del probing per risolvere il problema dell'OP funzionerà solo se la cartella dei plug-in specificata nell'attributo privatePath è una sottodirectory della directory principale dell'applicazione. Vedi [msdn docs] (http://msdn.microsoft.com/en-us/library/15hyw9x3 (v = vs.110) .aspx) – BitMask777

4

per espandere sulla risposta eccellente di AJ, ecco una classe personalizzata per assistere con il sovraccarico di registrazione e rimuovere l'evento mondiale.

public sealed class AddinCustomConfigResolveHelper : IDisposable 
{ 
    public AddinCustomConfigResolveHelper(
     Assembly addinAssemblyContainingConfigSectionDefinition) 
    { 
     Contract.Assert(addinAssemblyContainingConfigSectionDefinition != null); 

     this.AddinAssemblyContainingConfigSectionDefinition = 
      addinAssemblyContainingConfigSectionDefinition; 

     AppDomain.CurrentDomain.AssemblyResolve += 
      this.ConfigResolveEventHandler; 
    } 

    ~AddinCustomConfigResolveHelper() 
    { 
     this.Dispose(false); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool isDisposing) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve -= this.ConfigResolveEventHandler; 
    } 

    private Assembly AddinAssemblyContainingConfigSectionDefinition { get; set; } 

    private Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args) 
    { 
     // often the name provided is partial...this will match full or partial naming 
     if (this.AddinAssemblyContainingConfigSectionDefinition.FullName.Contains(args.Name)) 
     { 
      return this.AddinAssemblyContainingConfigSectionDefinition; 
     } 

     return null; 
    } 
} 

Vorrei suggerire la creazione di un'istanza in un'istruzione using, in questo modo:

// you'll need to populate these two variables 
var configuration = GetConfiguration(); 
var assembly = GetAssemblyContainingConfig(); 

using(new AddinCustomConfigResolveHelper(assembly)) 
{ 
    return (MyConfigSection)configuration.GetSection("myConfigSection"); 
} 
0

ho cercato la risposta di AJ, con supplemento di rileywhite ma ho scoperto che non ha funzionato per me.

Nel mio scenario, la classe ConfigurationSection personalizzata era già nell'assembly correntemente in esecuzione e il tentativo di caricarlo provoca un overflow dello stack. Inoltre, non volevo inserirlo in GAC anche se ha risolto il problema come riportato dall'OP.

Alla fine, ho trovato che questo funziona abbastanza bene per il mio scopo. Forse gli altri lo troveranno utile:

public class CustomConfigurationSection : ConfigurationSection { 
    public CustomConfigurationSection() 
    { 
    var reader = XmlReader.Create(<path to my dll.config>); 
    reader.ReadToDescendant("CustomConfigurationSection"); 
    base.DeserializeElement(reader,false); 
    } 

    // <rest of code> 
} 
0

dovuto usare la stringa di tipo completo della mia assemblaggio dei moduli/plugin, che si trova in una directory di sondaggio, in modo che possa essere individuato. Utilizzando EntityFramework come esempio ...

errato:

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework" 

corretta

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"