ASP.NET系列:自定義配置節點的複用

剛哥521發表於2015-12-16

appSettings太簡單,為每個程式自定義配置節點太複雜,因此要解決app.config&web.config自定義配置的複用問題。

1.讀取不依賴SectionName,根節點可以定義為任何名稱。

2.足夠簡單,配置項採用name value的形式;足夠複雜,採用樹型結構,每個節點都可以有多個配置項和子節點。

3.使用簡單,採用路徑簡化配置項的讀取。如: config.Get<string>("root.sub.item-test")。

一、呼叫方式:

1.配置檔案:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="node" type="Onion.Configuration.AppConfig.ConfigSection,Onion.Configuration" />
  </configSections>
  <node name="root">
    <items>
      <item name="version" value="1.0.0.1" />
    </items>
    <nodes>
      <node name="runtime">
        <items>
          <item name="debug" value="false" />
          <item name="ioc" value="IoC.Contianer.StructureMapIoC" />
        </items>
      </node>
      <node name="upload">
        <items>
          <item name="auth" value="true" />
          <item name="path" value="~/upload" />
          <item name="url" value="~/Upload/Index" />
        </items>
      </node>
      <node name="captcha">
        <items>
          <item name="timeout" value="3000" />
          <item name="url" value="~/Captcha/Index" />
        </items>
      </node>
      <node name="oauth2">
        <items>
          <item name="disabled" value ="false" />
          <item name="callback" value ="/Home/ExternalLoginCallBack?ProviderName=" />
        </items>
        <nodes>
          <node name="qqclient">
            <items>
              <item name="disabled" value="false" />
              <item name="method" value="get" />
              <item name="key" value="9233e24d" />
              <item name="secret" value="1ac35907-7cfa-4079-975c-959b98d23a95" />
            </items>
          </node>
          <node name="weiboclient">
            <items>
              <item name="disabled" value="true" />
              <item name="method" value="post" />
              <item name="key" value="0cdea8f3" />
              <item name="secret" value="dc679dbb-7e75-44f7-a99e-5359259fc94b" />
            </items>
          </node>
        </nodes>
      </node>
    </nodes>
  </node>
</configuration>

2.呼叫程式碼:

[Fact]
        public void Tests()
        {
            var config = new AppConfigAdapter();

            Assert.True(config.Get<string>("version") == "1.0.0.1");
            Assert.True(config.Get<bool>("runtime.debug") == false);
            Assert.True(config.Get<string>("runtime.ioc") == "IoC.Contianer.StructureMapIoC");
            Assert.True(config.Get<bool>("upload.auth") == true);
            Assert.True(config.Get<string>("upload.path") == "~/upload");
            Assert.True(config.Get<string>("upload.url") == "~/Upload/Index");
            Assert.True(config.Get<int>("captcha.timeout") == 3000);
            Assert.True(config.Get<string>("captcha.url") == "~/Captcha/Index");
            Assert.True(config.Get<bool>("oauth2.disabled") == false);
            Assert.True(config.Get<string>("oauth2.callback") == "/Home/ExternalLoginCallBack?ProviderName=");
            Assert.True(config.GetNode("oauth2").Nodes.Any(o => o.GetItem<bool>("disabled")));
            foreach (var node in config.GetNode("oauth2").Nodes)
            {
                if (node.Name == "qqclient")
                {
                    Assert.True(node.GetItem<bool>("disabled") == false);
                    Assert.True(node.GetItem<string>("method") == "get");
                    Assert.True(node.GetItem<string>("key") == "9233e24d");
                    Assert.True(node.GetItem<string>("secret") == "1ac35907-7cfa-4079-975c-959b98d23a95");
                }
                else if (node.Name == "weiboclient")
                {
                    Assert.True(node.GetItem<bool>("disabled") == true);
                    Assert.True(node.GetItem<string>("method") == "post");
                    Assert.True(node.GetItem<string>("key") == "0cdea8f3");
                    Assert.True(node.GetItem<string>("secret") == "dc679dbb-7e75-44f7-a99e-5359259fc94b");
                }
            }
        }

二、介面定義:

1.配置項定義:IItem介面定義最基礎的配置項,只包含Name和Value屬性。

public interface IItem
{
    string Name { get; set; }
 
    string Value { get; set; }
}

2.配置節點定義:INode介面定義了配置節點的樹形結構

public interface INode
{
    string Name { get; set; }
 
    IEnumerable<IItem> Items { get; set; }
 
    IEnumerable<INode> Nodes { get; set; }
 
    string GetItem(string itemName);
 
    T GetItem<T>(string itemName);
}

 

3.讀取介面定義:IConfig介面定義了配置節點和配置項的讀取

public interface IConfig
{
    INode GetNode(string nodeName);
 
    string Get(string nameOrPath);
 
    T Get<T>(string nameOrPath);
}

以上3個介面定義了所有的邏輯。

三、介面實現:

1.自定義ItemElement(IItem)和ItemElementCollection用於實現單個節點的配置項讀取。

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

        [ConfigurationProperty("value", IsRequired = true)]
        public string Value
        {
            get { return Convert.ToString(this["value"]); }
            set { this["value"] = value; }
        }
    }
    public class ItemElementCollection : ConfigurationElementCollection, IEnumerable<IItem>
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new ItemElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ItemElement)element).Name;
        }

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

 

