2010-10-14 27 views
133

Tôi đang cố triển khai phần cấu hình tùy chỉnh trong một dự án và tôi tiếp tục chạy lên chống lại các ngoại lệ mà tôi không hiểu. Tôi hy vọng ai đó có thể điền vào chỗ trống ở đây.Cách triển khai ConfigurationSection bằng ConfigurationElementCollection

Tôi có App.config trông như thế này:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
     <section name="ServicesSection" type="RT.Core.Config.ServicesConfigurationSectionHandler, RT.Core"/> 
    </configSections> 
    <ServicesSection type="RT.Core.Config.ServicesSection, RT.Core"> 
      <Services> 
       <AddService Port="6996" ReportType="File" /> 
       <AddService Port="7001" ReportType="Other" /> 
      </Services> 
     </ServicesSection> 
</configuration> 

Tôi có một yếu tố ServiceConfig được xác định như sau:

public class ServiceConfig : ConfigurationElement 
    { 
    public ServiceConfig() {} 

    public ServiceConfig(int port, string reportType) 
    { 
     Port = port; 
     ReportType = reportType; 
    } 

    [ConfigurationProperty("Port", DefaultValue = 0, IsRequired = true, IsKey = true)] 
    public int Port 
    { 
     get { return (int) this["Port"]; } 
     set { this["Port"] = value; } 
    } 

    [ConfigurationProperty("ReportType", DefaultValue = "File", IsRequired = true, IsKey = false)] 
    public string ReportType 
    { 
     get { return (string) this["ReportType"]; } 
     set { this["ReportType"] = value; } 
    } 
    } 

Và tôi có một ServiceCollection được xác định như sau:

public class ServiceCollection : ConfigurationElementCollection 
    { 
    public ServiceCollection() 
    { 
     Console.WriteLine("ServiceCollection Constructor"); 
    } 

    public ServiceConfig this[int index] 
    { 
     get { return (ServiceConfig)BaseGet(index); } 
     set 
     { 
     if (BaseGet(index) != null) 
     { 
      BaseRemoveAt(index); 
     } 
     BaseAdd(index, value); 
     } 
    } 

    public void Add(ServiceConfig serviceConfig) 
    { 
     BaseAdd(serviceConfig); 
    } 

    public void Clear() 
    { 
     BaseClear(); 
    } 

    protected override ConfigurationElement CreateNewElement() 
    { 
     return new ServiceConfig(); 
    } 

    protected override object GetElementKey(ConfigurationElement element) 
    { 
     return ((ServiceConfig) element).Port; 
    } 

    public void Remove(ServiceConfig serviceConfig) 
    { 
     BaseRemove(serviceConfig.Port); 
    } 

    public void RemoveAt(int index) 
    { 
     BaseRemoveAt(index); 
    } 

    public void Remove(string name) 
    { 
     BaseRemove(name); 
    } 
    } 

Phần tôi thiếu là phải làm gì cho người xử lý. Ban đầu, tôi đã cố gắng để thực hiện một IConfigurationSectionHandler nhưng thấy hai điều:

  1. nó đã không làm việc
  2. nó bị phản đối.

Tôi hoàn toàn bị mất ngay bây giờ về những việc cần làm để tôi có thể đọc dữ liệu của mình từ cấu hình. Xin vui lòng giúp đỡ!

+0

tôi không thể làm việc này. Tôi rất muốn xem RT.Core.Config.ServicesSection. Tôi chỉ nhận được phần tử không được công nhận 'AddService' mặc dù sử dụng mã từ câu trả lời được chấp nhận. – sirdank

+0

