Есть ли способ использовать словарь или XML в настройках приложения? - PullRequest
4 голосов
/ 19 апреля 2010

Я должен хранить сложный тип в настройках приложения. Я думал, что лучше всего хранить его как XML.

Проблема в том, что я не знаю, как хранить XML. Я предпочитаю хранить его как управляемый XML, а не использовать просто строку необработанного XML, которая должна анализировать его при каждом доступе. Мне удалось установить для столбца Type параметра значение XDocument, но я не смог установить его значение.

Есть ли способ использовать XDocument или XML в настройках приложения?

Обновление

Я нашел способ, просто отредактировав файл .settings с помощью редактора xml.

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

The property 'Setting' could not be created from it's default value.
Error message: There is an error in XML document (1, 41).

Будут оценены любые идеи.

Ответы [ 2 ]

2 голосов
/ 20 апреля 2010

То, что я в итоге сделал, потому что это был путь наименьшего сопротивления, заключалось в использовании встроенных классов конфигурации в .NET Fx. Несмотря на то, что приведенный ниже код написан на C #, вы сможете без труда преобразовать его в VB.NET (или отредактировать и скомпилировать его в сборку, на которую вы можете ссылаться из своего проекта).

Вы заметите, что класс ConfigurationElementCollection может быть легко преобразован в словарь пар ключ / значение (вам может потребоваться использовать отражение для пар значений или классов, которые вы хотите сохранить, поскольку пары значений могут принимать классы настроек, которые наследовать от ConfigurationElement в качестве аргумента конструктора).

// ConfigurationElement.cs
public class ConfigurationElement : System.Configuration.ConfigurationElement
{
    protected T GetValue<T>(string key, T defaultValue)
    {
        var value = default(T);
        if (base[key] != null)
        {
            var str = base[key].ToString();
            try
            {
                if (!String.IsNullOrEmpty(str))
                    value = (T)Convert.ChangeType(str, typeof(T));
            }
            catch // use the default
            {
            }
        }

        return value;
    }
}

// ConfigurationElementCollection.cs
public abstract class ConfigurationElementCollection<TElement,TKey> : 
    ConfigurationElementCollection, 
    IEnumerable<TElement> where TElement : System.Configuration.ConfigurationElement, new()
{
    public TElement this[int index]
    {
        get { return (TElement)BaseGet(index); }
    }

    public TElement this[TKey key]
    {
        get { return (TElement)BaseGet(key); }
    }

    protected override System.Configuration.ConfigurationElement CreateNewElement()
    {
        return new TElement();
    }

    protected override object GetElementKey(System.Configuration.ConfigurationElement element)
    {
        return GetElementKey((TElement)element);
    }
    protected abstract TKey GetElementKey(TElement element);

    public TKey[] GetAllKeys()
    {
        var keys = BaseGetAllKeys();
        var ret = new TKey[keys.Length];
        for (var i = 0; i < keys.Length; i++)
            ret[i] = (TKey)keys[i];

        // done
        return ret; 
    }
    public void Add(TElement element)
    {
        BaseAdd(element);
    }
    public void Remove(TElement element)
    {
        BaseRemove(element);
    }
    public void Clear()
    {
        BaseClear();
    }

    IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
    {
        foreach (TElement element in this)
        {
            yield return element;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new System.NotImplementedException();
    }
}

И вот пример, где я использую приведенный выше код базового класса для нашей стратегии шардинга в нашей системе (имена изменены для защиты невинных):

<!-- web.config -->
<!-- ... -->
<configuration>
    <configSections>
        <section name="sharding" type="Domain.ShardingSection, Domain.Configuration" />
    </configSections>
</configuration>
<!-- ... -->
<sharding>
    <configurationMappings>
        <add lastDigit="0" sqlMapFileName="Shard-0.SqlMap.config" />
        <add lastDigit="1" sqlMapFileName="Shard-1.SqlMap.config" />
        <add lastDigit="2" sqlMapFileName="Shard-2.SqlMap.config" />
        <add lastDigit="3" sqlMapFileName="Shard-3.SqlMap.config" />
        <add lastDigit="4" sqlMapFileName="Shard-4.SqlMap.config" />
        <add lastDigit="5" sqlMapFileName="Shard-5.SqlMap.config" />
        <add lastDigit="6" sqlMapFileName="Shard-6.SqlMap.config" />
        <add lastDigit="7" sqlMapFileName="Shard-7.SqlMap.config" />
        <add lastDigit="8" sqlMapFileName="Shard-8.SqlMap.config" />
        <add lastDigit="9" sqlMapFileName="Shard-9.SqlMap.config" />
    </configurationMappings>
</sharding>

А затем классы конфигурации, представленные приведенным выше экземпляром XML:

// ShardElement.cs
public class ShardElement : ConfigurationElement
{
    [ConfigurationProperty("lastDigit", IsKey=true, IsRequired=true)]
    public int LastDigit
    {
        get { return (int)this["lastDigit"]; }
    }

    [ConfigurationProperty("sqlMapFileName", IsRequired=true)]
    public string SqlMapFileName
    {
        get { return (string)this["sqlMapFileName"]; }
    }
}

// ShardElementCollection.cs
public class ShardElementCollection : ConfigurationElementCollection<ShardElement, int>
{
    protected override int GetElementKey(ShardElement element)
    {
        return element.LastDigit;
    }
}

 // ShardingSection.cs
 public class ShardingSection : ConfigurationSection
{
    public const string Name = "sharding";

    [ConfigurationProperty("configurationMappings")]
    public ShardingElementCollection ConfigurationMappings
    {
        get { return (ShardingElementCollection)base["configurationMappings"]; }
    }
}

Хотя это не истинный IDictionary в файле * .config, он может обрабатывать задание, и если ваш файл конфигурации обновляется во время выполнения, вам не нужно перезапускать приложение или перезапускать AppPool для получения новых значений.

2 голосов
/ 20 апреля 2010

То, что я сделал (даже мне не очень нравится - но работает):

Я создал простые сериализуемые классы своих значений:

<Xml.Serialization.XmlRoot("Rooms")> _
Public Class Rooms : Inherits List(Of Room)
End Class

<Serializable()> _
Public Class Room
    Private m_Room As String
    Public Property Room() As String
        Get
            Return m_Room
        End Get
        Set(ByVal value As String)
            m_Room = value
        End Set
    End Property

    Private m_Sections As New List(Of Section)
    Public ReadOnly Property Sections() As List(Of Section)
        Get
            Return m_Sections
        End Get
    End Property
End Class

<Serializable()> _
Public Class Section
    Private m_Section As String
    Public Property Section() As String
        Get
            Return m_Section
        End Get
        Set(ByVal value As String)
            m_Section = value
        End Set
    End Property
End Class

Затем в файле .settings я отредактировал тип настройки (файл .setting, открытый с помощью редактора xml) на полное имя Rooms (т.е. MyProject.Rooms). Затем я сделал сам образец десериализованного образца и скопировал его в поле значения в редакторе .settings, чтобы иметь значение по умолчанию. это прекрасно работает Это действительно не словарь, но я все еще мог бы реализовать его в классе Rooms и внутреннем словаре (который возвращает Sections by Room.Room.

Любые хорошие идеи все еще приветствуются, я все еще ищу способ использовать IDictinoary s в файле .settings.

Кроме того, я открыл для нее соединение , будьте добры и проголосуйте!

...