2.自定義NodeElement(INode)和NodeElementCollection用於實現節點樹功能。

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

        [ConfigurationProperty("items")]
        [ConfigurationCollection(typeof(ItemElementCollection), AddItemName = "item")]
        public ItemElementCollection ItemElements
        {
            get
            {
                return this["items"] as ItemElementCollection;
            }
            set { this["items"] = value; }
        }

        [ConfigurationProperty("nodes")]
        [ConfigurationCollection(typeof(NodeElementCollection), AddItemName = "node")]
        public NodeElementCollection NodeElements
        {
            get
            {
                return this["nodes"] as NodeElementCollection;
            }
            set { this["nodes"] = value; }
        }

        public IEnumerable<IItem> Items
        {
            get
            {
                return this["items"] as ItemElementCollection;
            }
            set { this["items"] = value; }
        }

        public IEnumerable<INode> Nodes
        {
            get
            {
                return this["nodes"] as NodeElementCollection;
            }
            set { this["nodes"] = value; }
        }

        public string GetItem(string itemName)
        {
            return this.Items.FirstOrDefault(o => o.Name == itemName)?.Value;
        }

        public T GetItem<T>(string itemName)
        {
            return (T)Convert.ChangeType(this.GetItem(itemName), typeof(T));
        }
    }
    public class NodeElementCollection : ConfigurationElementCollection, IEnumerable<INode>
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new NodeElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((NodeElement)element).Name;
        }

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

 

3.自定義ConfigSection實現配置節點和配置項讀取。

    public class ConfigSection : ConfigurationSection, INode
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return Convert.ToString(this["name"]); }
            set { this["name"] = value; }
        }

        [ConfigurationProperty("items")]
        [ConfigurationCollection(typeof(ItemElementCollection), AddItemName = "item")]
        public ItemElementCollection ItemElements
        {
            get
            {
                return this["items"] as ItemElementCollection;
            }
            set { this["items"] = value; }
        }

        [ConfigurationProperty("nodes")]
        [ConfigurationCollection(typeof(NodeElementCollection), AddItemName = "node")]
        public NodeElementCollection NodeElements
        {
            get
            {
                return (NodeElementCollection)this["nodes"];
            }
            set { this["nodes"] = value; }
        }

        public IEnumerable<IItem> Items
        {
            get
            {
                return this["items"] as ItemElementCollection;
            }
            set { this["items"] = value; }
        }

        public IEnumerable<INode> Nodes
        {
            get
            {
                return (NodeElementCollection)this["nodes"];
            }
            set { this["nodes"] = value; }
        }

        public string GetItem(string itemName)
        {
            return this.Items.FirstOrDefault(o => o.Name == itemName)?.Value;
        }

        public T GetItem<T>(string itemName)
        {
            return (T)Convert.ChangeType(this.GetItem(itemName), typeof(T));
        }
    }
View Code

 

4.自定義AppConfigAdapter實現IConfig介面。

    public class AppConfigAdapter : IConfig
    {
        private INode _section;

        public AppConfigAdapter()
        {
            var sectionName = (HostingEnvironment.IsHosted ? WebConfigurationManager.OpenWebConfiguration("~") : ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None))
                .Sections.Cast<ConfigurationSection>()
                .FirstOrDefault(o => o.SectionInformation.Type.IndexOf("Onion.Configuration.AppConfig.ConfigSection") != -1)
                .SectionInformation.Name ?? "Node";
            _section = (INode)ConfigurationManager.GetSection(sectionName);
        }

        public INode GetNode(string nodeName)
        {
            return this.GetNode(nodeName, this._section);
        }

        public string Get(string nameOrPath)
        {
            if (nameOrPath.IndexOf('.') == -1)
            {
                return this._section.Items.FirstOrDefault(o => o.Name == nameOrPath)?.Value;
            }
            var nodeItemPath = nameOrPath.Split('.');
            var node = this.GetNode(nodeItemPath.FirstOrDefault());
            var nodeNameList = nodeItemPath.Skip(1).Take(nodeItemPath.Length - 2);
            if (node != null)
            {
                foreach (var item in nodeNameList)
                {
                    if (node.Nodes.Any(o => o.Name == item))
                    {
                        node = node.Nodes.FirstOrDefault(o => o.Name == item);
                    }
                    else
                    {
                        throw new System.ArgumentException(string.Format("node name {0} error", item));
                    }
                }
                return node.Items.FirstOrDefault(o => o.Name == nodeItemPath.LastOrDefault()).Value;
            }
            return null;
        }

        public T Get<T>(string nameOrPath)
        {
            var value = this.Get(nameOrPath);
            return (T)Convert.ChangeType(value, typeof(T));
        }

        #region private

        private INode GetNode(string nodeName, INode node)
        {
            INode result = null;

            if (node.Name == nodeName)
            {
                return node;
            }
            else if (node.Nodes.Any())
            {
                foreach (var item in node.Nodes)
                {
                    result = GetNode(nodeName, item);
                    if (result != null)
                    {
                        break;
                    }
                }
            }
            return result;
        }

        #endregion private
    }
View Code

 

Nuget:https://www.nuget.org/packages/Onion.Configuration/

相關文章