2013-02-05 13 views
14

Tôi đã tạo phần cấu hình tùy chỉnh cho plugin DLL lưu trữ XML .config trong tệp riêng biệt (từ tệp ứng dụng chính).Phần cấu hình tùy chỉnh .NET: Configuration.GetSection ném 'không thể định vị assembly' ngoại lệ

Dưới đây là một mẫu của các lớp phần tùy chỉnh:

using System; 
using System.Configuration; 

namespace PluginFramework.MyConfiguration 
{ 

public class MyConfigurationSettings : ConfigurationSection 
{ 
    private Configuration _Config = null; 

    #region ConfigurationProperties  
    /// <summary> 
    /// A custom XML section for an application's configuration file. 
    /// </summary> 
    [ConfigurationProperty("MyProjects", IsDefaultCollection = true)] 
    public MyProjectConfigurationCollection MyProjects 
    { 
     get { return (MyProjectConfigurationCollection) base["MyProjects"]; } 
    } 

    // ... 
    #endregion 

    /// <summary> 
    /// Private Constructor used by our factory method. 
    /// </summary> 
    private MyConfigurationSettings() : base() { 
     // Allow this section to be stored in user.app. By default this is forbidden. 
     this.SectionInformation.AllowExeDefinition = 
     ConfigurationAllowExeDefinition.MachineToLocalUser; 
    } 

    // ... 

    #region Static Members 
    /// <summary> 
    /// Gets the current applications &lt;MyConfigurationSettings&gt; section. 
    /// </summary> 
    /// <param name="ConfigLevel"> 
    /// The &lt;ConfigurationUserLevel&gt; that the config file 
    /// is retrieved from. 
    /// </param> 
    /// <returns> 
    /// The configuration file's &lt;MyConfigurationSettings&gt; section. 
    /// </returns> 
    public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel) 
    { 
     string appDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 
     string localDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 
     System.Configuration.ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap(); 
     exeMap.ExeConfigFilename = System.IO.Path.Combine(appDataPath, @"MyCompany\MyPluginApp\Default.config"); 
     exeMap.RoamingUserConfigFilename = System.IO.Path.Combine(appDataPath, @"MyCompany\MyPluginApp\Roaming.config"); 
     exeMap.LocalUserConfigFilename = System.IO.Path.Combine(localDataPath, @"MyCompany\MyPluginApp\Local.config"); 

     System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(exeMap,ConfigLevel); 
     MyConfigurationSettings myConfigurationSettings = null; 

     try { 
      myConfigurationSettings = (MyConfigurationSettings)Config.GetSection("MyConfigurationSettings"); 
     } 
     catch (System.Exception ex) { 
      // ConfigurationErrorsException caught here ... 
     } 
     if (myConfigurationSettings == null) { 
      myConfigurationSettings = new MyConfigurationSettings(); 
      Config.Sections.Add("MyConfigurationSettings", myConfigurationSettings);     } 
     } 
     if(myConfigurationSettings != null) { 
      myConfigurationSettings._Config = Config; 
     } 

     return myConfigurationSettings; 
    }  
    #endregion 
} 
} // PluginFramework.MyConfiguration 

XML .config tạo ra khi tiết kiệm thời gian 1 trông như thế này:

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <configSections> 
     <!-- The exception complains about the following line (assembly attributes are compliant): --> 
     <section name="MyConfigurationSettings" type="PluginFramework.MyConfiguration.MyConfigurationSettings, PluginFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowDefinition="Everywhere" allowExeDefinition="MachineToLocalUser" /> 
    </configSections> 
    <MyConfigurationSettings> 
     <!-- Config properties are serialized fine according MyConfigurationSettings 
      properties marked with the ConfigurationProperty attribute ... --> 
     <MyProjects> 
      <MyProjectConfiguration GUID="{4307AC92-8180-4686-9322-830312ED59AB}"> 
       <!-- ... more complex configuration elements --> 
      </MyProjectConfiguration> 
     </MyProjects> 
    </MyConfigurationSettings> 
</configuration> 

Khi XML này đang cố gắng để được nạp bằng Config.GetSection() trên các lần chạy tiếp theo, tôi bắt một số ConfigurationErrorsException tại dòng được đánh dấu trong mẫu XML, cho biết rằng assembly MyPlugin hoặc một trong các phụ thuộc của nó không thể được định vị (xin vui lòng tha thứ rằng tôi không đăng ngoại lệ gốc mes hiền nhân, nhưng tôi có nó chỉ bằng tiếng Đức, và nghi ngờ văn bản này sẽ hữu ích ở đây). Ngoại lệ bên trong xuất phát từ System.IO trong khi cố gắng tải assembly và lấy phản xạ để giải quyết kiểu lớp 'MyConfigurationSettings'.

