http://edi.wang/post/2015/4/22/how-to-read-webconfig-appsettings-with-bigiblity

先插句题外话,下版本的ASP.NET貌似把web.config撸掉了,都变成json了。所以本文讨论的内容可能以后用不到了,但是一些设计思想还是可以用的~

直接进入正题,在ASP.NET网站里(也包括其他有web.config, app.config)的.NET工程里,读AppSettings的值是个很常见的场景。比如:

<add key="EnableAzureWebTrace" value="true"/>

在代码里读的时候就会用到:

ConfigurationManager.AppSettings["EnableAzureWebTrace"];

这个[]索引器返回的是string类型。所以下一步我们通常需要类型转换才能在代码里拿来用。比如这个例子里,我们就要转换成bool。其他时候,可能要转换为int等类型。

string enableAzureWebTraceConfig = ConfigurationManager.AppSettings["EnableAzureWebTrace"];
bool enableAzureWebTrace = bool.Parse(enableAzureWebTraceConfig);
if(enableAzureWebTrace)
{
// do some logic
}

但问题是,config文件的值对于我们代码来说是不稳定因素,不可控的,这里拿回来的string未必能正确转换格式。所以通常我们会用TryParse方法来防爆:

string enableAzureWebTraceConfig = ConfigurationManager.AppSettings["EnableAzureWebTrace"];
bool enableAzureWebTrace = false;
if (bool.TryParse(enableAzureWebTraceConfig, out enableAzureWebTrace) && enableAzureWebTrace)
{
// do some logic
}
else
{
throw new ConfigurationException("EnableAzureWebTrace value must be true of false.");
}

当然,不要忘了一点。读出来的string有可能首先就是空的。所以又得加上对string的判断,并且考虑到ConfigurationManager.AppSettings[]索引器本身可能会爆,所以还得加try-catch,最终代码就变成这样了:

try
{
string enableAzureWebTraceConfig = ConfigurationManager.AppSettings["EnableAzureWebTrace"];
if (!string.IsNullOrEmpty(enableAzureWebTraceConfig))
{
bool enableAzureWebTrace = false;
if (bool.TryParse(enableAzureWebTraceConfig, out enableAzureWebTrace) && enableAzureWebTrace)
{
// do some logic
}
else
{
throw new ConfigurationException("EnableAzureWebTrace value must be true of false.");
}
} }
catch (ConfigurationException ce)
{
// error handling logic
throw;
}

这样的代码非常没有逼格,重用性很差,如果你的config里面AppSettings比较多,或者一个settings在程序里到处被用,显然不应该每次都把这样的代码到处复制。所以封装一下呗:

public bool IsEnableAzureWebTrace()
{
try
{
bool enableAzureWebTrace = false;
string enableAzureWebTraceConfig = ConfigurationManager.AppSettings["EnableAzureWebTrace"];
if (!string.IsNullOrEmpty(enableAzureWebTraceConfig))
{
if (!bool.TryParse(enableAzureWebTraceConfig, out enableAzureWebTrace))
{
throw new ConfigurationException("EnableAzureWebTrace value must be true of false.");
}
}
return enableAzureWebTrace;
}
catch (ConfigurationException ce)
{
// error handling logic
return false;
}
}

现在要用到EnableAzureWebTrace的地方都只要调用public bool IsEnableAzureWebTrace()就行了,我们就把如何读config的逻辑抽离了。重构的目的是,万一以后读config的机制变了,只要改这一处。不用到处改。但是,我们重构的粒度还不够。这个方法只能用来读EnableAzureWebTrace这一个设置。我们要通用一下,让它也能读其他bool类型的设置。把key单独的抽出来变成参数:

public bool GetBooleanConfiguration(string key)
{
try
{
bool val = false;
string rawConfigValue = ConfigurationManager.AppSettings[key];
if (!string.IsNullOrEmpty(rawConfigValue))
{
if (!bool.TryParse(rawConfigValue, out val))
{
throw new ConfigurationException(string.Format("{0} value must be true of false.", key));
}
}
return val;
}
catch (ConfigurationException ce)
{
// error handling logic
return false;
}
}

但是这还不够,因为这个方法只能满足于bool类型的config,我们希望有个公用的方法,能读取其他类型。这时候就需要用泛型了。把返回类型给抽离出来。

难点在于,每种数据类型的类型转换写法不一样。比如bool类型是bool.TryParse,int类型是int.TryParse,怎么把这部分逻辑抽象出来呢?

一种办法是用C#本身的类型转换:

(T) Convert.ChangeType(rawConfigValue, typeof (T));

另一种是把类型转换的逻辑作为委托加在方法的参数里,这样就用lambda表达式去传,我比较偏向这种方法,因为方法的调用者能非常清晰的知道“该干嘛,该怎么干”。

