Как указать коллекцию в пользовательском configSection - PullRequest
8 голосов
/ 05 января 2010

Я хотел бы иметь конфигурационный файл xml следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
    <section name="plugins" type="MyApp.PluginsConfiguration, MyApp"/>
</configSections>

<plugins>
    <use assembly="MyApp.Plugin1.dll" />
    <use assembly="MyApp.Plugin2.dll" />
</plugins>
</configuration>

Как точно организовать взаимодействие между классами PluginsConfigurations, UsePluginCollection и UsePlugin в моем коде?

Я нашел это учебное пособие онлайн, но в нем был бы элемент внутри плагинов, окружающий коллекцию использования, и мне это не нужно.

Это - это то, что я имею до сих пор, но оно не совсем верно

Ответы [ 2 ]

17 голосов
/ 05 января 2010

Если MyApp.PluginsConfiguration является ConfigurationSection, то вы можете определить новый класс, который наследуется от ConfigurationElementCollection, и сделать новый класс ConfigurationProperty для MyApp.PluginsConfiguration

Проверьте эту статью для получения подробной информации об этих типах. Я также написал о с вложенными свойствами, но не специально для коллекций.

Редактировать: Вот с некоторым кодом. Данный бит в файле web.config:

<configSections>
    <section name="plugins"
             type="WebApplication1.PluginsConfiguration, WebApplication1"/>
</configSections>
<plugins>
    <use assembly="MyApp.Plugin1.dll"/>
    <use assembly="MyApp.Plugin2.dll"/>
</plugins>

Вот классы, чтобы это произошло. Обратите внимание, что, возможно, есть исключения NullReferenceExceptions, которые необходимо обработать.

namespace WebApplication1
{
    public class PluginsConfiguration : ConfigurationSection
    {
        private static ConfigurationPropertyCollection properties;
        private static ConfigurationProperty propPlugins;

        static PluginsConfiguration()
        {
            propPlugins = new ConfigurationProperty(null, typeof(PluginsElementCollection),
                                                          null,
                                                          ConfigurationPropertyOptions.IsDefaultCollection);
            properties = new ConfigurationPropertyCollection { propPlugins };
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                return properties;
            }
        }

        public PluginsElementCollection Plugins
        {
            get
            {
                return this[propPlugins] as PluginsElementCollection;
            }
        }
    }

    public class PluginsElementCollection : ConfigurationElementCollection
    {
        public PluginsElementCollection()
        {
            properties = new ConfigurationPropertyCollection();
        }

        private static ConfigurationPropertyCollection properties;

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                return properties;
            }
        }

        public override ConfigurationElementCollectionType CollectionType
        {
            get
            {
                return ConfigurationElementCollectionType.BasicMap;
            }
        }

        protected override string ElementName
        {
            get
            {
                return "use";
            }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new PluginsElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            var elm = element as PluginsElement;
            if (elm == null) throw new ArgumentNullException();
            return elm.AssemblyName;
        }
    }

    public class PluginsElement : ConfigurationElement
    {
        private static ConfigurationPropertyCollection properties;
        private static ConfigurationProperty propAssembly;

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                return properties;
            }
        }

        public PluginsElement()
        {
            propAssembly = new ConfigurationProperty("assembly", typeof(string),
                                                          null,
                                                          ConfigurationPropertyOptions.IsKey);
            properties = new ConfigurationPropertyCollection { propAssembly };
        }

        public PluginsElement(string assemblyName)
            : this()
        {
            AssemblyName = assemblyName;
        }

        public string AssemblyName
        {
            get
            {
                return this[propAssembly] as string;
            }
            set
            {
                this[propAssembly] = value;
            }
        }
    }
}

И чтобы получить к нему доступ, этот фрагмент кода должен помочь:

        var cfg = WebConfigurationManager.GetWebApplicationSection("plugins") as PluginsConfiguration;
        var sb = new StringBuilder();
        foreach(PluginsElement elem in cfg.Plugins)
        {
            sb.AppendFormat("{0}<br/>", elem.AssemblyName);
        }
        testLabel.Text = sb.ToString();

