关于C#:配置部分哪里出错?

Where am I going wrong with ConfigurationSections?

本问题已经有最佳答案,请猛点这里访问。

我正在尝试实现一个自定义配置部分,这样我就可以加载一个用户定义的项目列表,而且运气不好。我已经阅读了下面的文章,但我仍然不知道我做错了什么。他们似乎是很好的向导,但我遗漏了一些重要的事实。我希望有人能准确地指出什么。

  • 如何使用configurationElementCollection实现configurationSection
  • 单元测试自定义配置元素和配置元素集合
  • 带有"添加"元素的简单列表的自定义app.config部分

这是我的测试。当我跨过它时,config保持为空。这就像打电话给GetSection根本不起作用。

1
2
3
4
5
6
7
8
9
10
11
12
[TestClass]
public class ToDoConfigTests
{
    [TestMethod]
    public void TestGetTodoAttribute()
    {
        var config = ConfigurationManager.GetSection("ToDoListAttributesSection") as ToDoItemsConfigurationSection;

        Assert.Fail();

    }
}

我的配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>
       
       
       
      </ToDoListAttributes>
    </ToDoListAttributesSection>
</configuration>


为什么这不起作用的秘密与我写这个问题时没有分享的关于我的软件的细节有关。

我的应用程序不是独立的应用程序。它是用于VBA编辑器的COM加载项。因此,它是一个*.dll文件,而不是一个*.exe。App.config文件只与正在执行的程序集(*.exe)一起工作,所以这就是我的代码不工作的原因。这里有一些很好的解决方案,但最终我使用XML序列化滚动了自己的配置。

下面是我最后使用的代码。如果您喜欢在Github上的RubberBuck存储库中查看,也可以找到它。

解决方案的核心是IConfigurationService接口和ConfigurationLoader实现,它允许我读取和写入存储配置的XML文件。(此处的版本已简化为仅处理原始代码。)

i配置服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
?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);
    }
}

配置加载程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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 };
    }
}

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
?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;
        }
    }
}

用户设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
?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;
        }
    }
}

当前设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
?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文件:

1
2
3
4
5
6
7
8
9
10
11
12
?<?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>

您的代码看起来不错。它打赌您的集合构造函数中的某些内容有点不可靠。我一年前和你实现的几乎一样,但我使用了ConfigurationGroup,我的ConfigurationSection是一个具有属性而不是集合的对象。

只是为了好玩,你能运行这个吗?

1
2
3
4
5
6
7
8
9
[TestMethod]
    public void TestGetTodoAttribute()
    {
        ToDoItemsConfigurationSection config= (ToDoItemsConfigurationSection)ConfigurationManager.GetSection("ToDoListAttributesSection");


        Assert.Fail();

    }