使用 ConfigurationSection / ConfigurationElementCollection 从 web.config 创建一个嵌套的项目集合

Use ConfigurationSection / ConfigurationElementCollection to create a nested collection of items from web.config

我正在尝试在 web.config 中创建自定义分区组。它主要工作,但因以下错误而失败:

Unrecognized element 'add'. (web.config line 736) at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)....

代码中可能还有其他错误,但这是我无法通过的部分。

这是我的 web.config XML:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="systemEmailsGroup">
      <section name="systemEmails" type="SystemEmailsConfiguration" />
    </sectionGroup>
  </configSections>
  <!-- lots of other stuff -->
  <systemEmailsGroup>
    <systemEmails>
      <emails>
        <email name="SystemEmail01" defaultAddress="SystemEmail01@email.com" defaultName="John Doe 01" />
        <email name="SystemEmail02" source="UserName" username="Username02" defaultAddress="SystemEmail02@email.com" defaultName="Jane Doe 02" />
        <email name="SystemEmail03" defaultAddress="SystemEmail03@email.com" defaultName="John Doe 03" />
        <email name="SystemEmail04" source="UserName" username="Username04" />
        <email name="SystemEmail05" source="RoleName" rolename="administrators" defaultEmailName="SystemEmail02" />
        <email name="SystemEmailGroup01" source="Group">
          <add name="SystemEmail06" defaultAddress="SystemEmail06@email.com" defaultName="Jane Doe 06" /> 
          <!-- ^^^ LINE 736 ^^^ -->
          <add name="SystemEmail07" defaultAddress="SystemEmail07@email.com" defaultName="John Doe 07" />
          <add name="SystemEmail08" defaultAddress="SystemEmail08@email.com" defaultName="Jane Doe 08" />
        </email>
      </emails>
    </systemEmails>
  </systemEmailsGroup>
</configuration>

注意我把错误中提到的第736行标出来了

我的 class 结构如下。第一个继承ConfigurationSection,第二个继承ConfigurationElementCollection,最后一个继承ConfigurationElement。之后是 SystemEmailsElement 类型的扩展 class。它可能与错误无关,但为了完成起见,我将其包括在内。

public class SystemEmailsConfiguration : ConfigurationSection
{
    public static SystemEmailsConfiguration GetConfig()
    {
        return ConfigurationManager.GetSection("systemEmailsGroup/systemEmails") as SystemEmailsConfiguration;
    }

    [ConfigurationProperty("emails", IsDefaultCollection = false)]
    [ConfigurationCollection(typeof(SystemEmailsElementCollection), 
        AddItemName = "email", ClearItemsName = "clear", RemoveItemName = "remove")]
    public SystemEmailsElementCollection Emails
    {
        get
        {
            return base["emails"] as SystemEmailsElementCollection;
        }
    }

}

public enum SystemEmailsSource
{
    None = 0,
    UserName = 1,
    RoleName = 2,
    Group = 3
}

public class SystemEmailsElementCollection : ConfigurationElementCollection, IEnumerable<SystemEmailsElement>
{
    List<SystemEmailsElement> _elements = new List<SystemEmailsElement>();

    public SystemEmailsElement this[int index]
    {
        get
        {
            return _elements[index];
        }
        set
        {
            if (index >= 0 && index < _elements.Count)
            {
                _elements.RemoveAt(index);
            }
            _elements.Insert(index, value);
        }
    }

    public SystemEmailsElement this[string key]
    {
        get
        {
            return _elements.Where(e => e.Name == key).FirstOrDefault();
        }
        set
        {
            var el = _elements.Where(e => e.Name == key).FirstOrDefault();
            if (el != null)
            {
                int index = _elements.IndexOf(el);
                _elements.RemoveAt(index);
                _elements.Insert(index, value);
            }
            else
                _elements.Add(value);
        }
    }

    protected override ConfigurationElement CreateNewElement()
    {
        var el = new SystemEmailsElement();
        _elements.Add(el);
        return el;
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return _elements.Find(e => e.Equals(element)).Name;
    }

