65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使 AppSettings 能够为一个键使用多个值

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (20投票s)

2003 年 6 月 11 日

3分钟阅读

viewsIcon

283861

downloadIcon

2992

使用反射修复 NameValueSectionHandler 并无缝使用它。

Sample Image - NameValueMultipleSectionHandler.png

引言

.NET 框架提供的 NameValueSectionHandler 存在一个严重缺陷:尽管 NameValueCollection 能够为一个给定的键提供多个值,但当使用此处理程序时,我们只能获得该键添加的最后一个值。

由于 ConfigurationSettings.AppSettings(读取应用程序设置的最简单标准)以一种非常特殊的方式使用此处理程序,开发人员通常必须创建一个自定义部分,并使用 ConfigurationSettings.GetConfig 和大量的强制转换来读取值。

我们将破解 AppSettings 以使我们的生活更轻松,并学习一些有趣的事情。

为什么它不起作用

它不起作用的原因在于一行代码。使用反编译器(例如 Lutz Roeder 出色的 Reflector),我们将找到将新值添加到配置文件的集合中的那行代码,如下所示

collection[key] = value;

而不是

collection.Add(key, value);

您可能知道,如果该键已存在于集合中,则第一种形式会替换该值。

更多问题

更糟糕的是,返回的集合实际上是一个 ReadOnlyNameValueCollection,一个派生类。AppSettings 的 get 方法转换了处理程序返回的 NameValueCollection,并且由于此类型是内部的,我们(不能直接)实例化它。

但我们都是硬核开发人员,不会让这种愚蠢的事情困扰我们,对吧?

创建新处理程序

创建 IConfigurationSectionHandler 很容易。我们得到一个对应于app.configweb.config 文件中该部分的 XmlNode,以及父部分和上下文(这仅在 ASP.NET 文件中很重要)。

Create 方法的代码非常简单

NameValueCollection collection = null;

//Check if we have found the ReadOnlyNameValueCollection Type
if (readOnlyNameValueCollectionType != null)
{
    collection = CreateCollection(parent);
    foreach (XmlNode xmlNode in section.ChildNodes)
    {
        //Skip non-elements like section attributes, comments, etc
        if (xmlNode.NodeType == XmlNodeType.Element)
        {
            switch (xmlNode.Name)
            {
                case "add":
                    collection.Add(xmlNode.Attributes["key"].Value,
                        xmlNode.Attributes["value"].Value);
                    break;
                case "remove":
                    collection.Remove(xmlNode.Attributes["key"].Value);
                    break;
                case "clear":
                    collection.Clear();
                    break;
            }
        }
    }
}
return collection;

现在,这里的棘手部分是创建一个 ReadOnlyNameValueCollection。由于它是内部的,我们必须使用反射来创建它。

我们将在 static 方法中搜索类及其构造函数,因此它只执行一次。在 ASP.NET 应用程序中,我们可以使用 HttpRuntime.Cache 来存储它们并节省一些额外的时间(否则,此代码将为每个请求调用)。

readOnlyNameValueCollectionType = typeof(NameValueCollection).
    Assembly.GetType("System.Configuration.ReadOnlyNameValueCollection");
if (readOnlyNameValueCollectionType != null)
{
    //Get the constructors for the specified parameter types
    readOnlyNameValueCollectionConstructor1 =
        readOnlyNameValueCollectionType.GetConstructor(
        new Type[] {readOnlyNameValueCollectionType});

    readOnlyNameValueCollectionConstructor2 =
        readOnlyNameValueCollectionType.GetConstructor(
        new Type[] {typeof(IHashCodeProvider), typeof(IComparer)});
}

现在,当我们需要一个 ReadOnlyNameValueCollection 实例时,我们可以使用 static CreateCollection 方法

if (parent == null)
{
    //Create empty collection
    return
        (NameValueCollection)readOnlyNameValueCollectionConstructor2.Invoke(
        new object[] {
            new CaseInsensitiveHashCodeProvider(
            CultureInfo.InvariantCulture),
            new CaseInsensitiveComparer(
            CultureInfo.InvariantCulture)});
}
else
{
    //Copy parent elements
    return
        (NameValueCollection)readOnlyNameValueCollectionConstructor1.Invoke(
        new object[] {parent});

}

使用处理程序

为了让 AppSettings 使用我们的处理程序而不是默认处理程序,我们必须通过删除 appSettings 部分并创建一个新的部分来覆盖默认处理程序。不幸的是,这必须针对我们所有的应用程序完成,但我认为这是值得的。

这是一个示例 app.config 文件

<?xml version="1.0" encoding="Windows-1252" ?>
<configuration>
    <configSections>
        <remove name="appSettings" />
        <section name="appSettings"
            type="DigBang.Configuration.NameValueMultipleSectionHandler,
            Configuration" />
    </configSections>
    <appSettings>
        <add key="file" value="myfile1" />
        <add key="file" value="myfile2" />
        <add key="connectionString" value="my connection string" />
        <add key="another multiple values key" value="my value 1" />
        <add key="another multiple values key" value="my value 2" />
        <add key="another multiple values key" value="my value 3" />
    </appSettings>
</configuration>

现在,您可以像往常一样使用 ConfigurationSettings.AppSettings[key],以及 ConfigurationSettings.AppSettings.GetValues(key),它返回一个 string[],其中包含该键的所有值。

您将在演示项目中找到一个示例,该示例使用了这两种方法。请记住,如果您使用 ConfigurationSettings.AppSettings[key] 而不是 GetValues 读取具有多个值的键,您将得到一个以逗号分隔的字符串,其中包含所有连接的值。

当然,如果您不想,则不必覆盖默认行为。您可以创建自己的 configSections,使用此处理程序,并使用 ConfigurationSettings.GetConfig 方法(您必须进行强制转换)或提供的 NameValueMultipleSectionHandler.GetConfig 方法(它返回一个 NameValueCollection)(如果将其与其处理程序未返回 NameValueCollection 的部分一起使用,它将抛出 InvalidCastException)。

建议阅读(来自 VS.NET 文档)

历史

  • 2003-07-07:

    优化类型搜索

  • 2003-06-11:

    第一版

© . All rights reserved.