[总结]读取应用程序/类库配置文件(比如***.dll.config)的方法小结

0.引子

读取应用程序配置文件(比如***.exe.config)的方法有很多, .NET自带的ConfigurationManager也很方便. 这篇文章主要探讨类库生成的dll文件的配置信息的读取——特别地, 用户自定义SectionGroupName和SectionName(这里是指不同于appSettings的形式), 这种方式配置信息的读取. 配置文件格式举例(其实通过添加"设置文件", 注意:不是"应用程序配置文件")

代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> <? xml version="1.0" encoding="utf-8"  ?>
< configuration >
  
< configSections >
    
< sectionGroup  name ="userSettings"  type ="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"   >
      
< section  name ="TestDllConfig.Test"  type ="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  allowExeDefinition ="MachineToLocalUser"  requirePermission ="false"   />
    
</ sectionGroup >
    
< sectionGroup  name ="applicationSettings"  type ="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"   >
      
< section  name ="TestDllConfig.Test"  type ="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  requirePermission ="false"   />
    
</ sectionGroup >
  
</ configSections >
  
< userSettings >
    
< TestDllConfig.Test >
      
< setting  name ="ACCESSORY_TYPE"  serializeAs ="String" >
        
< value > ACCESSORY </ value >
      
</ setting >
    
</ TestDllConfig.Test >
  
</ userSettings >
  
< applicationSettings >
    
< TestDllConfig.Test >
      
< setting  name ="APPLICATION_NAME"  serializeAs ="String" >
        
< value > JGPT </ value >
      
</ setting >
      
< setting  name ="DATABASE_NAME"  serializeAs ="String" >
        
< value > JGPT </ value >
      
</ setting >
      
< setting  name ="APP_ATTACHMENT_PATH"  serializeAs ="String" >
        
< value > ~//APP_ATTACHMENT// </ value >
      
</ setting >
    
</ TestDllConfig.Test >
  
</ applicationSettings >
</ configuration >


首先, 交代一些东西. 1.不讨论为类库dll文件设置配置文件是否有必要, 我主要的出发点是, 一些参数可以通过读取配置文件得到, 从而不用重新编译类库; 2.本文实现的环境是: visual studio 2005/.net 2.0;3.dll配置信息类库内部要用到, 外部也可能用到.

 

1.首先, 新建一个空的项目解决方案. 并添加一个类库项目(空的即可, 示例中命名为TestDllConfig). 然后再添加一个空的控制台项目(测试输出用途, 示例中命名为ReadDllConfig).

到目前为止, 做好了准备工作. 接下来为类库添加配置文件. 右键类库项目, 选择 添加-新建项-选择"设置文件"-命名为Test.settings. 如下图所示:

 

 

接着, 双击新添加的设置文件, 添加参数和值. 如下图:

 

 

都是测试用的参数数据. 例子中用到的范围为Application, 范围中Application和User的选择会导致SectionGroup不一样. 保存, .NET就会自动生成一个app.config. 另外可以看到有个Test.Designer.cs 文件, 是保存的时候自动生成的(这个文件后面会说到). 打开app.config, 会看到和上面的xml代码一样.

 

到目前为止, 我们已经完成了搭架子的工作, 我们还没手工写一行代码. 下面就通过往类库里添加读取配置信息的代码, 和测试项目中控制台输出测试结果代码.

 

2. 往类库中添加读取配置信息的代码

首先大家可以读下园子里这两篇博文:

http://www.cnblogs.com/bearhand/archive/2008/09/07/1279087.html

http://www.cnblogs.com/rosanshao/archive/2008/09/07/1286191.html

这两篇说到了.NET 2.0下新增读取配置文件的方法: ConfigurationManager类

下面先给出读取配置文件路径的帮助方法(至于为什么要封装一遍, 注释里有写; 同时为了测试方便):

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  封装多一遍, 确保调用本方法的方法为Dll内部方法, 从而取得正确的Dll配置文件路径
        
///  否则可能取得的是执行程序(主程序)的路径
        
///  详细参考Assembly.GetCallingAssembly()的原理
        