这时候,如果因为非法类型转换爆,是得让调用者知道的。所以我偏向把TryParse改为Parse,死就要死个明白。

public T GetConfiguration<T>(Func<string, T> parseFunc, string key)
{
try
{
T val = default(T);
string rawConfigValue = ConfigurationManager.AppSettings[key];
if (!string.IsNullOrEmpty(rawConfigValue))
{
return parseFunc(rawConfigValue);
}
return val;
}
catch (ConfigurationException ce)
{
// error handling logic
return default(T);
}
}

现在,调用这个方法就能这样去写:

GetConfiguration<bool>(bool.Parse, "EnableAzureWebTrace");

看起来已经很牛逼了。但其实还不够。考虑到之前说的config值为空字符串的问题,安全一点的做法是,当遇到空字符串时候,返回一个默认值。因为这种错误,并不是key不存在的错误,而是key存在,但是值没填。非法值是应该认为错误的。但是空值我个人认为更应该处理为一种“警告”,是应该有fallback的策略的,而非不可饶恕的错误。为了返回默认值,我们可以多加一个委托。

public T GetConfiguration<T>(Func<string, T> parseFunc, Func<T> defaultTValueFunc, string key)
{
try
{
string rawConfigValue = ConfigurationManager.AppSettings[key];
return !string.IsNullOrEmpty(rawConfigValue) ?
parseFunc(rawConfigValue) :
defaultTValueFunc();
}
catch (ConfigurationException ce)
{
// error handling logic
return default(T);
}
}

现在,调用者就能灵活处理遇到config为空时候的默认值了:

GetConfiguration<bool>(bool.Parse, () => false, "EnableAzureWebTrace");

但是如果每次都在条件判断里写上面那样的语句是挺麻烦的,在一般的系统开发中,我们常常会用一个管理配置的Settings类来对应Web.config里的设置表,维护这个关系。为了使用方便,我们会把每个Settings的名字,也就是key,作为属性去暴露给调用者,于是你就能这样写:

public bool EnableAzureWebTrace
{
get
{
return GetConfiguration<bool>(bool.Parse, () => false, "EnableAzureWebTrace");
}
}

你以为装逼结束了吗?当然不行!你没发现,属性名称和传进去的string类型的key名称是重复的吗?这样写代码是不是有点蛋疼?而且最惨的是,在VS2015,C#6.0之前(也就是下版本的C#),string这种东西,要是写错了是编译不出来的,所以我们应该尽量避免用string传key。经常会发生改了属性名,没有一起改string值的悲剧。比如MVVM框架的RaisePropertyChanged(string)就经常坑爹(题外话)。。。

好在,.NET4.5有个CallerMemberName特性,意思是”调用我的方法叫什么名字”,就能帮我们把这个string参数撸掉。

所以,我们只需要把方法签名里的string key改成:

public T GetConfiguration<T>(Func<string, T> parseFunc, Func<T> defaultTValueFunc, [CallerMemberName]string key = "")

这样这个方法被调用的时候,key就会自动赋值为调用它的方法或属性名。然后,刚才的那个属性就能够这样去写:

public bool EnableAzureWebTrace
{
get
{
return GetConfiguration<bool>(bool.Parse, () => false);
}
}

你以为装逼真的结束了吗?还有最后一步。万一要是碰到有些情况,属性名真的和appSettings里的key名字不一样怎么办?为了灵活处理这种边缘情况,还可以加个参数,强撸这种名称不一样的情况,如果这个参数被赋值了(下面的supressKey),就用它去读config而不用传入 的key。

下面给出我博客里读AppSettings的通用代码:

private T TryGetValueFromConfig<T>(Func<string, T> parseFunc, Func<T> defaultTValueFunc,
[CallerMemberName]string key = "", string supressKey = "")
{
try
{
if (!supressKey.IsNullOrEmptyOrWhiteSpace())
{
key = supressKey;
} var node = ConfigurationManager.AppSettings[key];
return !string.IsNullOrEmpty(node) ? parseFunc(node) : defaultTValueFunc();
}
catch (Exception ex)
{
Logger.Error(string.Format("Error Reading web.config on AppSettings node: {0}", key), ex);
return default(T);
}
}

现在,你就能灵活装逼了,给几个例子:

string类型,属性名和key不一样,默认值“FileSystemImageProvider”:

public string PostImageProvider
{
get
{
return TryGetValueFromConfig(_ => _, () => "FileSystemImageProvider", supressKey: "ImageProvider");
}
}

bool类型,默认值想要true

public bool IncludeSiteDomainForImageUploadUrl
{
get
{
return TryGetValueFromConfig(bool.Parse, () => true);
}
}

int类型,默认值为20