По сути, у нас есть ConfigurationSection, который обрабатывает plugins-элемент. Здесь мы указываем свойство ConfigurationElementCollection и объявляем его коллекцией по умолчанию (теоретически вы можете иметь несколько разных коллекций в одном корневом узле).

PluginsElementCollection реализует ConfigurationElementCollection. ElementName должно быть именем тега, в нашем случае «use». Кроме того, GetElementKey должен быть переопределен и должен возвращать атрибут, который является уникальным среди записей.

Затем PluginsElement реализует одноразовый тег. Мы определяем только одно свойство: AssemblyName, которое сопоставляется с атрибутом assembly.

Я не утверждаю, что полностью понимаю все это (особенно ConfigurationElementCollection и его различные свойства BaseAdd, BaseGet и т. Д. Здесь на самом деле не рассматриваются), но я могу утверждать, что это работает:)

Кроме того, он не использует никаких атрибутов. Я ненавижу атрибуты - к счастью, все эти атрибуты могут быть преобразованы в правильный код. Вы можете использовать один или другой (или оба).

4 голосов
/ 05 января 2010

На основе этого руководства вы можете легко создать конфигурацию XML следующим образом:

<MyApp>
  <Plugins>
    <add assembly="MyApp.Plugin1.dll" />
    <add assembly="MyApp.Plugin2.dll" />
  </Plugins>
</MyApp>

Что я считаю вполне приемлемым. Есть ли причина, по которой это было бы неприемлемо?

Edit:

Я не уверен, как именно это сделать с помощью учебника. Вам понадобится какой-то промежуточный элемент (например, в учебнике есть «действия») - Джордж Мауэр

Хорошо, попробуйте это для размера. У меня могут быть некоторые опечатки, так как я копирую / вставляю / редактирую некоторый код, который я использовал, который делает то, что вы хотите, но он должен работать с определенным выше XML

public sealed class MyAppConfiguration : ConfigurationSection
{
    public const string MyAppConfigurationTagName = "MyApp";
    [ConfigurationProperty(MyAppConfigurationTagName, IsRequired = true)]
    public PluginConfigurationCollection Plugins
    {
        get
        {
            return this[MyAppConfigurationTagName] as PluginConfigurationCollection;
        }
        set
        {
            this[MyAppConfigurationTagName] = value;
        }
    }
}

public sealed class PluginConfigurationItem : ConfigurationElement
{
    // repeat this pattern for each additional attribute you want in the <add /> tag. 
    // Only the assembly="foo.dll" portion is defined in this class, and is accessed 
    // via the AssemblyName property. 
    public const string AssemblyPropertyName = "assembly";
    [ConfigurationProperty(AssemblyPropertyName, IsRequired = true, IsKey = true)]
    public string AssemblyName
    {
        get
        {
            return this[AssemblyPropertyName] as string;
        }
        set
        {
            this[AssemblyPropertyName] = value;
        }
    }
}

public class PluginConfigurationCollection : ConfigurationElementCollection, IEnumerable<PluginConfigurationItem>
{
    public const string PluginsElementName = "Plugins";

    protected override ConfigurationElement CreateNewElement()
    {
        return new PluginConfigurationItem();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((PluginConfigurationItem)element).AssemblyName;
    }

    protected override string ElementName
    {
        get
        {
            return PluginsElementName;
        }
    }

    // this is extraneous, but I find it very useful for enumerating over a configuration collection in a type-safe manner.
    #region IEnumerable<PluginConfigurationItem> Members

    public new IEnumerator<PluginConfigurationItem> GetEnumerator()
    {
        foreach(PluginConfigurationItem item in (this as IEnumerable))
        {
            yield return item;
        }
    }

    #endregion
}
...