///   </summary>
         private   static   string  DllConfigFilePath
        {
            
get
            {
                Assembly t_assembly 
=  Assembly.GetCallingAssembly();
                Uri t_uri 
=   new  Uri(Path.GetDirectoryName(t_assembly.CodeBase));

                
// NOTE: 这个时候读取的路径是应用程序/网站下面的bin目录, 或者应用程序/网站编译运行dll的目录(自定义输出路径的情况)
                
// 而不是类库的生成dll的输出路径! 
                
// 所以, 如果采用引用项目的方式引用, 要将配置文件一同复制到和dll同个目录(bin目录), 否则读取不到配置信息; 修改配置
                
// 文件后同样记得此目录下覆盖原文件
                 return  Path.Combine(t_uri.LocalPath, t_assembly.GetName().Name  +   " .dll.config " );
            }
        }
复制代码


下面这段代码是常用的读取自定义配置信息的方法, 返回Configuration类型:

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> ///   <summary>
        
///  读取dll的配置信息的方法
        
///   </summary>
        
///   <returns></returns>
         private   static  Configuration CurrentDllConfiguration
        {
            
get
            {
                ExeConfigurationFileMap configFile 
=   new  ExeConfigurationFileMap();

                
// NOTE: 这个时候读取的路径是应用程序/网站下面的bin目录, 或者应用程序/网站编译运行dll的目录(自定义输出路径的情况)
                
// 而不是类库的生成dll的输出路径! 
                
// 所以, 如果采用引用项目的方式引用, 要将配置文件一同复制到和dll同个目录(bin目录), 否则读取不到配置信息; 修改配置
                
// 文件后同样记得此目录下覆盖原文件
                configFile.ExeConfigFilename  =  DllConfigFilePath;
                
return  ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);

                
// 也可以使用以下方法读取配置信息
                
// Configuration t_appConfig =
                 /// /ConfigurationManager.OpenExeConfiguration(Path.Combine(t_uri.LocalPath, t_assembly.GetName().Name));
                 // ConfigurationManager.OpenExeConfiguration(Path.Combine(t_uri.LocalPath, t_assembly.GetName().Name + ".dll"));
                 /// /ConfigurationManager.OpenExeConfiguration(Path.Combine(t_uri.LocalPath, t_assembly.GetName().Name + ".dll.config"));
                 // return t_appConfig;
            }
        }
复制代码


通过查看程序自动生成的配置文件, 知道SectionGroupName和SectionName, 定义属性:

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  SectionGroup名
        
///   </summary>
         private   static   string  SectionGroupName
        {
            
get
            {
                
return   " applicationSettings " ;
            }
        }
        
///   <summary>
        
///  Section名
        
///   </summary>
         private   static   string  SectionName
        {
            
get
            {
                
return   " TestDllConfig.Test " ;
            }
        }

        
private   static   string  FullSectionName
        {
            
get
            {
                
return  SectionGroupName  +   " / "   +  SectionName;
            }
        }
复制代码


对于以上代码, 网上常见都是如此处理(.NET 1.1一般XML操作, 后面也会说到).  现在开始我们的各种情况说明了.

 

2.1 直接使用Configuration.AppSettings方式读取配置的情况

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> #region  直接使用appConfig.AppSettings方式读取配置的情况
        
// NOTE: 下面直接使用appConfig.AppSettings方式读取数据, 必须按照以下格式写配置文件
        
// <appSettings>
        
//     <add key="DATABASE_NAME" value="JGPT"/>
        
// </appSettings>
        
// 如果配置文件中没有<appSettings></appSettings>或没有在该节点配置信息
        
// 那么可以debug到以下情况, 说明AppSettings默认读取的是<appSettings></appSettings>内的配置信息
        
// CurrentDllConfiguration.AppSettings.Settings 不为null
        
// 但是CurrentDllConfiguration.AppSettings.Settings.Count == 0
        
//  NOTE2: 读取指定key的值value必须使用appConfig.AppSettings.Settings["APPLICATION_NAME"].Value方式;
        
//      不能使用appConfig.AppSettings["APPLICATION_NAME"]方式, 否则编译器会提示
        
//  "错误“System.Configuration.ConfigurationElement.this[System.Configuration.ConfigurationProperty]”不可访问,因为它受保护级别限制"
        
// 这与平常读取Web.Config配置信息不太一样
         public   static   readonly   string  DATABASE_NAME_By_AppSettings  =  CurrentDllConfiguration.AppSettings.Settings[ " DATABASE_NAME_By_AppSettings " ].Value;
        
