开通了blog这么长时间,一直没来写篇文章,原因是实在太忙.…(其实也是有点偷懒).最近在开发CSDN新社区的过程中遇到了一个与配置文件操作有关的问题,觉得比较有代表性,干脆就以此为主题,作为我的第一篇文章好了:)
需求很简单,我们需要一个XML形式的配置文件,记录了单个论坛的配置信息,现在我们就需要一个解决方案来方便的对配置信息进行存取.
脑子几乎没多想,立刻想到了使用类的序列化方式来实现.实际正是如此,很快,我的代码按我的想法就出来了.
一个RoomConfig类,包含room的基本信息,既有基础数据类型,也有对象在里面.代码片段如下:
members#region members
private int rommID = -1;
private OpWithOneTemplate list = new OpWithOneTemplate();
//.
#endregion
public properties#region public properties
public int RoomID
{
get
{
return rommID;
}
set
{
rommID = value;
}
}
public OpWithOneTemplate List
{
get
{
return list;
}
set
{
list = value;
}
}
//
#endregion
接下来定义一个ConfigManager.把存取操作的细节封装.
部分代码如下:
public class ConfigManager
{
make config#region make config
public static void MakeConfig(RoomConfig config, string filePath)
{
XmlSerializer formatter = new XmlSerializer(typeof(RoomConfig));
FileStream fs = new FileStream(filePath, FileMode.Create,
FileAccess.Write, FileShare.None);
formatter.Serialize(fs, config);
fs.Close();
}
#endregion
get config#region get config
public static RoomConfig2 GetConfig(string filePath)
{
XmlSerializer formatter = new XmlSerializer(typeof(RoomConfig));
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
RoomConfig config = (RoomConfig2)formatter.Deserialize(fs);
fs.Close();
return config;
}
#endregion
}
这样,就完成了需求,过程非常轻松.但,很快,我就从蝈蝈那里得到了新需求:必须使用,net2.0的推荐的方式对配置文件进行操作.参考了他的文章:http://blog.joycode.com/ghj/archive/2006/02/11/71309.aspx
本以为按着例子写一遍OK了,但实际上由于这里的配置对象包含子对象,直接用类似
config.Sections.Add("SettingsData", configData);
config.Save();
的方式立刻出错了,无法把对象和string进行转换.
然后试了许多方法,总是没法解决这个问题,现在想起来,始终是我把问题想的过于简单,这里面,需要额外做的工作还很多.
首先把RoomConfig从ConfigurationSection继承,顾名思义,这是表示一个section,里面还包含子对象;
在此处,我们包含了一个OpWithOneTemplate对象,我们对这个类也需要做额外的事.
把它从 ConfigurationElement继承,其实这种关系很明显,是section的单个对象.类里面的操作,与RoomConfig的属性操作大同小异,可以参照上面提到的蝈蝈的文章.
如果你的子对象包含的都是基础数据类型,那OK,到这一步,就算完成了,但偏偏在这里OpWithOneTemplate还包含子对象,并且那个子对象还是个集合类型!
又郁闷了一阵子,跟蝈蝈大哥讨论了这个问题.得出的结论是查资料.期间,我们参考了企业库的做法和http://blogs.conchango.com/pauloreichert/archive/2005/05/31/1514.aspx 这篇文章.虽然获得了许多启发,但依然没有解决问题.
郁闷的下班回去思考,最后换了一个思路终于解决了这个问题.集合的呈现,问题就在于无法自动的给你生成列表(实际上后来证明是可以的),那么我就给集合类增加一个专门用于写入配置文件的属性,这个属性代表了集合里的核心数据,并且通过它可以转换回数据集合.代码如下:(注,RoleList为OpWithOneTemplate包含的子类)
[Serializable()]
public class RoleList : ConfigurationElement
{
Private Static Variables#region Private Static Variables
private readonly ConfigurationProperty _propRoles;
private ConfigurationPropertyCollection _properties;
#endregion
members#region members
private ArrayList roles = new ArrayList();
#endregion
public properties#region public properties
public ArrayList Roles
{
get
{
MakeRoles(RoleString);
return roles;
}
set
{
roles = value;
}
}
[ConfigurationProperty("roles")]
public string RoleString
{
get
{
return (string)base[this._propRoles];
}
set
{
MakeRoles(value);
base[this._propRoles] = value;
}
}
/**//// <summary>
/// read only
/// </summary>
public int Count
{
get
{
return roles.Count;
}
}
#endregion
construct#region construct
public RoleList()
{
this._propRoles = new ConfigurationProperty("roles", typeof(string), null, ConfigurationPropertyOptions.IsRequired);
this._properties = new ConfigurationPropertyCollection();
this._properties.Add(this._propRoles);
this.Add(RoleEnum.Administrator);
}
#endregion
methods#region methods
public void Add(RoleEnum role)
{
if (!HasRole(role))
{
roles.Add(role);
base[this._propRoles] = GetRolesString();
}
}
/**//// <summary>
/// 队列里是否存在某角色
/// </summary>
/// <param name="role"></param>
/// <returns></returns>
public bool HasRole(RoleEnum role)
{
foreach (RoleEnum rl in roles)
{
if (rl == role)
return true;
}
return false;
}
/**//// <summary>
/// 返回角色字符串
/// </summary>
/// <returns></returns>
public string GetRolesString()
{
string str = string.Empty;
foreach (RoleEnum rl in roles)
{
str += rl.ToString() + ";";
}
return str;
}
/**//// <summary>
/// 反向生成角色队列
/// </summary>
/// <param name="str"></param>
private void MakeRoles(string str)
{
string[] list = str.Split(new char[]{';'},StringSplitOptions.RemoveEmptyEntries);
foreach (string li in list)
{
Add((RoleEnum)Enum.Parse(typeof(RoleEnum), li));
}
}
#endregion
}
这样,终所有的配置类里的信息都可以在配置文件里呈现了.
正当我兴冲冲跑来上班,要告诉蝈蝈我的解决方案时,蝈蝈却有了更好的解决方案,这里帮他介绍下,呵呵
这种方法依然是严格依照.net推荐的方式进行,处理集合时,使用ConfigurationElementCollection,上面提到的RoleList从这个类派生,具体的集合对象再细化成一个类(上面的方法是直接把数据封存到list里面了)
关键的地方还是RoleList,从ConfigurationElementCollection派生后,需要重写2个函数
protected override ConfigurationElement CreateNewElement()
{
//throw new Exception("The method or operation is not implemented.");
return new Role(RoleEnum.Administrator);
}
protected override object GetElementKey(ConfigurationElement element)
{
//throw new Exception("The method or operation is not implemented.");
Role r = element as Role;
if (r == null)
return "Administrator";
else
return r.RoleName;
}
这样,在返回集合对象时,将按上面的函数来创建单个对象,这也就是为什么要把数据再进一步封到一个类里的原因,要不Create函数就没法返回了.
Role类非常简单,如下:
/**//// <summary>
/// 只是单纯的把一个枚举封装起来,目的只是要使枚举值可被操作(从ConfigurationElement继承).
/// </summary> public class Role : ConfigurationElement
{
public Role(RoleEnum e)
{
this["RoleName"] = e;
}
[ConfigurationProperty("RoleName")]
public RoleEnum RoleName
{
get
{
return (RoleEnum)this["RoleName"];
}
set
{
this["RoleName"] = value;
}
}
到这里,3种配置文件存取的方法都介绍完了,写的很乱,也不知道对大家会不会有用,各取所需好了:)
参考文献:
http://blog.joycode.com/ghj/category/1257.aspx
http://blogs.conchango.com/pauloreichert/archive/2005/05/31/1514.aspx