2014-11-19 9 views
2

Я пытаюсь реализовать пользовательский раздел конфигурации, чтобы я мог загрузить список определенных пользователем элементов и не иметь большой удачи. Я прочитал следующие сообщения, но я до сих пор не могу понять, что я делаю неправильно. Они кажутся хорошими проводниками, но я упускаю здесь какой-то важный факт. Я надеюсь, кто-то может указать, что именно.Где я ошибаюсь в ConfigurationSections?

Это мой тест. Когда я перехожу через него, config остается пустым. Это как вызов GetSection ничего не делает.

[TestClass] 
public class ToDoConfigTests 
{ 
    [TestMethod] 
    public void TestGetTodoAttribute() 
    { 
     var config = ConfigurationManager.GetSection("ToDoListAttributesSection") as ToDoItemsConfigurationSection; 

     Assert.Fail(); 

    } 
} 

Мои настройки Классы:

using System.Configuration; 
using Rubberduck.ToDoItems; 

namespace Rubberduck.Config 
{ 

    public class ToDoItemsConfigurationCollection : ConfigurationElementCollection 
    { 
     protected override ConfigurationElement CreateNewElement() 
     { 
      return new ToDoListAttributeElement(); 
     } 

     protected override object GetElementKey(ConfigurationElement element) 
     { 
      return ((ToDoListAttributeElement)element).Comment; 
     } 
    } 

    public class ToDoItemsConfigurationSection : ConfigurationSection 
    { 
     [ConfigurationProperty("ToDoListAttributes", IsRequired = true, IsDefaultCollection=true)] 
     public ToDoItemsConfigurationCollection ToDoListAttributes 
     { 
      get { return (ToDoItemsConfigurationCollection)this["ToDoListAttributes"]; } 
      set { this["ToDoListAttributes"] = value; } 
     } 
    } 

    public class ToDoListAttributeElement : ConfigurationElement 
    { 
     [ConfigurationProperty("TaskPriority", DefaultValue = TaskPriority.Low, IsRequired = true)] 
     public TaskPriority Priority 
     { 
      get { return (TaskPriority)this["TaskPriority"]; } 
      set { this["TaskPriority"] = value; } 
     } 

     [ConfigurationProperty("Comment",IsKey=true, IsRequired = true)] 
     public string Comment 
     { 
      get { return (string)this["Comment"]; } 
      set { this["Comment"] = value; } 
     } 
    } 
} 

Наконец, app.config файл:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
     <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > 
      <section name="Rubberduck.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> 
     </sectionGroup> 
     <section name="ToDoListAttributesSection" type="Rubberduck.Config.ToDoItemsConfigurationSection, Rubberduck.Config"/> 
    </configSections> 
    <ToDoListAttributesSection> 
     <ToDoListAttributes> 
     <add Comment="note" TaskPriority="0" /> 
     <add Comment="todo" TaskPriority="1" /> 
     <add Comment="bug" TaskPriority="2"/> 
     </ToDoListAttributes> 
    </ToDoListAttributesSection> 
</configuration> 
+0

Мне просто интересно. Если вы удалите кусок «как ToDoItemsConfigurationSection», поместите контрольную точку в «Assert» и отлаживаете свой тест, есть ли что-нибудь в переменной 'config', когда вы ее проверяете? –

+0

Я просто попробовал @GrantWinney. Он остается равным нулю. – RubberDuck

ответ

1

Секрет, почему это не работает, имеет отношение к деталям о моем программном обеспечении, о котором я не сообщал, когда писал этот вопрос.

Мое приложение не является самостоятельным приложением. Это надстройка COM для редактора VBA. Таким образом, это файл * .dll, а не * .exe. App.config файлы работают только с исполняющей сборкой (* .exe), поэтому мой код не работал. Есть few good solutions here, но я закончил rolling my own configuration, используя XML Serialization.

Ниже приведен код, который я использовал. Также можно найти in the Rubberduck repository hosted on GitHub, если вы предпочитаете смотреть на него.