#endregion
复制代码


说明: 上面的参数名"DATABASE_NAME_By_AppSettings" 直接拷贝项目代码, 大家可以换成自动生成的设置文件中有的名称:

"DATABASE_NAME ". 结果是一样的.

很显然, 直接在控制台项目中

System.Console.WriteLine(TestDllConfig.DllConfig.DATABASE_NAME_By_AppSettings);

Debug可以发现CurrentDllConfiguration.AppSettings.Settings 不为null, 但是CurrentDllConfiguration.AppSettings.Settings.Count == 0; 通过多次debug以及在配置文件中添加以下代码:

<appSettings>
    <add key="DATABASE_NAME_By_AppSettings" value="JGPT"/>
</appSettings> 

再次运行就可以读取到数据了. 说明AppSettings默认读取的是<appSettings></appSettings>内的配置信息. 如果我们要读取类似程序自动生成的配置信息怎么办呢? 先不急, 先提醒下大家一个小问题. 用这种方式读取数据, 需要注意和平常读取Web.Config配置信息不太一样(平时可以直接通过[]读取)

 NOTE2: 读取指定key的值value必须使用appConfig.AppSettings.Settings["APPLICATION_NAME"].Value方式;
     不能使用appConfig.AppSettings["APPLICATION_NAME"]方式, 否则编译器会提示
 "错误“System.Configuration.ConfigurationElement.this[System.Configuration.ConfigurationProperty]”不可访问,因为它受保护级别限制"


2.2 Configuration类型转换为KeyValueConfigurationCollection 或者 NameValueCollection 类型的情况

 这个没什么好说的, 编译器报错.

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  以下方式编译器会报错
        
///  cs as KeyValueConfigurationCollection 或者 cs as NameValueCollection 编译器会提示:
        
///  错误    无法通过内置转换将类型“System.Configuration.ConfigurationSection”转换为 ***类型
        
///   </summary>
         // public static KeyValueConfigurationCollection DllSettings_NameValueCollection
        
// {
        
//     get
        
//     {
        
//         ConfigurationSection cs = CurrentDllConfiguration.GetSection(FullSectionName);
        
//         return cs as KeyValueConfigurationCollection;  // 或者 cs as NameValueCollection 
        
//     }
        
//
复制代码


 2.3 Configuration类型转换为AppSettingsSection类型的情况

这个和上面稍微有点不同, 编译无提示错误, 但转换后为null. 

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  内置转换为AppSettingsSection类型
        
///  编译无提示错误, 但转换后为null
        
///   </summary>
         public   static  AppSettingsSection DllSettings_AppSettingsSection
        {
            
get
            {
                ConfigurationSection cs 
=  CurrentDllConfiguration.GetSection(FullSectionName);
                
return  cs  as  AppSettingsSection;
            }
        }
复制代码


2.4 Configuration类型转换为自定义配置信息数据结构的情况

 这段代码也是很常见的啦. 不多说了, 结果和转换为AppSettingsSection的情形一样: 编译无提示错误, 但转换后为null.

这种做法可以参考

http://stackoverflow.com/questions/204695/storing-values-in-the-web-config-appsettings-or-configsection-which-is-more-e

http://haacked.com/archive/2007/03/12/custom-configuration-sections-in-3-easy-steps.aspx ;