public int CacheSlideExpireTimeSpanFallbackMinutes
{
get
{
return TryGetValueFromConfig(int.Parse, () => 20);
}
}

ASP.NET5和EF7马上来了,我的博客代码准备来一次脱胎换骨的重写,所以我陆续把以前博客代码里一些自认为非常适合.NET初学者学习的东西分享出来,让读者们在注孤生的道路上越走越远:)

如何高逼格读取Web.config中的AppSettings的更多相关文章

  1. c# 如何读取web.config中的内容(ConfigurationManager)

    1.web.config中写入 <appSettings>    <add key="TokenQPark" value="http://localho ...

  2. 前后台读取Web.config中的值的方法

    webconfig <configuration> <appSettings> <add key="Workflow_Url" value=" ...

  3. c# 怎么读取web.config中的配置项

    ConfigurationManager.AppSettings["templateId"]

  4. [转]WinForm和WebForm下读取app.config web.config 中邮件配置的方法

    本文转自:http://blog.csdn.net/jinbinhan/article/details/1598386 1. 在WinForm下读取 App.config中的邮件配置语句如下: Con ...

  5. 在Asp.Net MVC 中如何用JS访问Web.Config中appSettings的值

    应用场景: 很多时候我们要在Web.Config中添加appSettings的键值对来标识一些全局的信息,比如:调用service的domain,跳转其他网站页面的url 等等: 那么此时就涉及到了一 ...

  6. 释放SQL Server占用的内存 .Net 读取xml UrlReWriter 在web.config中简单的配置

    释放SQL Server占用的内存   由于Sql Server对于系统内存的管理策略是有多少占多少,除非系统内存不够用了(大约到剩余内存为4M左右),Sql Server才会释放一点点内存.所以很多 ...

  7. 【转载】两个Web.config中连接字符串中特殊字符解决方案

    userid =  test password = aps'"; 那么连接字符串的写法为: Provider=SQLOLEDB.1;Password="aps'"&quo ...

  8. web.config中configSections section节 -Z

    由于最近一个项目的数据库变动比较频繁, 为了减少数据层的负担, 打算采用.net的MVC框架, 使用LINQ对付数据层.       这个框架的web.config文件里出现了configSectio ...

  9. ASP.NET MVC系列:web.config中ConnectionString aspnet_iis加密与AppSettings独立文件

    1. web.config中ConnectionString aspnet_iis加密 web.config路径:E:\Projects\Libing.Web\web.config <conne ...

随机推荐

  1. HDU 5504 GT and sequence 模拟

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5504 思路:模拟 代码: #include<stdio.h>//------杭电5504 ...

  2. GIL(全局解释器锁)

    引入 现在绝大部分的Python都是CPython解释器(但不是必须使用CPython解释器),而CPython的一个特性就是有GIL,作用保证解释器级别的代码在运行时不被其他的线程进行修改,即加锁处 ...

  3. 日志模块---logging

    作用 在程序的运行过程中,会遇到很多需要打印的中间信息,通过使用logging模块可以详细的输出信息,不同于print()函数的单一功能. 应用场景 对于软件程序的运行过程中输出中间信息,如账单信息, ...

  4. LeetCode139:Word Break

    Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separa ...

  5. Unity游戏中使用贝塞尔曲线

    孙广东   2015.8.15 比方在3D rpg游戏中.我们想设置弹道,不同的轨迹类型! 目的:这篇文章的主要目的是要给你关于在游戏怎样使用贝塞尔曲线的基本想法. 贝塞尔曲线是最主要的曲线,一般用在 ...

  6. 谈谈PBOC3.0中使用的国密SM2算法

    转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/39780825 作者:小马 一 知识准备 SM2是国密局推出的一种他们自己说具有自主 ...

  7. hadoop用mutipleInputs实现map读取不同格式的文件

    mapmap读取不同格式的文件这个问题一直就有,之前的读取方式是在map里获取文件的名称,依照名称不同分不同的方式读取,比如以下的方式 //取文件名 InputSplit inputSplit = c ...

  8. 在Visual Studio 中开发Office Add-in

    作者:陈希章 发表于2017年7月13日 "Talk is cheap, show me the code",我们就用代码来说话吧.这一篇将给大家介绍如何开始Office Add- ...

  9. linux vi/vim编辑文件显示行号

    方法一(最尴尬的方法): 1.显示当前行行号,在VI的命令模式下输入 :nu 2.显示所有行号,在VI的命令模式下输入 :set nu #这是:set number 的简写 方法二(最好的方法): 使 ...

  10. sql对每一条记录都给他一个随机的数。

    update [WonyenMall].[dbo].[T_Real_Commodity] set increment=FLOOR(RAND(ABS(CHECKSUM(NEWID()))) * 100) ...