appSettings太简单,为每个程序自定义配置节点太复杂,因此要解决app.config&web.config自定义配置的复用问题。
1.读取不依赖SectionName,根节点可以定义为任何名称。
2.足够简单,配置项采用name value的形式;足够复杂,采用树型结构,每个节点都可以有多个配置项和子节点。
3.使用简单,采用路径简化配置项的读取。如: config.Get<string>("root.sub.item-test")。
一、调用方式:1.配置文件:
<?xmlversion="1.0" encoding="utf-8"?><configuration><configSections><sectionname="node"type="Onion.Configuration.AppConfig.ConfigSection,Onion.Configuration"/></configSections><nodename="root"><items><itemname="version"value="1.0.0.1"/></items><nodes><nodename="runtime"><items><itemname="debug"value="false"/><itemname="ioc"value="IoC.Contianer.StructureMapIoC"/></items></node><nodename="upload"><items><itemname="auth"value="true"/><itemname="path"value="~/upload"/><itemname="url"value="~/Upload/Index"/></items></node><nodename="captcha"><items><itemname="timeout"value="3000"/><itemname="url"value="~/Captcha/Index"/></items></node><nodename="oauth2"><items><itemname="disabled"value="false"/><itemname="callback"value="/Home/ExternalLoginCallBack?PRoviderName="/></items><nodes><nodename="QQclient"><items><itemname="disabled"value="false"/><itemname="method"value="get"/><itemname="key"value="9233e24d"/><itemname="secret"value="1ac35907-7cfa-4079-975c-959b98d23a95"/></items></node><nodename="weiboclient"><items><itemname="disabled"value="true"/><itemname="method"value="post"/><itemname="key"value="0cdea8f3"/><itemname="secret"value="dc679dbb-7e75-44f7-a99e-5359259fc94b"/></items></node></nodes></node></nodes></node></configuration>
2.调用代码:
[Fact]publicvoidTests()
{varconfig =newAppConfigAdapter();
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(varnodeinconfig.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");
}elseif(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用于实现单个节点的配置项读取。
[flash=500,400]http://s1.knowsky.com/20160211/dxkkierjalu21.gif[/flash][flash=500,400]http://s1.knowsky.com/20160211/gw42gquvzty21.gif[/flash]publicclassItemElement : ConfigurationElement, IItem
{
[ConfigurationProperty("name", IsRequired =true)]publicstringName
{get{returnConvert.ToString(this["name"]); }set{this["name"] =value; }
}
[ConfigurationProperty("value", IsRequired =true)]publicstringValue
{get{returnConvert.ToString(this["value"]); }set{this["value"] =value; }
}
}publicclassItemElementCollection : ConfigurationElementCollection, IEnumerable<IItem>{protectedoverrideConfigurationElement CreateNewElement()
{returnnewItemElement();
}protectedoverrideobjectGetElementKey(ConfigurationElement element)
{return((ItemElement)element).Name;
}publicnewIEnumerator<IItem>GetEnumerator()
{for(inti =0; i <base.Count; i++)
{yieldreturnbase.BaseGet(i)asIItem;
}
}
}
View Code
2.自定义NodeElement(INode)和NodeElementCollection用于实现节点树功能。
[flash=500,400]http://s1.knowsky.com/20160211/dxkkierjalu21.gif[/flash][flash=500,400]http://s1.knowsky.com/20160211/gw42gquvzty21.gif[/flash]publicclassNodeElement : ConfigurationElement, INode
{
[ConfigurationProperty("name", IsRequired =true)]publicstringName
{get{returnConvert.ToString(this["name"]); }set{this["name"] =value; }
}
[ConfigurationProperty("items")]
[ConfigurationCollection(typeof(ItemElementCollection), AddItemName ="item")]publicItemElementCollection ItemElements
{get{returnthis["items"]asItemElementCollection;
}set{this["items"] =value; }
}
[ConfigurationProperty("nodes")]
[ConfigurationCollection(typeof(NodeElementCollection), AddItemName ="node")]publicNodeElementCollection NodeElements
{get{returnthis["nodes"]asNodeElementCollection;
}set{this["nodes"] =value; }
}publicIEnumerable<IItem>Items
{get{returnthis["items"]asItemElementCollection;
}set{this["items"] =value; }
}publicIEnumerable<INode>Nodes
{get{returnthis["nodes"]asNodeElementCollection;
}set{this["nodes"] =value; }
}publicstringGetItem(stringitemName)
{returnthis.Items.FirstOrDefault(o => o.Name == itemName)?.Value;
}publicT GetItem<T>(stringitemName)
{return(T)Convert.ChangeType(this.GetItem(itemName),typeof(T));
}
}publicclassNodeElementCollection : ConfigurationElementCollection, IEnumerable<INode>{protectedoverrideConfigurationElement CreateNewElement()
{returnnewNodeElement();
}protectedoverrideobjectGetElementKey(ConfigurationElement element)
{return((NodeElement)element).Name;
}publicnewIEnumerator<INode>GetEnumerator()
{for(inti =0; i <base.Count; i++)
{yieldreturnbase.BaseGet(i)asINode;
}
}
}
View Code
3.自定义ConfigSection实现配置节点和配置项读取。
[flash=500,400]http://s1.knowsky.com/20160211/dxkkierjalu21.gif[/flash][flash=500,400]http://s1.knowsky.com/20160211/gw42gquvzty21.gif[/flash]publicclassConfigSection : ConfigurationSection, INode
{
[ConfigurationProperty("name", IsRequired =true)]publicstringName
{get{returnConvert.ToString(this["name"]); }set{this["name"] =value; }
}
[ConfigurationProperty("items")]
[ConfigurationCollection(typeof(ItemElementCollection), AddItemName ="item")]publicItemElementCollection ItemElements
{get{returnthis["items"]asItemElementCollection;
}set{this["items"] =value; }
}
[ConfigurationProperty("nodes")]
[ConfigurationCollection(typeof(NodeElementCollection), AddItemName ="node")]publicNodeElementCollection NodeElements
{get{return(NodeElementCollection)this["nodes"];
}set{this["nodes"] =value; }
}publicIEnumerable<IItem>Items
{get{returnthis["items"]asItemElementCollection;
}set{this["items"] =value; }
}publicIEnumerable<INode>Nodes
{get{return(NodeElementCollection)this["nodes"];
}set{this["nodes"] =value; }
}publicstringGetItem(stringitemName)
{returnthis.Items.FirstOrDefault(o => o.Name == itemName)?.Value;
}publicT GetItem<T>(stringitemName)
{return(T)Convert.ChangeType(this.GetItem(itemName),typeof(T));
}
}
View Code
4.自定义AppConfigAdapter实现IConfig接口。
[flash=500,400]http://s1.knowsky.com/20160211/dxkkierjalu21.gif[/flash][flash=500,400]http://s1.knowsky.com/20160211/gw42gquvzty21.gif[/flash]publicclassAppConfigAdapter : IConfig
{privateINode _section;publicAppConfigAdapter()
{varsectionName = (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);
}publicINode GetNode(stringnodeName)
{returnthis.GetNode(nodeName,this._section);
}publicstringGet(stringnameOrPath)
{if(nameOrPath.IndexOf('.') == -1)
{returnthis._section.Items.FirstOrDefault(o => o.Name == nameOrPath)?.Value;
}varnodeItemPath = nameOrPath.Split('.');varnode =this.GetNode(nodeItemPath.FirstOrDefault());varnodeNameList = nodeItemPath.Skip(1).Take(nodeItemPath.Length -2);if(node !=null)
{foreach(variteminnodeNameList)
{if(node.Nodes.Any(o => o.Name ==item))
{
node= node.Nodes.FirstOrDefault(o => o.Name ==item);
}else{thrownewSystem.ArgumentException(string.Format("node name {0} error", item));
}
}returnnode.Items.FirstOrDefault(o => o.Name ==nodeItemPath.LastOrDefault()).Value;
}returnnull;
}publicT Get<T>(stringnameOrPath)
{varvalue =this.Get(nameOrPath);return(T)Convert.ChangeType(value,typeof(T));
}#regionprivateprivateINode GetNode(stringnodeName, INode node)
{
INode result=null;if(node.Name ==nodeName)
{returnnode;
}elseif(node.Nodes.Any())
{foreach(variteminnode.Nodes)
{
result=GetNode(nodeName, item);if(result !=null)
{
break;
}
}
}returnresult;
}#endregionprivate}
View Code
Nuget:https://www.nuget.org/packages/Onion.Configuration/