看 以上例子, 发现人家能测试成功,  就是不知道为什么我的例子失败 :-(. anyway, 挫折多点也好, 可以学到更多东西, 特别是对于执着的人.

 

代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  自定义配置信息数据结构
        
///   </summary>
         public   class  UserDefinedDllConfig : ConfigurationSection
        {
            
///   <summary>
            
///  
            
///   </summary>
             public   static  UserDefinedDllConfig DllSettings
            {
                
get
                {
                    
// TODO: 每次调用CurrentDllConfiguration都会再次读取配置文件, 效率低下; 改用Cache
                    ConfigurationSection cs  =  CurrentDllConfiguration.GetSection(FullSectionName);
                    
//  或者CurrentDllConfiguration.GetSectionGroup(SectionGroupName).Sections[SectionName] 

                    
return  cs  as  UserDefinedDllConfig; // 转换后为null
                }
            }

            
///   <summary>
            
///  数据库名
            
///   </summary>
            [ConfigurationProperty( " DATABASE_NAME " , IsRequired  =   true )]
            
public   string  DatabaseName
            {
                
get
                {
                    
return   this [ " DATABASE_NAME " as   string ;
                }
            }

            
///   <summary>
            
///  本系统名称
            
///   </summary>
            [ConfigurationProperty( " APPLICATION_NAME " , IsRequired  =   true )]
            
public   string  ApplicationName
            {
                
get
                {
                    
return   this [ " APPLICATION_NAME " as   string ;
                }
            }
        }


2.5  Configuration类型转换为ClientSettingsSection类型的情况

经过以上测试, 用google查找很多关于dll.config的设置文件读取的资料, 发现有例子将配置信息类型Configuration转换为ClientSettingsSection操作. 但是我测试发现还是有问题, 下面是代码:

代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  内置转换为ClientSettingsSection类型
        
///  不提示错误而且转换后也不为null, 但调用 .Settings["APPLICATION_NAME"] 提示:
        
///  错误    “System.Configuration.ConfigurationElement.this[System.Configuration.ConfigurationProperty]”不可访问,因为它受保护级别限制
        
///   </summary>
         public   static  ClientSettingsSection DllSettings_ClientSettingsSection
        {
            
get
            {
                ConfigurationSection cs  =  CurrentDllConfiguration.GetSection(FullSectionName);
                
return  cs  as  ClientSettingsSection;
            }
        }

不提示错误而且转换后也不为null, 但调用 .Settings["APPLICATION_NAME"] 提示:
错误    “System.Configuration.ConfigurationElement.this[System.Configuration.ConfigurationProperty]”不可访问,因为它受保护级别限制

这个错误很郁闷, 因为遇到很多次, 上次就是AppSettings["参数名"], 改为AppSettings.Settings["参数名"].Value就可以, 但是这里不行. 还是会有错误. 到这里, 再次查找很多资料, 发现不少老外也遇到这个问题没解决. 今天一个偶然机会, 看到帖子:

http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/ec237df4-f05e-4711-a230-fb089c395c73/?prof=required

(根据微软网站以往的情况, 这个链接很可能以后会改变...)

OK. 下面就提出这个帖子中提到的3种可行的解决方案.

2.6 参考上面帖子nrolland的回复
接着2.5的问题,

转换为ClientSettingsSection类型后, 采用以下方式可以读取出数据:
System.Console.WriteLine(TestDllConfig.DllConfig.DllSettings_ClientSettingsSection.Settings.Get("APPLICATION_NAME").Value.ValueXml.InnerText); 

很显然, 这种方案在类似ConfigurationManager的方式下调用, 但是还是有很明显的XML操作痕迹. 没办法, XML才是老本根, 谁叫设置文件采用xml格式呢.

 

2.7 经典的XML解决方案

上面的帖子回帖中就有人写了一段代码: (下面这段基本上是直接手工照敲一遍, 就是处理叶子节点稍微修改了下, 因为配置文件不一样嘛)

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->          ///   <summary>
        
///  通过Xml处理取得配置信息
        
///   </summary>
        
///   <param name="sSection"></param>
        
///   <param name="sFilePath"></param>
        
///   <returns></returns>
         private   static  NameValueCollection GetNameValueCollectionSection( string  sSection,  string  sFilePath)
        {
            
string  sFile  =  sFilePath;
            System.Xml.XmlDocument xDoc 
=   new  System.Xml.XmlDocument();
            NameValueCollection nRet 
=   new  NameValueCollection();

            
try
            {
                ExeConfigurationFileMap fMap 
=   new  ExeConfigurationFileMap();
                fMap.ExeConfigFilename 
=  sFile;
                Configuration config 
=  ConfigurationManager.OpenMappedExeConfiguration(fMap, ConfigurationUserLevel.None);

                
string  sXml  =  config.GetSection(sSection).SectionInformation.GetRawXml();
                xDoc.LoadXml(sXml);
            }
            
catch  (Exception exp)
            {
                
throw   new  Exception(exp.Message);
            }

            System.Xml.XmlNode xList 
=  xDoc.ChildNodes[ 0 ];
            
// System.Xml.XmlNode xList = xDoc.GetElementsByTagName(SectionName)[0];
             foreach  (System.Xml.XmlNode xNode  in  xList)
            {
                
// nRet.Add(xNode.Attributes[0].Value, xNode.Attributes[1].Value);
                
// NOTE: Test.settings自动生成的xml格式与<appSettings>不一样: Value在子节点
                nRet.Add(xNode.Attributes[ 0 ].Value, xNode.ChildNodes[ 0 ].ChildNodes[ 0 ].Value);
            }

            
return  nRet;
        }
复制代码

网上类似的代码很多. 就是基本的XML操作, 加上ConfigurationManager.

通过调用 return GetNameValueCollectionSection(FullSectionName, DllConfigFilePath); 直接返回NameValueCollection类型数据, 得到NameValueCollection类型数据就没问题了.

这里补充说明下为什么开头会将获取dll.config文件的路径的方法封装一遍, 因为写这段代码的时候直接照敲, 结果发现用Assembly读取到的配置文件路径不一样. 查MSDN发现:

 Assembly.GetCallingAssembly返回调用当前正在执行的方法的方法的   Assembly。  
 这就是基础不扎实的表现...

 

2.8 从自动生成的代码中读取配置的情况

 上面帖子最后一篇(当前时间20100301是最后一篇)提到另外一种解决思路. 参考网址: http://www.codeproject.com/KB/cs/UserSettings.aspx. 这篇文章开头的截图是不是很像呢? 哈, 自动生成配置文件基本操作都差不多.

还记得不? 开始设置参数后自动生成的那个cs文件吗? 自动生成的代码一看就明白.

代码
复制代码

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> #region  从自动生成的代码中读取配置的情况
        
///   <summary>
        
///  可以通过以下方式给外部调用
        
///   </summary>
         public   static   string  m_DllSettings_By_AutoGenerated  =  TestDllConfig.Test.Default.DATABASE_NAME;
        
///   <summary>
        
///  从自动生成的代码中读取(有默认值)
        
///  NOTE: 如果声明为public或protected, 会编译错误: 
        
///  错误    23    可访问性不一致: 属性类型“TestDllConfig.Test”比属性“TestDllConfig.DllConfig.DllSettings_By_AutoGenerated”的可访问性低
        
///  所以只能声明为private.
        
///  但是TestDllConfig.Test.Default.DATABASE_NAME却可以访问
        
///  NOTE2: 有时候自动生成的cs文件可能会丢失, 或者删除. 因此这种方法必须要求自动生成的代码存在.
        
///   </summary>
         private   static  Test DllSettings_By_AutoGenerated
        {
            
get
            {
                
return  TestDllConfig.Test.Default;
            }
        } 
        
#endregion
复制代码

除了要留意自动生成的类是sealed类型, 需要通过类似

public static string m_DllSettings_By_AutoGenerated = TestDllConfig.Test.Default.DATABASE_NAME;

的方式给外部程序调用(就是测试用的控制台程序, 如果是类库自身调用问题就不大了)


3. 小结

本文从头到尾记录了本人对这个问题的思考过程. 或许还有更加完美的解决方案, 希望园子里的大牛们不吝赐教. 文章写的有点啰嗦了, 记录下来也是为了以后自己查找资料方便. 希望这篇文章对有需要的朋友有用. 欢迎各位针对本文提出意见(不希望见到匿名谩骂)


总的来说, 我还是偏向于2.6的解决方案, 比较方便, 而且不容易出错(通过参数名来读取值, 而不是索引);

自动生成的代码是可以删除的, 某些情况下也可能丢失, 所以不是很可靠.也是可以从设置文件读数据, 而且可以有默认值

 

另外, 对于为一个dll文件是否有必要添加设置文件,

http://stackoverflow.com/questions/594298/c-dll-config-file

这里的最好的答复有说明(太长了, 没看完...)

 

最后, 附上本文的测试源代码:/Files/GuominQiu/SourceCode/20100301_TestDllConfig_SourceCode.7z


PS. 再啰嗦一句, 采用7-ZIP格式压缩源代码, 可以比winrar得到更加高的压缩率, 对比相当明显. 一般一个50M以上的项目源代码目录, 用7zip可以压缩到大约6M多点.

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页