    public new IEnumerator<SystemEmailsElement> GetEnumerator()
    {
        return _elements.GetEnumerator();
    }
}


public class SystemEmailsElement : ConfigurationElement 
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get
        {
            return this["name"] as string;
        }
    }

    [ConfigurationProperty("source", DefaultValue = SystemEmailsSource.None)]
    public SystemEmailsSource Source
    {
        get
        {
            return (this["source"] != null) ? (SystemEmailsSource)this["source"] : SystemEmailsSource.None;
        }
    }

    [ConfigurationProperty("defaultAddress")]
    public string DefaultAddress
    {
        get
        {
            return this["defaultAddress"] as string;
        }
    }

    [ConfigurationProperty("defaultName")]
    public string DefaultName
    {
        get
        {
            return this["defaultName"] as string;
        }
    }

    [ConfigurationProperty("defaultEmailName")]
    public string DefaultEmailName
    {
        get
        {
            return this["defaultEmailName"] as string;
        }
    }

    [ConfigurationProperty("username")]
    public string UserName
    {
        get
        {
            return this["username"] as string;
        }
    }

    [ConfigurationProperty("rolename")]
    public string RoleName
    {
        get
        {
            return this["rolename"] as string;
        }
    }

    [ConfigurationProperty("email")]
    [ConfigurationCollection(typeof(SystemEmailsElementCollection), 
        AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
    public SystemEmailsElementCollection Emails
    {
        get
        {
            return base["email"] as SystemEmailsElementCollection;
        }
    }
}


public static class SystemEmailsElementExtenstions
{
    /// <summary>
    /// Returns true if the SystemEmailsElement may contain a group of email addresses.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool IsGroup(this SystemEmailsElement value)
    {
        return (value.Source == SystemEmailsSource.Group || value.Source == SystemEmailsSource.RoleName);
    }

    /// <summary>
    /// Gets the email or emails result of this section as a MailAddressCollection.
    /// Returns an empty MailAddressCollection if not able to create any MailAddress objects.
    /// </summary>
    /// <returns></returns>
    public static MailAddressCollection GetEmails(this SystemEmailsElement value)
    {
        var emails = new MailAddressCollection();
        MailAddress eml = null;

        if (value.IsGroup())
        {
            switch (value.Source)
            {
                case SystemEmailsSource.RoleName:
                    if (value.RoleName != null)
                    {
                        foreach (UsersNames user in AppPublic.GetUsersByRole(value.RoleName))
                        {
                            eml = AppPublic.GetEmailByUserID(user.UserId);
                            if (eml != null)
                                emails.Add(eml);
                        }
                    }
                    break;

                default: //Group
                    foreach (var el in value.Emails)
                    {
                        foreach (MailAddress e in el.GetEmails())
                            emails.Add(e);
                    }
                    break;
            }
        }
        else
        {
            emails.Add(value.GetEmail());
        }

        if (emails.Count == 0)
        {
            eml = value.CreateEmailFromDefaults();
            if (eml != null)
                emails.Add(eml);
        }
        return emails;
    }

    /// <summary>
    /// Gets the email result of this section or the first item in a group as a MailAddress object.
    /// Returns null if not able to create the MailAddress.
    /// </summary>
    /// <returns></returns>
    public static MailAddress GetEmail(this SystemEmailsElement value)
    {
        if (value.IsGroup())
        {
            return value.GetEmails().FirstOrDefault();
        }
        else
        {
            MailAddress eml = null;

            if (value.Source == SystemEmailsSource.UserName)
            {
                if (value.UserName != null)
                    eml = AppPublic.GetEmailByUserName(value.UserName);
            }

            if (eml == null)
                eml = CreateEmailFromDefaults(value);

            return eml;
        }
    }

    /// <summary>
    /// Creates a mail address object using the default attribute values from the SystemEmailElement.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    private static MailAddress CreateEmailFromDefaults(this SystemEmailsElement value)
    {
        MailAddress eml = null;

        //DefaultEmailName takes precedence.
        if (value.DefaultEmailName != null)
        {
            var el = SystemEmailsConfiguration.GetConfig().Emails[value.DefaultEmailName];
            if (el != null)
                eml = el.GetEmail();
        }

        //Fall back to DefaultAddress.
        if (eml == null && value.DefaultAddress != null)
        {
            if (value.DefaultName == null)
                eml = new MailAddress(value.DefaultAddress);
            else
                eml = new MailAddress(value.DefaultAddress, value.DefaultName);
        }

        return eml;
    }
}

在理想世界中,<add> 节点将重命名为 <email> 节点,并且此代码将是递归的并允许任意(理论上)数量的节点深度。

但是,如果我能让第一个 <add> 级别正常工作,那就太好了。几个小时以来,我一直在努力寻找属性、签名、XML 等的正确组合。

谢谢!!

要修复您的即时错误,您只需将包装 <email> 标记添加到您的 <add/> 元素。那是因为你的 SystemEmailsElement 包含 Emails 属性 这是根标签定义为 "email" 的集合(通过 [ConfigurationProperty("email")])。像这样:

<systemEmails>
  <emails>
    <email name="SystemEmail01" defaultAddress="SystemEmail01@email.com" defaultName="John Doe 01" />
    <email name="SystemEmail02" source="UserName" username="Username02" defaultAddress="SystemEmail02@email.com" defaultName="Jane Doe 02" />
    <email name="SystemEmail03" defaultAddress="SystemEmail03@email.com" defaultName="John Doe 03" />
    <email name="SystemEmail04" source="UserName" username="Username04" />
    <email name="SystemEmail05" source="RoleName" rolename="administrators" defaultEmailName="SystemEmail02" />
    <email name="SystemEmailGroup01" source="Group">
      <email>
        <add name="SystemEmail06" defaultAddress="SystemEmail06@email.com" defaultName="Jane Doe 06" />
        <!-- ^^^ LINE 736 ^^^ -->
        <add name="SystemEmail07" defaultAddress="SystemEmail07@email.com" defaultName="John Doe 07" />
        <add name="SystemEmail08" defaultAddress="SystemEmail08@email.com" defaultName="Jane Doe 08" />
      </email>
    </email>
  </emails>
</systemEmails>

现在,要使其像理想世界一样工作,您可以像这样利用 ConfigurationProperty 属性的 IsDefaultCollection 属性:

[ConfigurationProperty("", IsDefaultCollection = true)]
[ConfigurationCollection(typeof (SystemEmailsElementCollection),
    AddItemName = "email")]
public SystemEmailsElementCollection Emails
{
    get { return base[""] as SystemEmailsElementCollection; }
}

这将允许您按照您期望的方式创建递归电子邮件节点:

  <emails>
    <email name="SystemEmail01" defaultAddress="SystemEmail01@email.com" defaultName="John Doe 01" />
    <email name="SystemEmail02" source="UserName" username="Username02" defaultAddress="SystemEmail02@email.com" defaultName="Jane Doe 02" />
    <email name="SystemEmail03" defaultAddress="SystemEmail03@email.com" defaultName="John Doe 03" />
    <email name="SystemEmail04" source="UserName" username="Username04" />
    <email name="SystemEmail05" source="RoleName" rolename="administrators" defaultEmailName="SystemEmail02" />
    <email name="SystemEmailGroup01" source="Group">
      <email name="SystemEmail06" defaultAddress="SystemEmail06@email.com" defaultName="Jane Doe 06">
        <email name="SystemEmail04" source="UserName" username="Username04" />
        <email name="SystemEmail05" source="RoleName" rolename="administrators" defaultEmailName="SystemEmail02" />
      </email>
      <!-- ^^^ LINE 736 ^^^ -->
      <email name="SystemEmail07" defaultAddress="SystemEmail07@email.com" defaultName="John Doe 07" />
      <email name="SystemEmail08" defaultAddress="SystemEmail08@email.com" defaultName="Jane Doe 08" />
    </email>
  </emails>