我有一个类库,通常从.NET控制台或Web应用程序中调用。它集成了各种组件,并依赖于app.config或web.config。
如果我想要从脚本(例如IronPython)利用这个类库,我该如何让脚本使用config文件呢?理想情况下,我希望能在运行脚本时选择配置文件,或者按照约定(将配置文件放在脚本文件旁边)。
如果可能的话,我不想更改ipy.exe.config,因为这样做在多个配置情况下无法扩展,必须拥有IronPython的多个副本。有没有其他替代方案?
我有一个类库,通常从.NET控制台或Web应用程序中调用。它集成了各种组件,并依赖于app.config或web.config。
如果我想要从脚本(例如IronPython)利用这个类库,我该如何让脚本使用config文件呢?理想情况下,我希望能在运行脚本时选择配置文件,或者按照约定(将配置文件放在脚本文件旁边)。
如果可能的话,我不想更改ipy.exe.config,因为这样做在多个配置情况下无法扩展,必须拥有IronPython的多个副本。有没有其他替代方案?
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)
System.Configuration.ConfigurationManager
类。更具体地说,OpenMappedExeConfiguration
方法将允许您加载任何 .config 文件。这将为您提供一个 Configuration
对象,该对象公开了标准的 AppSettins、ConnectionStrings、SectionGroups 和 Sections 属性。您可以在配置文件中始终包含其他部分。在您的ipy.exe.config文件中,您可以添加一个包含以导入外部配置设置; 比如myApp.config。
在批处理/命令文件中,您可以始终将特定的.config集合复制到myApp.config中,因此可以根据需要使用不同的配置文件运行。
请查看此博客以了解如何实现此目标; http://weblogs.asp.net/pwilson/archive/2003/04/09/5261.aspx
我尝试按照上面的答案操作,但发现太过复杂。如果你确切知道你需要从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属性。
为了解决问题,我所做的工作是“手动”填充ConfigurationManager静态类的AppSettings集合。所以我创建了一个PY脚本,在IronPython上运行“import”命令,然后设置将可用于类库。然而,我无法将值分配给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 类中的方法会很好。
将这篇博客文章翻译成Python,应该可以这样做:
import clr
import System.AppDomain
System.AppDomain.CurrentDomain.SetData(“APP_CONFIG_FILE”, r”c:\your\app.config”)
import System
而不是import System.AppDomain
) - Macke