Секрет, почему это не работает, имеет отношение к деталям о моем программном обеспечении, о котором я не сообщал, когда писал этот вопрос.
Мое приложение не является самостоятельным приложением. Это надстройка 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>
Мне просто интересно. Если вы удалите кусок «как ToDoItemsConfigurationSection», поместите контрольную точку в «Assert» и отлаживаете свой тест, есть ли что-нибудь в переменной 'config', когда вы ее проверяете? –
Я просто попробовал @GrantWinney. Он остается равным нулю. – RubberDuck