Cách triển khai Cấu hình với Cấu hình Cấu hình


166

Tôi đang cố gắng thực hiện một phần cấu hình tùy chỉnh trong một dự án và tôi tiếp tục chạy theo 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.

Tôi có App.confighình 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 ServiceConfigyếu tố được định nghĩa như vậy:

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định nghĩa như vậy:

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 còn 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 IConfigurationSectionHandlernhưng tìm thấy hai điều:

  1. nó không hoạt động
  2. nó không được dùng nữa

Bây giờ tôi hoàn toàn không biết phải làm gì để tôi có thể đọc dữ liệu của mình từ cấu hình. Xin mọi người giúp đỡ!


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

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

Câu trả lời:


188

Câu trả lời trước là đúng nhưng tôi cũng sẽ cung cấp cho bạn tất cả cá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>

Của bạn ServiceConfigServiceCollectioncác 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à điều đó nên làm các mẹo. Để tiêu thụ nó, bạn có thể sử dụng:

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

ServiceConfig serviceConfig = serviceConfigSection.Services[0];

10
Các [Add|Remove|Clear]ItemNamethuộc tính trên ConfigurationCollectionthuộc tính không thực sự cần thiết trong trường hợp này, vì "thêm" / "xóa" / "xóa" đã là tên mặc định của các thành phần XML.
Wim Coenen

2
Làm thế nào tôi có thể làm cho nó hoạt động để các thẻ không được thêm vào? 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 đó là <Service Port = "6996" ReportType = "File" /> hoặc <Service Port = "7001" ReportType = "Other" />
JonathanWolfson

7
@JonathanWolfson: chỉ cần thay đổi AddItemName = "thêm" thành AddItemName = "Dịch vụ"
Mubashar

Đây có còn là cách tiếp cận cho .NET 4.5 không?
đè bẹp

6
@crush: có, không có nhiều thay đổi trong góc bụi này của .NET.
Russell McClure

84

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 phần triển khai cấu hình của tôi để bắt đầu thêm System.Configurationtham chiếu lắp ráp vào dự án của bạn

Nhìn vào từng yếu tố lồng nhau mà tôi đã sử dụng, Đầu tiên là Thông tin xác thực với hai thuộc tính vì vậy hãy thêm nó vào trước

Yếu tố thông tin

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

Tiểu học và Trung học

Cả hai đều có cùng thuộc tính và có vẻ giống như một Địa chỉ cho một bộ máy chủ cho chính và chuyển đổi dự phòng, vì vậy bạn chỉ cần tạo một lớp phần tử cho cả hai loạ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 yếu tố khác nhau với một lớp sau trong bài viết này, chúng ta hãy bỏ qua SiteId vì không có sự khác biệt trong nó. 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. Hãy cho chúng tôi xem cách triển khai bộ sưu tập Lanes

nó được chia thành hai phần trước tiên bạn phải tạo một lớp triển khai phần tử sau đó bạn phải tạo lớp phần tử bộ sưu tập

LaneConfigEuity

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 LanElementlà 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ẽ System.Configuration.ConfigurationErrorsExceptionkhởi động khi khởi động. Ok hãy chuyển sang Định nghĩa Bộ sưu tậ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 đã đặt AddItemName = "Lane"bạn có thể chọn bất cứ thứ 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ặc định nhưng tôi đã thay đổi nó chỉ vì mục đích của bài đăng này.

Bây giờ tất cả các phần tử lồng nhau của chúng ta đã được triển khai, chúng ta nên tổng hợp tất cả các phần tử trong một lớp 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 thuộc tính có tên PrimaryAgentSecondaryAgentcả hai đều có cùng loại bây giờ bạn có thể dễ dàng hiểu tại sao chúng ta chỉ có một lớp thực hiện chống lạ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), bạn chỉ cần cho ứng dụng 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 nó, để bạn phải thêm các dòng sau trong app.config (có thể ngay sau khi bắt đầu thẻ gốc).

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

LƯU Ý: MyAssuggingName không được có, ví dụ: nếu bạn tập hợp tên tệp là myDll.dll thì hãy sử dụng myDll thay vì myDll.dll

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

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

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

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

**** Chỉnh sửa **** Để bật LINQ, LaneConfigCollectionbạn phải thực hiệnIEnumerable<LaneConfigElement>

Và thêm sau khi thực hiện GetEnumerator

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

Đối với 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, hãy đọc bài viết hay này

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

nó không thực sự kết thúc việc thực hiện phương thức. return return tạm dừng thực thi phương thức và lần sau bạn gọi nó (đối với giá trị liệt kê tiếp theo), phương thức sẽ tiếp tục thực hiện từ lệnh gọi trả lại lợi suất cuối cùng. Nghe có vẻ hơi khó hiểu Tôi nghĩ rằng (Shay Friedman)

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


3
Cảm ơn đã cung cấp một ví dụ đầy đủ, điều này thực sự giúp ích rất nhiều!
John Leidegren

46

Đâ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 có GenericConfigurationElementCollection, bạn có thể sử dụng nó một cách đơn giản trong phần cấu hình (đây là một ví dụ từ Bộ điều phối 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"]; }
    }
}

Phần tử cấu hình được cấu hình ở đây:

public class EventsDispatchConfigurationElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
        get
        {
            return (string) this["name"];
        }
        set
        {
            this["name"] = value;
        }
    }
}

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!


Mát mẻ! Đã suy nghĩ giống nhau và thấy rằng tôi không cô đơn. Chúc MS thực hiện điều đó cho tất cả các cấu hình FCL
abatishchev

Có gợi ý nào về cách thực hiện điều đó với BasicMap cho các Mục không? Tôi không muốn thực hiện Thêm nếu tôi có thể tránh được.
SpaceCowboy74

28

Một cách khác dễ dàng hơn cho những người không muốn viết tất cả các cấu hình soạn sẵn bằng tay ...

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

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

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 (lưu ý đặt tên camelCase)

<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>();

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

Đối với những người chỉ muốn hoàn thành nó và không nhất thiết phải tạo mọi thứ từ đầu, đây là câu trả lời thực sự :)
CodeThief

5

Hãy thử kế thừa từ Cấu hình . Bài đăng blog này của Phil Haack có một ví dụ.

Xác nhận, theo tài liệu về IConfigurationSectionHandler :

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.