Để chính xác tình hình, mã từ trên được đặt bên trong một khuôn khổ DLL (lắp ráp), do đó được tham chiếu bởi plugin DLL thực tế được tải từ ứng dụng chính.

Sơ đồ UML sau đây minh họa mối quan hệ với một số thành phần: Main App plugin and framework components

Sau khi nhìn xung quanh một chút về vấn đề này, tôi có cảm giác đó là cần thiết để tên mạnh (dấu hiệu) lắp ráp xuất khẩu lớp MyConfigurationSettings (tức PluginFramework) và đăng ký nó với GAC. Tôi đã không thử điều này được nêu ra, và muốn tránh bước này vì nhiều lý do (trước khi biết nếu nó thậm chí có thể giúp đỡ và đó là sự lựa chọn duy nhất để giải quyết vấn đề).

Vì vậy, đây là những câu hỏi (xin lỗi tôi đặt 4 câu hỏi ở đây, nhưng chúng liên quan chặt chẽ đến mức không tạo ra các câu hỏi SO riêng biệt cho chúng).

  1. Tôi có thể giải quyết vấn đề lỗi định vị bằng cách đặt tên đúng cách lắp ráp được đề cập và đăng ký nó với GAC không?

  2. Ngẫu nhiên, quá trình lắp ráp quản lý cấu hình bị phàn nàn, được đảm bảo được tải (vì nó tự gọi là số Configuration.GetSection()).
    Có thể có cách nào để đăng ký lắp ráp hoặc loại cấu hình phù hợp với/hoặc serializers một cách rõ ràng với lớp học ConfigurationManager hoặc Confguration?

  3. Tôi cũng quan tâm đến thông tin thêm về Hans Passant's comment đề cập đến điều này có thể là một vấn đề gây ra bởi cách lắp ráp (chính) được tải từ ứng dụng chính. Tôi không có quyền kiểm soát cơ chế này, và nếu điều này gây ra hành vi này vốn dĩ tôi muốn biết liệu có cách giải quyết hợp lý không?

  4. Một ý tưởng khác (nếu bất cứ điều gì ở trên không hiển thị một cách) là hoàn toàn quản lý định dạng XML cấu hình nguyên bản (sử dụng hỗ trợ xê-ri XML) và từ nơi tải và hợp nhất các tệp cấu hình. Nếu đây là lựa chọn thích hợp nhất, bất cứ ai có thể cung cấp cho con trỏ tốt cách thực hiện điều này một cách hiệu quả (ít nhất là mã cần thiết để quản lý đường dẫn và hợp nhất)?

Cập nhật:
Vì không ai có vẻ để có thể cung cấp cho cái nhìn sâu sắc hơn về câu hỏi này (s) (2 câu trả lời không thực sự làm cho tôi thêm), tôi thay đổi để lựa chọn từ 4., làm tất cả bằng tay.

+1

Một điều tôi cho bạn biết chắc chắn - nó không cần phải ở trong GAC. Trước tiên, hãy đảm bảo rằng Trình xử lý mục của bạn sống trong cùng thư mục với phần còn lại của mã. Thứ hai, hãy đảm bảo bạn sử dụng trình quản lý cấu hình phù hợp. Thứ ba, hãy chắc chắn rằng bạn có tất cả {} tương ứng với các thuộc tính trong "MyConfigurationSettings" của bạn. Đây là bắt đầu ... Và thứ tư! Nhìn vào lớp của bạn ... Tôi không nghĩ bạn sử dụng nó đúng. Bạn cần tài sản ở đó, không phải "GetSection". Đây là toàn bộ ý tưởng mà bạn gọi GetSection và khung tìm thấy bạn phần xử lý bên phải, nơi bạn chỉ phải làm tài sản -call –

+1

Nếu ứng dụng chính được đặt tên mạnh thì bất kỳ assembly nào nó tham chiếu cũng phải được đặt tên mạnh. – groverboy

+0

@ T.S. Âm thanh đầy hứa hẹn! Hãy xem một số chỉnh sửa để cải thiện câu hỏi. DLL framework được đề cập nằm trong cùng thư mục với plugin DLL, nhưng không nằm trong cùng thư mục với ứng dụng chính có thể thực thi được. Tôi nghĩ rằng điểm của bạn về các thuộc tính cấu hình có thể không liên quan (ít nhất là cho ngoại lệ đặc biệt này, nó phàn nàn chính xác về dòng tôi đã đánh dấu trong XML .config). Về việc sử dụng phương thức 'GetSection()', cái này chỉ dành cho thuận tiện cho việc truy cập vào phần cụ thể này, tôi không biết tôi có thể thay thế cái này bằng bất động sản ở đâu ... –

