我正在尝试实现自定义配置部分,以便我可以加载用户定义的项目列表而没有太多运气。我已经阅读了以下帖子,但我仍然无法弄清楚我做错了什么。他们似乎是很好的向导,但我在这里错过了一些重要的事实。我希望有人能指出究竟是什么。
这是我的考试。当我单步执行它时,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 :(得分:1)
为什么这不起作用的秘密与我写的这个问题时我没有分享的关于我的软件的细节有关。
我的应用程序不是一个独立的应用程序。它是VBA编辑器的COM加载项。因此,它是* .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>
答案 1 :(得分:0)
您的代码看起来不错。它打赌你的集合构造函数中的东西有点偏。我和一年前一样实现了nealy,但是我使用了ConfigurationGroup
而我的ConfigurationSection
是一个属性不是集合的对象。
只是为了好玩,你可以运行它。
[TestMethod]
public void TestGetTodoAttribute()
{
ToDoItemsConfigurationSection config= (ToDoItemsConfigurationSection)ConfigurationManager.GetSection("ToDoListAttributesSection");
Assert.Fail();
}