2009-11-05 11 views
20

Me está resultando muy difícil intentar acceder a una sección de configuración personalizada en mi archivo de configuración.Sección de configuración personalizada: No se pudo cargar el archivo o el ensamblaje

El archivo de configuración se lee desde un .dll que se carga como un complemento. Creé la Configuración y el código necesario usando el complemento Configuration Section Designer VS.

El espacio de nombre es 'ImportConfiguration'. La clase ConfigurationSection es 'ImportWorkflows'. El ensamblado es ImportEPDMAddin.

El xml:

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

Cada vez que trato de leer en la configuración, se produce el error:

Se produjo un error al crear el controlador de sección de configuración para importWorkflows: No se pudo cargar el archivo o ensamblado ' ImportEPDMAddin.dll 'o una de sus dependencias. El sistema no puede encontrar el archivo especificado.

El dll no residirá en el mismo directorio que el ejecutable, ya que el software que carga el complemento coloca el dll y sus dependencias en su propio directorio. (No puedo controlar eso.)

He editado el código para la instancia singleton a lo siguiente:

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; 

También he intentado usar un NameValueFileSectionHandler simple también, pero me da una excepción diciendo que no puede cargar el archivo o el ensamblado 'Sistema'.

He leído numerosas publicaciones y artículos en el blog y parece que es posible leer un archivo de configuración para un dll, pero no puedo hacer que funcione. ¿Algunas ideas? Gracias.

+0

¿Copiaste 'ImportEPDMAddin.dll.config' en la misma ubicación también? – ephemient

+0

La configuración está segura, ya que he intentado usar un DictionarySectionHandler de otra clase y eso funciona. – ehcanadian

Respuesta

34

Por desgracia, tendrá que o bien tienen la ImportEPDMAddin montaje que reside en la misma carpeta que el ejecutable, con domicilio en la carpeta NET Framework relacionadas con el marco .Net que está utilizando (es decir, C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727), o registrado en el caché de ensamblaje global.

La única otra opción es, si conoce la ruta de acceso al ensamblado que contiene la clase que define el manejador de configuración, puede cargar sin una referencia con algo como esto:

//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; 
} 

Asegúrese de manipular la Evento AssemblyResolve, ya que arrojará una excepción sin él.

+0

Muchas gracias, muchísimo. Esto funcionó al 100% !! – ehcanadian

+0

Esto funcionó como un amuleto para acceder y emitir el tipo de sección personalizada en un archivo T4. ¡Gracias! – Matt

+0

@AJ. Lo siento, ¿puedes explicarme qué es 'configDefiningAssemblyPath' ... ¿es el archivo .exe? – Ciccio

0

¿Se ha asegurado de que la DLL se cargue primero? Tal vez con Assembly.LoadFile("PATH")?

Si no puede obtener las clases en System.Configuration para que funcionen correctamente, siempre puede volver a utilizar XmlDocument para analizar manualmente el archivo de configuración. Use XPaths para facilitar la obtención de los datos. Por ejemplo (asumiendo que la variable de ruta anterior):

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

Este método funciona, pero esperaba seguir con las clases de configuración. Si todo lo demás falla, tendré que seguir tu sugerencia. – ehcanadian

1

Podría verificar que las trayectorias de sondeo están configurados correctamente en el archivo de configuración de la aplicación host? Es posible que no se esté cargando una referencia necesaria en su dominio de aplicación actual.

Assembly Binding ->Probing

5

En el archivo principal de aplicaciones de configuración, añada lo siguiente (donde plugins es la carpeta para su ensamblaje para cargar desde. Se pueden utilizar varios caminos punto y coma separa.

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

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

+1

Tenga en cuenta que utilizar el sondeo para resolver el problema del OP solo funcionará si la carpeta de complementos especificada en el atributo privatePath es un subdirectorio del directorio raíz de la aplicación. Consulte [msdn docs] (http://msdn.microsoft.com/en-us/library/15hyw9x3 (v = vs.110) .aspx) – BitMask777

4

para ampliar excelente respuesta de AJ, aquí es una clase personalizada para ayudar con los gastos generales de registrar y eliminar el evento global.

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; 
    } 
} 

que sugeriría la creación de una instancia de una instrucción using, así:

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

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

me trataron respuesta de AJ, con un suplemento de rileywhite, pero me pareció que no funcionó para mí.

En mi caso, la clase ConfigurationSection personalizada ya estaba en el ensamblado que se está ejecutando actualmente, y al intentar cargarla provoca un desbordamiento de la pila. Tampoco quería ponerlo en GAC aunque resolvió el problema según lo informado por el OP.

Al final, encontré que esto funcionaba lo suficientemente bien para mi propósito. Tal vez otros lo encontrarán útil:

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

tenido que utilizar la cadena de tipo completo de mi conjunto del módulo/plugin, que se encuentra en un directorio de sondeo, por lo que pudo ser localizado. ADO.NET Entity Framework utilizando como ejemplo ...

incorrecta:

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

correcta

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