Trả lời

4

Tôi đã thử điều đó, nhưng tôi chưa bao giờ làm việc đó như thế. Tôi chỉ cần tải một .config tự động không hoạt động cho. Dll chỉ dành cho .exe's. Sau đó, tôi đã từ bỏ và quyết định sẽ dễ dàng hơn khi tải tệp .config theo cách thủ công. Bạn có thể xem mã đầy đủ ở đây: https://github.com/GeertBellekens/Enterprise-Architect-Toolpack/blob/master/EANavigator/NavigatorSettings.cs Đây là phần phù hợp nhất:

public NavigatorSettings() { 
    Configuration roamingConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming); 

    // the roamingConfig now get a path such as C:\Users\<user>\AppData\Roaming\Sparx_Systems_Pty_Ltd\DefaultDomain_Path_2epjiwj3etsq5yyljkyqqi2yc4elkrkf\9,_2,_0,_921\user.config 
    // which I don't like. So we move up three directories and then add a directory for the EA Navigator so that we get 
    // C:\Users\<user>\AppData\Roaming\GeertBellekens\EANavigator\user.config 
    string configFileName = System.IO.Path.GetFileName(roamingConfig.FilePath); 
    string configDirectory = System.IO.Directory.GetParent(roamingConfig.FilePath).Parent.Parent.Parent.FullName; 

    string newConfigFilePath = configDirectory + @"\Geert Bellekens\EANavigator\" + configFileName; 
    // Map the roaming configuration file. This 
    // enables the application to access 
    // the configuration file using the 
    // System.Configuration.Configuration class 
    ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap(); 
    configFileMap.ExeConfigFilename = newConfigFilePath;  

    // Get the mapped configuration file. 
    currentConfig = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None); 
    // merge the default settings 
    this.mergeDefaultSettings(); 
} 

Truy cập vào một tài sản cấu hình:

public bool trackSelectedElement 
{ 
    get { 
     bool result; 
     if(bool.TryParse(this.currentConfig.AppSettings.Settings["trackSelectedElement"].Value, out result)) { 
      return result; 
     } 
     else { 
      return true; 
     } 
    } 
    set { 
     this.currentConfig.AppSettings.Settings["trackSelectedElement"].Value = value.ToString(); 
    } 
} 
+0

Cảm ơn rất nhiều! Tôi sẽ cố gắng ... Vẫn tò mò về hành vi kỳ lạ ... –

+0

Hiện không có thời gian. Sau đó sẽ có được ví dụ thực tế về cuộc sống. –

+0

Vì vậy, từ tiếp tục nghiên cứu mã của bạn, tôi thấy bạn tránh gọi 'Configuration.GetSection()' ở tất cả và thực hiện ánh xạ thuộc tính từ (các) cấu hình được nạp và hợp nhất bằng tay (theo nghĩa đen). Tôi đã thêm mẫu truy cập thuộc tính vào câu trả lời của bạn. Nhưng tôi có một số thuộc tính phức tạp trong 'ConfigurationSettings' tùy chỉnh của tôi (có nguồn gốc từ' ConfigurationElementCollection' và 'ConfigurationElement'), tôi sẽ phải xem cách xử lý những ... –

0

@ g-makulik

Ở đây tôi có một lao động bản sao của những gì đã được thực hiện trong môi trường thực tế và được chứng minh là có hiệu quả.

Trong App.config

<configSections> 
    <sectionGroup name="mySectionGroupName"> 
     <section name="mySectionName" type="MyNamespace.MySectionHandler,MyNamespace" /> 
    </sectionGroup> 
</configSections> 
.... 
<mySectionGroupName> 
    <mySectionName> 
     <add key="MyKey" value="MyKeyValue" /> 
    </mySectionName> 
</mySectionGroupName> 

Trong lớp học, nơi bạn sử dụng cấu hình:

.... 
Hashtable ht = ConfigurationManager.GetSection("mySectionGroupName/mySectionName") as Hashtable; 
// when you call this, your handler will do what you want in there 
string keyVal = ht["MyKey"] as String; 
.... 

Lớp chịu trách nhiệm xử lý cấu hình:

public class MySectionHandler : DictionarySectionHandler 
{ 
    public override object Create(object parent, object context, XmlNode section) 
    { 
     // here do what you want with the value of "MyKey" - "MyKeyValue" 
    } 
} 

