2009-02-15 41 views
21

Tôi có một thư viện lớp thường được gọi từ bảng điều khiển .net hoặc ứng dụng web. Nó tích hợp với các thành phần khác nhau và dựa trên app.config hoặc web.config.Làm thế nào để sử dụng IronPython với App.Config?

Nếu tôi muốn sử dụng thư viện lớp từ tập lệnh (ví dụ: IronPython), làm cách nào tôi có thể lấy tập lệnh để sử dụng tệp cấu hình? Lý tưởng nhất là tôi muốn có thể chọn tệp cấu hình khi chạy tập lệnh hoặc theo quy ước (tệp cấu hình nằm cùng với tệp tập lệnh).

Tôi không muốn thay đổi ipy.exe.config nếu có thể vì điều này sẽ không mở rộng cho nhiều cấu hình mà không có nhiều bản sao của IronPython?

Bất kỳ lựa chọn thay thế nào?

Trả lời

3

tôi có một giải pháp làm việc với mẫu mã Xem blog của tôi:. http://technomosh.blogspot.com/2012/01/using-appconfig-in-ironpython.html

Nó đòi hỏi một lớp proxy đặc biệt mà được tiêm vào ConfigurationManager.

Đây là nguồn cho các thư viện 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; 
     } 
    } 
} 

và ở đây là làm thế nào nó có thể được nạp từ Python:

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

from IronPythonUtilities import ConfigurationProxy 

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

Cuối cùng, một mẫu sử dụng:

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

Tôi đã tạo cài đặt ConfigProxy trong IronPython thuần túy dựa trên câu trả lời của bạn và các bài đăng trên blog có liên quan. Nó có sẵn tại http://www.software-architects.com/devblog/2012/10/29/appconfig-in-IronPython-without-additional-assemblies. –

+0

Simon - việc triển khai IronPython thuần túy thật tuyệt vời. Cảm ơn! – Moshe

+0

tôi biết rằng tôi có thể âm thanh vô lý, nhưng trong trường hợp của tôi, giải pháp "tiêm chích" tệp cấu hình của chúng tôi ở trên, không hoạt động cho phiên bản được biên dịch (sử dụng pyc.py) của tập lệnh IronPython. được rồi, thực ra không có lý do gì để ghi đè lên tập tin cấu hình, vì chúng ta có ứng dụng riêng của mình rồi và có thể có một cấu hình riêng - nó chỉ là bản thân tôi bị trộn lẫn, tôi đã phát triển với ipy.exe và phải sử dụng giải pháp trên, với phiên bản đã biên dịch và các cài đặt trên không hoạt động (?), các cài đặt chỉ được đọc khi tôi đặt tên cấu hình của mình theo các tiêu chuẩn đặt tên .net, ví dụ: myapp.exe.config –

0

Dịch this blog post vào Python, điều này sẽ làm việc:

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

Điều này dường như không tạo nên bất kỳ sự khác biệt nào –

+0

Bạn có chắc chắn gọi điều này trước khi thư viện cố gắng tải cài đặt của nó không? Cách dễ nhất để đảm bảo điều này sẽ là làm điều đó * trước khi * bạn clr.Thêm thư viện. – oefe

+1

Vâng ở đầu tập lệnh, nhưng không tạo ra sự khác biệt nào. –

0

Bạn luôn có thể bao gồm các phần thêm trong file config. Trong tệp ipy.exe.config của bạn, bạn có thể thêm một bao gồm để nhập các cài đặt cấu hình bên ngoài; nói myApp.config.

Trong tệp batch/lệnh, bạn luôn có thể sao chép trên một tệp .config cụ thể được đặt thành myApp.config và do đó chạy với các tệp cấu hình khác nhau theo yêu cầu.

Xem nhanh blog này về cách đạt được điều này; http://weblogs.asp.net/pwilson/archive/2003/04/09/5261.aspx

2

Bạn có thể xem lớp học System.Configuration.ConfigurationManager. Cụ thể hơn, phương thức OpenMappedExeConfiguration sẽ cho phép bạn tải bất kỳ tệp .config nào mà bạn chọn. Điều này sẽ cung cấp cho bạn một đối tượng Configuration hiển thị các thuộc tính AppSettins, ConnectionStrings, SectionGroups và Section.

Cách tiếp cận này yêu cầu bạn chuyển tên tệp cấu hình cho tập lệnh của mình dưới dạng đối số dòng lệnh hoặc để có logic mã để chọn tệp .config lúc chạy.

Tôi không biết Python, vì vậy tôi sẽ không cố gắng đăng mã mẫu. :-)

+1

Tôi đã xem xét điều này trước đây, nhưng lớp nghiệp vụ được tạo thành từ rất nhiều mô-đun bên thứ 3 khác nhau truy cập web.config một cách độc lập. Vì vậy, tôi thực sự cần phải đảm bảo rằng toàn bộ ứng dụng đang truy cập vào dữ liệu phù hợp. –

0

Để giải quyết những gì tôi đã làm là điền vào bộ sưu tập AppSettings cho lớp tĩnh ConfigurationManager "theo cách thủ công", vì vậy tôi đã tạo Tập lệnh PY và chạy "nhập" nó trên IronPython và các cài đặt sau đó sẽ khả dụng cho thư viện lớp. Tuy nhiên tôi couldn assing giá trị cho bộ sưu tập connectionStrings :(

kịch bản của tôi trông như thế này

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" 

Nó sẽ được tốt đẹp mặc dù biết một cách để "nạp" một tùy chỉnh config file để lớp ConfigurationManager.

+1

Lý do điều này sẽ không hoạt động trên bộ sưu tập ConnectionStrings là bởi vì bộ sưu tập này là chỉ đọc, do đó không thể sửa đổi thông qua mã. – Moshe

0

Tôi đã cố gắng làm theo các câu trả lời ở trên, nhưng thấy nó quá phức tạp. Nếu bạn biết chính xác thuộc tính nào bạn cần từ tệp App.config, thì bạn có thể đặt nó trực tiếp trong mã. Ví dụ, một dll tôi đã nhập khẩu cần thiết để biết thuộc tính AssemblyPath trong tệp App.Config của tôi.

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

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

Đây là tất cả những gì tôi cần và thư viện lớp tôi đã kết nối có thể xem thuộc tính AssemblyPath cần thiết để chạy.

Các vấn đề liên quan