Целое решение - это интерфейс IConfigurationService и реализация ConfigurationLoader, которая позволяет мне читать и записывать в xml-файл, где хранится конфигурация. (Версия здесь упрощена для обработки только исходного кода.)

IConfigurationService:

using System; 
using System.Runtime.InteropServices; 
using System.Collections.Generic; 

namespace Rubberduck.Config 
{ 
    [ComVisible(false)] 
    public interface IConfigurationService 
    { 
     Configuration GetDefaultConfiguration(); 
     ToDoMarker[] GetDefaultTodoMarkers(); 
     Configuration LoadConfiguration(); 
     void SaveConfiguration<T>(T toSerialize); 
    } 
} 

ConfigurationLoader:

public class ConfigurationLoader : IConfigurationService 
{ 
    private static string configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "rubberduck.config"); 

    /// <summary> Saves a Configuration to Rubberduck.config XML file via Serialization.</summary> 
    public void SaveConfiguration<T>(T toSerialize) 
    { 
     XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); 
     using (TextWriter textWriter = new StreamWriter(configFile)) 
     { 
      xmlSerializer.Serialize(textWriter, toSerialize); 
     } 
    } 

    /// <summary> Loads the configuration from Rubberduck.config xml file. </summary> 
    /// <remarks> If an IOException occurs, returns a default configuration.</remarks> 
    public Configuration LoadConfiguration() 
    { 
     try 
     { 
      using (StreamReader reader = new StreamReader(configFile)) 
      { 
       var deserializer = new XmlSerializer(typeof(Configuration)); 
       var config = (Configuration)deserializer.Deserialize(reader); 

       //deserialization can silently fail for just parts of the config, 
       // so we null check and return defaults if necessary. 
       if (config.UserSettings.ToDoListSettings == null) 
       { 
        config.UserSettings.ToDoListSettings = new ToDoListSettings(GetDefaultTodoMarkers()); 
       } 

       return config; 
      } 
     } 
     catch (IOException) 
     { 
      return GetDefaultConfiguration(); 
     } 
     catch (InvalidOperationException ex) 
     { 
      var message = ex.Message + System.Environment.NewLine + ex.InnerException.Message + System.Environment.NewLine + System.Environment.NewLine + 
        configFile + System.Environment.NewLine + System.Environment.NewLine + 
        "Would you like to restore default configuration?" + System.Environment.NewLine + 
        "Warning: All customized settings will be lost."; 

      DialogResult result = MessageBox.Show(message, "Error Loading Rubberduck Configuration", MessageBoxButtons.YesNo,MessageBoxIcon.Exclamation); 

      if (result == DialogResult.Yes) 
      { 
       var config = GetDefaultConfiguration(); 
       SaveConfiguration<Configuration>(config); 
       return config; 
      } 
      else 
      { 
       throw ex; 
      } 
     } 
    } 

    public Configuration GetDefaultConfiguration() 
    { 
     var userSettings = new UserSettings(new ToDoListSettings(GetDefaultTodoMarkers())); 

     return new Configuration(userSettings); 
    } 

    public ToDoMarker[] GetDefaultTodoMarkers() 
    { 
     var note = new ToDoMarker("NOTE:", TodoPriority.Low); 
     var todo = new ToDoMarker("TODO:", TodoPriority.Normal); 
     var bug = new ToDoMarker("BUG:", TodoPriority.High); 

     return new ToDoMarker[] { note, todo, bug }; 
    } 
} 

Конфигурация:

using System; 
using System.IO; 
using System.Xml.Serialization; 
using System.Runtime.InteropServices; 

namespace Rubberduck.Config 
{ 
    [ComVisible(false)] 
    [XmlTypeAttribute(AnonymousType = true)] 
    [XmlRootAttribute(Namespace = "", IsNullable = false)] 
    public class Configuration 
    { 
     public UserSettings UserSettings { get; set; } 

     public Configuration() 
     { 
      //default constructor required for serialization 
     } 