Tôi hy vọng, điều này giúp

+0

Cảm ơn bạn đã lấy mẫu, tôi sẽ phải thử ... –

+0

Hmmmm, tôi không đề cập đến tệp cấu hình ứng dụng (chính/có thể thực thi), nhưng muốn tạo riêng cho ứng dụng plugin và quản lý nó một cách riêng biệt (hãy xem mẫu sơ đồ thành phần mà tôi đã đăng). Sử dụng các nhóm phần trong cấu hình ứng dụng chính có thể là một giải pháp, nhưng không thực sự là những gì tôi đang tìm kiếm. –

+0

Điều đầu tiên tôi đã thử là điều chỉnh thuộc tính 'type' của phần tử 'section' mà không có công cụ' Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null', nhưng điều đó không giúp ích gì. Tôi không chắc chắn viết lại mã của tôi cho một 'DictionarySectionHandler' sẽ thực sự giải quyết vấn đề tải lắp ráp của các lớp' Configuation'/'ConfigurationManager'. –

1

Những gì bạn đang cố gắng làm không được hỗ trợ bởi .NET Framework.

1st - nó làm cho cảm giác rằng plugin.dll bạn được cấu hình cho mỗi ứng dụng máy chủ (.exe hoặc web) được sử dụng nó (mà tại sao nó là cấu hình)

2 - tập tin cấu hình hỗ trợ thừa kế (ví dụ: máy. config -> applicationHost.config -> web.config). Đó là những gì họ được thiết kế cho. Cấu hình off-the-path của bạn sẽ không hoạt động đúng theo khía cạnh đó.

Vì vậy, nếu bạn cần cấu hình tùy chỉnh cho một phần của ứng dụng hoặc plugin, không tuân theo các khái niệm .config, tạo tệp XML chuẩn hoặc jsonconfig và cài đặt tải từ đó.

+0

Vâng, có vẻ như đây là những sự kiện, nhưng hãy xem các bình luận của tôi trả lời cho @giammin. Hiện tại tôi đang cố gắng làm việc với việc sử dụng lại các lớp cơ sở từ không gian tên 'System.Configuration' và thực thi tuần tự hóa XML của riêng tôi với các thay thế cho các lớp' ConfigurationManager' và 'Configuration'. Cuối cùng tôi sẽ đăng giải pháp ở đây, nếu tôi làm nó như mong muốn. –

+0

Đó là điểm chính xác của tôi. Bạn sẽ chỉ sử dụng System.Configuration cho mục đích thẩm mỹ. Nó sẽ không hoạt động như app.config dự kiến ​​sẽ hoạt động và nó có thể gây nhầm lẫn cho người dùng plugin của bạn.Và bạn đang bổ sung thêm chi phí cho việc triển khai của mình mà không có lý do chính đáng. Thay vào đó hãy tạo POCO và tuần tự hóa thành XML (+ XTD nếu bạn cần intellisense), hoặc tuần tự hóa thành JSON. – Nenad

0

Tôi gặp vấn đề tương tự và chưa tìm thấy giải pháp hoàn toàn thỏa mãn. Tải ứng dụng của chúng tôi được biên dịch với tham chiếu đến một lớp phần cấu hình được xác định trong một hội đồng chuyên dụng. Ứng dụng này được liên kết với một hội đồng được đặt tên mạnh nhưng sau đó khi trình tải cấu hình cố gắng đọc cấu hình, các dấu vết kết hợp cho thấy rằng nó cố tải một tên yếu của cùng một assembly. Vì một số lý do, .net không thấy rằng nó là cùng một assembly và ném ra một ngoại lệ System.IO.FileNotFound. Về giải pháp làm việc trong trường hợp của tôi là tham chiếu tên mạnh trong cấu hình.

Tôi cũng nhận thấy một hành vi lạ: Sau khi lắp ráp được tải bởi bộ nạp cấu hình .net với tên mạnh, tham chiếu thêm với tên yếu thực sự thành công! Vì một lý do nào đó, khuôn khổ "ghi nhớ" rằng tên yếu sẽ thu lại cùng một assembly.

Mọi tin tức từ sự cố về phía bạn sẽ rất thú vị!

+0

Tôi chỉ đơn giản là từ bỏ điều này và đã chọn để có các định dạng tập tin cấu hình của riêng tôi cho lắp ráp của tôi. –

0

Thêm trình xử lý sự kiện cho AppDomain.CurrentDomain.AssemblyResolve. Điều này sẽ hoạt động cho option 2.