2009-02-15 22 views
21

Tengo una biblioteca de clases que generalmente se llama desde una consola .net o aplicación web. Se integra con varios componentes y se basa en un app.config o web.config.¿Cómo usar IronPython con App.Config?

Si deseo utilizar la biblioteca de clases del script (es decir, IronPython), ¿cómo puedo obtener el script para utilizar el archivo de configuración? Idealmente, quiero poder elegir el archivo de configuración cuando ejecuto el script, o por convención (archivo de configuración junto al archivo de script).

No quiero cambiar el ipy.exe.config si es posible, ya que esto no se escalaría para múltiples configuraciones sin tener varias copias de IronPython?

¿Alguna otra alternativa?

Respuesta

3

tengo una solución de trabajo con código de ejemplo Ver mi blog:. http://technomosh.blogspot.com/2012/01/using-appconfig-in-ironpython.html

se requiere una clase de proxy especial que se inyecta a la ConfigurationManager.

Aquí es la fuente para la biblioteca ConfigurationProxy:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Configuration; 
using System.Configuration.Internal; 
using System.Xml; 
using System.Collections.Specialized; 
using System.Reflection; 
using System.IO; 

namespace IronPythonUtilities 
{ 
    /// <summary> 
    /// A custom app.config injector for use with IronPython code that needs configuration files. 
    /// The code was taken and modified from the great work by Tom E Stephens: 
    /// http://tomestephens.com/2011/02/making-ironpython-work-overriding-the-configurationmanager/ 
    /// </summary> 
    public sealed class ConfigurationProxy : IInternalConfigSystem 
    { 
     Configuration config; 
     Dictionary<string, IConfigurationSectionHandler> customSections; 

     // this is called filename but really it's the path as needed... 
     // it defaults to checking the directory you're running in. 
     public ConfigurationProxy(string fileName) 
     { 
      customSections = new Dictionary<string, IConfigurationSectionHandler>(); 

      if (!Load(fileName)) 
       throw new ConfigurationErrorsException(string.Format(
        "File: {0} could not be found or was not a valid cofiguration file.", 
        config.FilePath)); 
     } 

     private bool Load(string file) 
     { 
      var map = new ExeConfigurationFileMap { ExeConfigFilename = file }; 
      config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); 

      var xml = new XmlDocument(); 
      using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read)) 
       xml.Load(stream); 

      //var cfgSections = xml.GetElementsByTagName("configSections"); 

      //if (cfgSections.Count > 0) 
      //{ 
      // foreach (XmlNode node in cfgSections[0].ChildNodes) 
      // { 
      //  var type = System.Activator.CreateInstance(
      //        Type.GetType(node.Attributes["type"].Value)) 
      //        as IConfigurationSectionHandler; 

      //  if (type == null) continue; 

      //  customSections.Add(node.Attributes["name"].Value, type); 
      // } 
      //} 

      return config.HasFile; 
     } 

     public Configuration Configuration 
     { 
      get { return config; } 
     } 

     #region IInternalConfigSystem Members 

     public object GetSection(string configKey) 
     { 
      if (configKey == "appSettings") 
       return BuildAppSettings(); 

      object sect = config.GetSection(configKey); 

      if (customSections.ContainsKey(configKey) && sect != null) 
      { 
       var xml = new XmlDocument(); 

       xml.LoadXml(((ConfigurationSection)sect).SectionInformation.GetRawXml()); 
       // I have no idea what I should normally be passing through in the first 
       // two params, but I never use them in my confighandlers so I opted not to 
       // worry about it and just pass through something... 
       sect = customSections[configKey].Create(config, 
             config.EvaluationContext, 
             xml.FirstChild); 
      } 

      return sect; 
     } 

     public void RefreshConfig(string sectionName) 
     { 
      // I suppose this will work. Reload the whole file? 
      Load(config.FilePath); 
     } 

     public bool SupportsUserConfig 
     { 
      get { return false; } 
     } 

     #endregion 

     private NameValueCollection BuildAppSettings() 
     { 
      var coll = new NameValueCollection(); 

      foreach (var key in config.AppSettings.Settings.AllKeys) 
       coll.Add(key, config.AppSettings.Settings[key].Value); 

      return coll; 
     } 

     public bool InjectToConfigurationManager() 
     { 
      // inject self into ConfigurationManager 
      var configSystem = typeof(ConfigurationManager).GetField("s_configSystem", 
              BindingFlags.Static | BindingFlags.NonPublic); 
      configSystem.SetValue(null, this); 

      // lame check, but it's something 
      if (ConfigurationManager.AppSettings.Count == config.AppSettings.Settings.Count) 
       return true; 

      return false; 
     } 
    } 
} 

y aquí es cómo se puede cargar desde Python:

import clr 
clr.AddReferenceToFile('ConfigurationProxy.dll') 

from IronPythonUtilities import ConfigurationProxy 

def override(filename): 
    proxy = ConfigurationProxy(filename) 
    return proxy.InjectToConfigurationManager() 