Tôi cũng bỏ lỡ điều này lúc đầu - phần này: [ConfigurationCollection (typeof (ServiceCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")] AddItemName phải khớp nếu bạn thay đổi "add" thành "addService" nó sẽ hoạt động – HeatherD

Trả lời

167

Câu trả lời trước là chính xác nhưng tôi cũng sẽ cung cấp cho bạn tất cả mã.

app.config của bạn sẽ trông như thế này:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
     <section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/> 
    </configSections> 
    <ServicesSection> 
     <Services> 
     <add Port="6996" ReportType="File" /> 
     <add Port="7001" ReportType="Other" /> 
     </Services> 
    </ServicesSection> 
</configuration> 

bạn ServiceConfigServiceCollection lớp vẫn không thay đổi.

Bạn cần một lớp học mới:

public class ServiceConfigurationSection : ConfigurationSection 
{ 
    [ConfigurationProperty("Services", IsDefaultCollection = false)] 
    [ConfigurationCollection(typeof(ServiceCollection), 
     AddItemName = "add", 
     ClearItemsName = "clear", 
     RemoveItemName = "remove")] 
    public ServiceCollection Services 
    { 
     get 
     { 
     return (ServiceCollection)base["Services"]; 
     } 
    } 
} 

Và đó nên làm các trick.Để tiêu thụ nó, bạn có thể sử dụng:

ServiceConfigurationSection serviceConfigSection = 
    ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection; 

ServiceConfig serviceConfig = serviceConfigSection.Services[0]; 
+8

Thuộc tính '[Add | Remove | Clear] ItemName' trên thuộc tính' ConfigurationCollection' không thực sự cần thiết trong trường hợp này, bởi vì "add"/"clear"/"remove "đã là tên mặc định của các phần tử XML. –

+2

Tôi làm cách nào để nó hoạt động để các thẻ không được thêm? Nó chỉ có vẻ hoạt động nếu chúng được thêm vào. Nó sẽ không hoạt động nếu hoặc JonathanWolfson

+7

@JonathanWolfson: chỉ cần thay đổi AddItemName = "add" thành AddItemName = "Dịch vụ" –

4

Thử kế thừa từ ConfigurationSection. Điều này blog post bởi Phil Haack có một ví dụ.

Xác nhận, mỗi tài liệu cho IConfigurationSectionHandler:

Trong .NET Framework phiên bản 2.0 trở lên, bạn thay vì phải xuất phát từ lớp ConfigurationSection để thực hiện xử lý phần cấu hình có liên quan.

43

Đây là mã chung cho bộ sưu tập cấu hình:

public class GenericConfigurationElementCollection<T> : ConfigurationElementCollection, IEnumerable<T> where T : ConfigurationElement, new() 
{ 
    List<T> _elements = new List<T>(); 

    protected override ConfigurationElement CreateNewElement() 
    { 
     T newElement = new T(); 
     _elements.Add(newElement); 
     return newElement; 
    } 

    protected override object GetElementKey(ConfigurationElement element) 
    { 
     return _elements.Find(e => e.Equals(element)); 
    } 

    public new IEnumerator<T> GetEnumerator() 
    { 
     return _elements.GetEnumerator(); 
    } 
} 

Sau khi bạn có GenericConfigurationElementCollection, bạn có thể sử dụng đơn giản nó trong phần cấu hình (đây là một ví dụ từ Dispatcher của tôi):

public class DispatcherConfigurationSection: ConfigurationSection 
{ 
    [ConfigurationProperty("maxRetry", IsRequired = false, DefaultValue = 5)] 
    public int MaxRetry 
    { 
     get 
     { 
      return (int)this["maxRetry"]; 
     } 
     set 
     { 
      this["maxRetry"] = value; 
     } 
    } 

    [ConfigurationProperty("eventsDispatches", IsRequired = true)] 
    [ConfigurationCollection(typeof(EventsDispatchConfigurationElement), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")] 
    public GenericConfigurationElementCollection<EventsDispatchConfigurationElement> EventsDispatches 
    { 
     get { return (GenericConfigurationElementCollection<EventsDispatchConfigurationElement>)this["eventsDispatches"]; } 
    } 
} 

Các Config Element là cấu hình đây:

Các tập tin cấu hình sẽ trông như thế này:

<?xml version="1.0" encoding="utf-8" ?> 
    <dispatcherConfigurationSection> 
    <eventsDispatches> 
     <add name="Log" ></add> 
     <add name="Notification" ></add> 
     <add name="tester" ></add> 
    </eventsDispatches> 
    </dispatcherConfigurationSection> 

Hy vọng nó sẽ giúp!

+0

Tuyệt! Nghĩ về điều tương tự và thấy rằng tôi không đơn độc. Muốn MS thực hiện điều đó cho tất cả các cấu hình FCL – abatishchev

+0

Bất kỳ đề xuất nào về cách thực hiện điều đó với Bản đồ cơ bản cho các mục? Tôi không muốn thực hiện Thêm nếu tôi có thể tránh nó. – SpaceCowboy74

61

Nếu bạn đang tìm kiếm một phần cấu hình tùy chỉnh như sau

<CustomApplicationConfig> 
     <Credentials Username="itsme" Password="mypassword"/> 
     <PrimaryAgent Address="10.5.64.26" Port="3560"/> 
     <SecondaryAgent Address="10.5.64.7" Port="3570"/> 
     <Site Id="123" /> 
     <Lanes> 
      <Lane Id="1" PointId="north" Direction="Entry"/> 
      <Lane Id="2" PointId="south" Direction="Exit"/> 
     </Lanes> 
</CustomApplicationConfig> 

sau đó bạn có thể sử dụng thực hiện của tôi về phần cấu hình như vậy để bắt đầu thêm tài liệu tham khảo System.Configuration lắp ráp để dự án của bạn

Nhìn vào mỗi phần tử lồng nhau mà tôi đã sử dụng, Đầu tiên là Thông tin xác thực có hai thuộc tính để cho phép thêm nó trước tiên

Credentials Element

public class CredentialsConfigElement : System.Configuration.ConfigurationElement 
    { 
     [ConfigurationProperty("Username")] 
     public string Username 
     { 
      get 
      { 
       return base["Username"] as string; 
      } 
     } 

     [ConfigurationProperty("Password")] 
     public string Password 
     { 
      get 
      { 
       return base["Password"] as string; 
      } 
     } 
    } 

PrimaryAgent và SecondaryAgent

Cả hai có các thuộc tính giống nhau và có vẻ như một địa chỉ để một tập hợp các máy chủ cho một tiểu học và một failover, vì vậy bạn chỉ cần tạo một lớp yếu tố cho cả của những người như sau

public class ServerInfoConfigElement : ConfigurationElement 
    { 
     [ConfigurationProperty("Address")] 
     public string Address 
     { 
      get 
      { 
       return base["Address"] as string; 
      } 
     } 

     [ConfigurationProperty("Port")] 
     public int? Port 
     { 
      get 
      { 
       return base["Port"] as int?; 
      } 
     } 
    } 

Tôi sẽ giải thích cách sử dụng hai phần tử khác nhau với một lớp sau này, hãy bỏ qua SiteId vì không có sự khác biệt trong đó. Bạn chỉ cần tạo một lớp giống như trên với một thuộc tính duy nhất. chúng ta hãy xem làm thế nào để thực hiện Lanes bộ sưu tập

nó được tách ra thành hai phần đầu tiên bạn phải tạo một lớp thực hiện yếu tố sau đó bạn phải tạo lớp yếu tố thu

LaneConfigElement

public class LaneConfigElement : ConfigurationElement 
    { 
     [ConfigurationProperty("Id")] 
     public string Id 
     { 
      get 
      { 
       return base["Id"] as string; 
      } 
     } 

     [ConfigurationProperty("PointId")] 
     public string PointId 
     { 
      get 
      { 
       return base["PointId"] as string; 
      } 
     } 

     [ConfigurationProperty("Direction")] 
     public Direction? Direction 
     { 
      get 
      { 
       return base["Direction"] as Direction?; 
      } 
     } 
    } 

    public enum Direction 
    { 
     Entry, 
     Exit 
    } 

bạn có thể nhận thấy rằng một thuộc tính của LanElement là một liệt kê và nếu bạn cố gắng sử dụng bất kỳ giá trị nào khác trong cấu hình không được xác định trong ứng dụng liệt kê sẽ ném System.Configuration.ConfigurationErrorsException khi khởi động. Ok cho phép chuyển sang Định nghĩa Tập hợp

[ConfigurationCollection(typeof(LaneConfigElement), AddItemName = "Lane", CollectionType = ConfigurationElementCollectionType.BasicMap)] 
    public class LaneConfigCollection : ConfigurationElementCollection 
    { 
     public LaneConfigElement this[int index] 
     { 
      get { return (LaneConfigElement)BaseGet(index); } 
      set 
      { 
       if (BaseGet(index) != null) 
       { 
        BaseRemoveAt(index); 
       } 
       BaseAdd(index, value); 
      } 
     } 

     public void Add(LaneConfigElement serviceConfig) 
     { 
      BaseAdd(serviceConfig); 
     } 

     public void Clear() 
     { 
      BaseClear(); 
     } 

     protected override ConfigurationElement CreateNewElement() 
     { 
      return new LaneConfigElement(); 
     } 

     protected override object GetElementKey(ConfigurationElement element) 
     { 
      return ((LaneConfigElement)element).Id; 
     } 

     public void Remove(LaneConfigElement serviceConfig) 
     { 
      BaseRemove(serviceConfig.Id); 
     } 

     public void RemoveAt(int index) 
     { 
      BaseRemoveAt(index); 
     } 

     public void Remove(String name) 
     { 
      BaseRemove(name); 
     } 

    } 

bạn có thể nhận thấy rằng tôi đã thiết lập các AddItemName = "Lane" bạn có thể chọn bất cứ điều gì bạn thích cho mục nhập bộ sưu tập của bạn, tôi thích sử dụng "thêm" một mặc định, nhưng tôi đã thay đổi nó chỉ vì lợi ích của bài đăng này.

Bây giờ tất cả các yếu tố lồng nhau của chúng tôi đã được thực hiện tại chúng ta nên tổng hợp tất cả những người trong một lớp mà phải thực hiện System.Configuration.ConfigurationSection

CustomApplicationConfigSection

public class CustomApplicationConfigSection : System.Configuration.ConfigurationSection 
    { 
     private static readonly ILog log = LogManager.GetLogger(typeof(CustomApplicationConfigSection)); 
     public const string SECTION_NAME = "CustomApplicationConfig"; 

     [ConfigurationProperty("Credentials")] 
     public CredentialsConfigElement Credentials 
     { 
      get 
      { 
       return base["Credentials"] as CredentialsConfigElement; 
      } 
     } 

     [ConfigurationProperty("PrimaryAgent")] 
     public ServerInfoConfigElement PrimaryAgent 
     { 
      get 
      { 
       return base["PrimaryAgent"] as ServerInfoConfigElement; 
      } 
     } 

     [ConfigurationProperty("SecondaryAgent")] 
     public ServerInfoConfigElement SecondaryAgent 
     { 
      get 
      { 
       return base["SecondaryAgent"] as ServerInfoConfigElement; 
      } 
     } 

     [ConfigurationProperty("Site")] 
     public SiteConfigElement Site 
     { 
      get 
      { 
       return base["Site"] as SiteConfigElement; 
      } 
     } 

     [ConfigurationProperty("Lanes")] 
     public LaneConfigCollection Lanes 
     { 
      get { return base["Lanes"] as LaneConfigCollection; } 
     } 
    } 

Bây giờ bạn có thể thấy rằng chúng ta có hai các thuộc tính có tên PrimaryAgentSecondaryAgent cả hai đều có cùng một loại bây giờ, bạn có thể dễ dàng hiểu tại sao chúng tôi chỉ có một lớp triển khai đối với hai phần tử này.

Trước khi bạn có thể sử dụng phần cấu hình mới được phát minh này trong app.config (hoặc web.config) của bạn, bạn chỉ cần cho bạn biết rằng bạn đã phát minh ra phần cấu hình của riêng bạn và tôn trọng, để thêm các dòng sau vào app.config (có thể ngay sau khi bắt đầu thẻ gốc).

<configSections> 
    <section name="CustomApplicationConfig" type="MyNameSpace.CustomApplicationConfigSection, MyAssemblyName" /> 
    </configSections> 

LƯU Ý: MyAssemblyName nên không .dll ví dụ nếu bạn lắp ráp tập tin tên là myDll.dll sau đó sử dụng mydll thay vì myDll.dll

để lấy sử dụng cấu hình này sau dòng mã bất kỳ nơi nào trên ứng dụng của bạn

CustomApplicationConfigSection config = System.Configuration.ConfigurationManager.GetSection(CustomApplicationConfigSection.SECTION_NAME) as CustomApplicationConfigSection; 

Tôi hy vọng ở trên bài sẽ giúp bạn bắt đầu với một loại phần cấu hình tùy chỉnh phức tạp.

Chúc mừng Mã hóa :)

**** Sửa **** Để Enable LINQ trên LaneConfigCollection bạn phải thực hiện IEnumerable<LaneConfigElement>

Và Thêm sau thi hành GetEnumerator

public new IEnumerator<LaneConfigElement> GetEnumerator() 
     { 
      int count = base.Count; 
      for (int i = 0; i < count; i++) 
      { 
       yield return base.BaseGet(i) as LaneConfigElement; 
      } 
     } 

cho những người vẫn còn bối rối về cách năng suất thực sự hoạt động được đọc this nice article

Hai điểm chính được lấy từ bài viết trên là

nó không thực sự chấm dứt thực thi phương pháp. lợi nhuận mang lại tạm dừng thực hiện phương thức và lần tiếp theo bạn gọi nó (cho giá trị liệt kê tiếp theo), phương pháp sẽ tiếp tục thực hiện từ cuộc gọi trả về lợi nhuận cuối cùng. Nghe có vẻ hơi khó hiểu Tôi nghĩ ... (Shay Friedman)

Lợi nhuận không phải là tính năng của thời gian chạy .Net. Nó chỉ là một ngôn ngữ C# tính năng được biên dịch thành mã IL đơn giản bởi trình biên dịch C#. (Lars Corneliussen)

+1

Cảm ơn bạn đã cung cấp một ví dụ hoàn chỉnh, điều này thực sự giúp ích rất nhiều! –

11

Một giải pháp thay thế dễ dàng hơn cho những người không muốn viết tất cả cấu hình đó theo cách thủ công ...

1) Cài đặt Nerdle.AutoConfig từ NuGet

2) Xác định loại ServiceConfig của bạn (hoặc một lớp bê tông hoặc chỉ là một giao diện, hoặc sẽ làm)

public interface IServiceConfiguration 
{ 
    int Port { get; } 
    ReportType ReportType { get; } 
} 

3) Bạn sẽ cần một loại để giữ bộ sưu tập, ví dụ

public interface IServiceCollectionConfiguration 
{ 
    IEnumerable<IServiceConfiguration> Services { get; } 
} 

4) Thêm phần cấu hình như vậy (camelCase đặt tên note)

<configSections> 
    <section name="serviceCollection" type="Nerdle.AutoConfig.Section, Nerdle.AutoConfig"/> 
</configSections> 

<serviceCollection> 
    <services> 
    <service port="6996" reportType="File" /> 
    <service port="7001" reportType="Other" /> 
    </services> 
</serviceCollection> 

5) Bản đồ với AutoConfig

var services = AutoConfig.Map<IServiceCollectionConfiguration>(); 
+1

Cảm ơn Chúa vì câu trả lời này – Svend

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