【问题标题】:How to use IronPython with App.Config?如何将 IronPython 与 App.Config 一起使用?
【发布时间】:2010-10-07 17:38:15
【问题描述】:

我有一个通常从 .net 控制台或 Web 应用程序调用的类库。它与各种组件集成,并依赖于 app.config 或 web.config。

如果我想利用脚本中的类库(即 IronPython),我怎样才能让脚本利用配置文件?理想情况下,我希望能够在运行脚本时选择配置文件,或者按照惯例(配置文件位于脚本文件旁边)。

如果可能的话,我不想更改 ipy.exe.config,因为如果没有多个 IronPython 副本,这将无法针对多个配置进行扩展?

还有其他选择吗?

【问题讨论】:

    标签: ironpython


    【解决方案1】:

    this blog post 翻译成 Python,应该可以:

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

    【讨论】:

    • 这似乎没有任何区别
    • 您确定在库尝试加载其设置之前调用它吗?确保这一点的最简单方法是你 clr.AddReference 库之前进行。
    • 是的,它位于脚本的顶部,但没有任何区别。
    • FWIW,这与 Python.NET 配合得非常好! :)(尽管使用import System 而不是import System.AppDomain
    【解决方案2】:

    您始终可以在配置文件中包含其他部分。在您的 ipy.exe.config 文件中,您可以添加一个包含以导入外部配置设置;说 myApp.config。

    在批处理/命令文件中,您始终可以将特定的 .config 集复制到 myApp.config 中,因此可以根据需要使用不同的配置文件运行。

    看看这个博客如何实现这一点; http://weblogs.asp.net/pwilson/archive/2003/04/09/5261.aspx

    【讨论】:

      【解决方案3】:

      您可以查看System.Configuration.ConfigurationManager 类。更具体地说,OpenMappedExeConfiguration 方法将允许您加载您选择的任何 .config 文件。这将为您提供一个 Configuration 对象,该对象公开标准 AppSettins、ConnectionStrings、SectionGroups 和 Sections 属性。

      此方法要求您将配置文件的名称作为命令行参数传递给脚本,或者具有代码逻辑以在运行时选择 .config 文件。

      我不懂 Python,所以我不会尝试发布示例代码。 :-)

      【讨论】:

      • 我之前研究过这个,但是业务层由许多独立访问 web.config 的不同的 3rd 方模块组成。所以我真的需要确保整个应用都在访问正确的数据。
      【解决方案4】:

      对于解决方法,我所做的是“手动”填充 ConfigurationManager 静态类的 AppSettings 集合,因此我创建了一个 PY 脚本并在 IronPython 上运行它的“导入”,然后这些设置将可用于类库.但是我可以为 ConnectionStrings 集合赋值:(

      我的脚本是这样的

      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"
      

      如果知道一种将自定义 .config 文件“加载”到 ConfigurationManager 类的方法,那就太好了。

      【讨论】:

      • 这在 ConnectionStrings 集合上不起作用的原因是因为这个集合是只读的,因此不能通过代码修改。
      【解决方案5】:

      我有一个带有代码示例的工作解决方案。见我的博客: http://technomosh.blogspot.com/2012/01/using-appconfig-in-ironpython.html

      它需要一个注入到 ConfigurationManager 的特殊代理类。

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

      这是从 Python 加载它的方式:

      import clr
      clr.AddReferenceToFile('ConfigurationProxy.dll')
      
      from IronPythonUtilities import ConfigurationProxy
      
      def override(filename):
          proxy = ConfigurationProxy(filename)
          return proxy.InjectToConfigurationManager()
      

      最后是一个使用示例:

      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)
      

      【讨论】:

      • 我根据您的回答和相关博文在纯 IronPython 中创建了一个 ConfigProxy 实现。可通过software-architects.com/devblog/2012/10/29/… 获得。
      • Simon - 纯 IronPython 实现非常出色。谢谢!
      • 我知道我可能听起来很荒谬,但就我而言,上述“注入”我们自己的配置文件的解决方案不适用于 IronPython 的编译(使用 pyc.py)版本脚本。好吧,实际上没有理由覆盖配置文件,因为我们已经有了自己的应用程序并且可以有一个单独的配置 - 只是我自己搞混了,我用 ipy.exe 开发并且不得不使用上述解决方案,而编译后的版本和以上不起作用(?)当我根据那些.net命名标准命名我的配置时,设置是只读的,例如myapp.exe.config
      【解决方案6】:

      我尝试按照上面的答案进行操作,但发现它太复杂了。如果您从 App.config 文件中确切知道需要什么属性,则可以将其直接放在代码中。例如,我导入的 dll 需要知道 App.Config 文件中的 AssemblyPath 属性。

      import clr
      import System.Configuration
      clr.AddReference("System.Configuration")
      from System.Configuration import ConfigurationManager
      
      ConfigurationManager.AppSettings["AssemblyPath"] = 'C:/Program Files (X86)/...
      

      这就是我所需要的,我连接的类库能够看到它需要运行的 AssemblyPath 属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-15
        • 1970-01-01
        • 1970-01-01
        • 2011-02-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多