Por último, una muestra de uso:

import configproxy 
import sys 

if not configproxy.override('blogsample.config'): 
    print "could not load configuration file" 
    sys.exit(1) 

import clr 
clr.AddReference('System.Configuration') 
from System.Configuration import * 
connstr = ConfigurationManager.ConnectionStrings['TestConnStr'] 
print "The configuration string is {0}".format(connstr) 
+1

Creé una implementación de ConfigProxy en IronPython puro en función de su respuesta y las publicaciones de blog relacionadas. Está disponible en http://www.software-architects.com/devblog/2012/10/29/appconfig-in-IronPython-without-additional-assemblies. –

+0

Simon: la implementación pura de IronPython es brillante. ¡Gracias! – Moshe

+0

Sé que puedo sonar absurdo, pero en mi caso, la solución anterior de "inyectar" nuestro propio archivo de configuración, no funcionó para una versión compilada (usando pyc.py) del script de IronPython. Está bien, en realidad no hay razón para anular el archivo de configuración, ya que tenemos nuestra propia aplicación y podemos tener una configuración separada. Simplemente me confundí, desarrollé con ipy.exe y tuve que usar la solución anterior, mientras con la versión compilada y el anterior no funcionaba (?) la configuración solo se leía cuando nombraba mi configuración de acuerdo con esos estándares de nomenclatura .net, por ejemplo myapp.exe.config –

0

Traduciendo this blog post en Python, esto debería funcionar:

import clr 
import System.AppDomain 
System.AppDomain.CurrentDomain.SetData(“APP_CONFIG_FILE”, r”c:\your\app.config”) 
+1

Esto no pareció marcar ninguna diferencia –

+0

¿Se aseguró de llamar esto antes de que la biblioteca intente cargar su configuración? La forma más fácil de garantizar esto sería hacerlo * antes * de que clr.AddReference la biblioteca. – oefe

+1

Sí, está en la parte superior del guión, pero no hace ninguna diferencia. –

0

Siempre se puede incluir secciones adicionales dentro de los archivos de configuración. En su archivo ipy.exe.config, puede agregar un include para importar configuraciones de configuración externas; di myApp.config.

En un archivo por lotes/comando, siempre puede copiar un conjunto .config específico en myApp.config y, por lo tanto, ejecutarlo con diferentes archivos de configuración bajo demanda.

Echa un vistazo a este blog sobre cómo lograr esto; http://weblogs.asp.net/pwilson/archive/2003/04/09/5261.aspx

2

Puede consultar la clase System.Configuration.ConfigurationManager. Más específicamente, el método OpenMappedExeConfiguration le permitirá cargar cualquier archivo .config de su elección. Esto le dará un objeto Configuration que expone las propiedades estándar AppSettins, ConnectionStrings, SectionGroups y Sections.

Este enfoque requiere que pase el nombre del archivo de configuración a la secuencia de comandos como un argumento de línea de comando o que tenga una lógica de código para elegir el archivo .config en tiempo de ejecución.

No conozco Python, por lo que me abstendré de intentar publicar el código de muestra. :-)

+1

He examinado esto anteriormente, pero la capa empresarial está compuesta por muchos módulos de terceros que acceden a web.config de forma independiente. Así que realmente necesito asegurarme de que toda la aplicación esté accediendo a los datos correctos. –

0

Por una solución, lo que hice fue llenar la colección AppSettings para la clase estática de ConfigurationManager "manualmente", así que creé un PY Script y ejecuté una "importación" en IronPython y la configuración estará disponible para la biblioteca de la clase. Sin embargo no pude assing valores a la colección ConnectionStrings :(

mi guión se parece a esto

import clr 
clr.AddReferenceToFileAndPath(r'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.configuration.dll') 
from System.Configuration import * 
ConfigurationManager.AppSettings["settingA"] = "setting A value here" 
ConfigurationManager.AppSettings["settingB"] = "setting B value here" 

Sería bueno aunque a conocer una forma de "carga" archivo .config una costumbre de la clase ConfigurationManager.

+1

La razón por la que esto no funcionaría en la colección de ConnectionStrings se debe a que esta colección es de solo lectura, por lo que no se puede modificar mediante código. – Moshe

0

Intenté seguir las respuestas anteriores, pero me pareció demasiado complejo. Si sabe exactamente qué atributo necesita de su archivo App.config, puede colocarlo directamente en el código. Por ejemplo, una DLL que importé necesitaba conocer el atributo AssemblyPath en mi archivo App.Config.

import clr 
import System.Configuration 
clr.AddReference("System.Configuration") 
from System.Configuration import ConfigurationManager 

ConfigurationManager.AppSettings["AssemblyPath"] = 'C:/Program Files (X86)/... 

Esto era todo lo que necesitaba, y la biblioteca de clases que estaba conectando con la oportunidad de ver la AssemblyPath atributo que necesita para funcionar.