     public Configuration(UserSettings userSettings) 
     { 
      this.UserSettings = userSettings; 
     } 
    } 
} 

Настройки пользователя:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Xml.Serialization; 
using System.Runtime.InteropServices; 

namespace Rubberduck.Config 
{ 
    [ComVisible(false)] 
    [XmlTypeAttribute(AnonymousType = true)] 
    public class UserSettings 
    { 
     public ToDoListSettings ToDoListSettings { get; set; } 

     public UserSettings() 
     { 
      //default constructor required for serialization 
     } 

     public UserSettings(ToDoListSettings todoSettings) 
     { 
      this.ToDoListSettings = todoSettings; 
     } 
    } 
} 

TodoListSettings:

using System.Xml.Serialization; 
using System.Runtime.InteropServices; 

namespace Rubberduck.Config 
{ 
    interface IToDoListSettings 
    { 
     ToDoMarker[] ToDoMarkers { get; set; } 
    } 

    [ComVisible(false)] 
    [XmlTypeAttribute(AnonymousType = true)] 
    public class ToDoListSettings : IToDoListSettings 
    { 
     [XmlArrayItemAttribute("ToDoMarker", IsNullable = false)] 
     public ToDoMarker[] ToDoMarkers { get; set; } 

     public ToDoListSettings() 
     { 
      //empty constructor needed for serialization 
     } 

     public ToDoListSettings(ToDoMarker[] markers) 
     { 
      this.ToDoMarkers = markers; 
     } 
    } 
} 

TodoMarkers:

using System.Xml.Serialization; 
using System.Runtime.InteropServices; 
using Rubberduck.VBA; 

namespace Rubberduck.Config 
{ 
    [ComVisible(false)] 
    public enum TodoPriority 
    { 
     Low, 
     Normal, 
     High 
    } 

    [ComVisible(false)] 
    public interface IToDoMarker 
    { 
     TodoPriority Priority { get; set; } 
     string Text { get; set; } 
    } 

    [ComVisible(false)] 
    [XmlTypeAttribute(AnonymousType = true)] 
    public class ToDoMarker : IToDoMarker 
    { 
     //either the code can be properly case, or the XML can be, but the xml attributes must here *exactly* match the xml 
     [XmlAttribute] 
     public string Text { get; set; } 

     [XmlAttribute] 
     public TodoPriority Priority { get; set; } 

     /// <summary> Default constructor is required for serialization. DO NOT USE. </summary> 
     public ToDoMarker() 
     { 
      // default constructor required for serialization 
     } 

     public ToDoMarker(string text, TodoPriority priority) 
     { 
      Text = text; 
      Priority = priority; 
     } 

     /// <summary> Convert this object into a string representation. Over-riden for easy databinding.</summary> 
     /// <returns> The Text property. </returns> 
     public override string ToString() 
     { 
      return this.Text; 
     } 
    } 
} 

и файл образца XML:

<?xml version="1.0" encoding="utf-8"?> 
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <UserSettings> 
    <ToDoListSettings> 
     <ToDoMarkers> 
     <ToDoMarker Text="NOTE:" Priority="Low" /> 
     <ToDoMarker Text="TODO:" Priority="Normal" /> 
     <ToDoMarker Text="BUG:" Priority="High" /> 
     </ToDoMarkers> 
    </ToDoListSettings> 
    </UserSettings> 
</Configuration> 
0

Ваш код выглядит хорошо. Ставьте что-то в конструкторе коллекции немного. Я реализовал nealy так же, как и год назад, но использовал ConfigurationGroup, а мой ConfigurationSection был объектом со свойствами, а не коллекцией.

Просто для удовольствия вы можете запустить это.

[TestMethod] 
    public void TestGetTodoAttribute() 
    { 
     ToDoItemsConfigurationSection config= (ToDoItemsConfigurationSection)ConfigurationManager.GetSection("ToDoListAttributesSection"); 


     Assert.Fail(); 

    } 
Смежные вопросы