序列化自定义配置

Serializing custom configuration

我需要一些帮助来实现序列化自定义配置的方法。我从以下示例的内容开始:Polymorphic custom configuration section

读取配置工作正常,但我无法保存对某些配置属性的修改(例如,将 属性 P1 更改为包含另一个字符串)。虽然调试不同对象的内容看起来不错(部分包含集合,其中包含三个代理项,这些代理项本身包含一个 Parent class 的实例)。已更改的项目 (P1="") 的 isModified 标志设置为 true(如预期)。

调用 config.Save() 时出现一些奇怪的行为,经过三天的调查(即使是 Microsoft base classes)我也无法找出问题所在。以下是我的一些结论:

我为每个 SerializeX 方法(SerializeSection、SerializeElement 和 SerializeToXmlElement)添加了一个覆盖,并通过代码逐步调试。

我知道需要有一个自定义的 SerializeElement 方法(可能在代理 class 上)然后调用 _Parent.ProxySerializeElement(writer, serializeCollectionKey)对于每个元素,就像反序列化一样。但我无法让它发挥作用。 SerializeElement 的覆盖不起作用,因为 XmlWriter 实例始终为 null(即使 Proxy class 有一些 IsModified 方法来检查 Parent 对象是否已更改)。此外,只要我输入此自定义 SerializeElement 方法,Parent 对象也始终为 null。

以下是我添加到示例中的代码片段:

Parent.cs

new public bool IsModified { get { return IsModified(); } }

public virtual bool ProxySerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
    return SerializeElement(writer, serializeCollectionKey);
}

Proxy.cs

protected override bool IsModified()
{
    bool isModified = base.IsModified();
    return isModified || (Parent == null ? false : Parent.IsModified);
}

protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
    bool serialize = base.SerializeElement(writer, serializeCollectionKey);
    return serialize || (_Parent == null ? false : _Parent.ProxySerializeElement(writer, serializeCollectionKey));
}

它无法正常工作,这让我抓狂。也许其他人可以帮助我。

提前致谢!

你好,Stefi

最后这对我有用。也许它可以帮助遇到同样问题的其他人。为了简单起见,我将 post 完整的代码。它可能不是第一个 class 解决方案,但它确实有效。如果其他人可以查看它并提出更好的方法,我将不胜感激。

帮助我让它工作的是这篇文章:https://www.codeproject.com/Articles/16466/Unraveling-the-Mysteries-of-NET-2-0-Configuration

我的代码缺少元素集合(参见 ThingElement.cs)。

thing.config

<configuration>
  <configSections>
    <section name="ExampleSection" type="ConsoleApplication1.Things.ExampleSection, ConsoleApplication1" />
  </configSections>

  <ExampleSection>
    <things>
      <thing type="one" name="one-1" color="green" />
      <thing type="one" name="one-2" color="red" />
      <thing type="two" name="two-1" />
    </things>
  </ExampleSection>
</configuration>

ExampleSection.cs

    public class ExampleSection : ConfigurationSection
    {
        static ExampleSection() { }

        [ConfigurationProperty("things")]
        [ConfigurationCollection(typeof(ThingElement), AddItemName = "thing",
            CollectionType = ConfigurationElementCollectionType.BasicMap)]
        public ExampleThingElementCollection Things
        {
            get { return (ExampleThingElementCollection)this["things"]; }
            set { this["things"] = value; }
        }
    }

ExampleThingElementCollection.cs

    [ConfigurationCollection(typeof(ThingElement), AddItemName = "thing",
       CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class ExampleThingElementCollection : ConfigurationElementCollection
    {
        #region Constructors
        public ExampleThingElementCollection()
        {
        }
        #endregion

        #region Properties
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
        protected override string ElementName
        {
            get { return "thing"; }
        }
        #endregion

        #region Indexers
        public ThingElement this[int index]
        {
            get { return (ThingElement)base.BaseGet(index); }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                base.BaseAdd(index, value);
            }
        }
        new public ThingElement this[string name]
        {
            get { return (ThingElement)base.BaseGet(name); }
        }
        #endregion

        #region Overrides
        protected override ConfigurationElement CreateNewElement()
        {
            return new ThingElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as ThingElement).Name;
        }
        #endregion

        #region Methods
        public void Add(ThingElement thing)
        {
            base.BaseAdd(thing);
        }
        public void Remove(string name)
        {
            base.BaseRemove(name);
        }
        public void Remove(ThingElement thing)
        {
            base.BaseRemove(GetElementKey(thing));
        }
        public void Clear()
        {
            base.BaseClear();
        }
        public void RemoveAt(int index)
        {
            base.BaseRemoveAt(index);
        }
        public string GetKey(int index)
        {
            return (string)base.BaseGetKey(index);
        }
        #endregion
    }

ThingElement.cs(这个class作为代理元素)

    public class ThingElement : ConfigurationElement
    {
        #region Constructors
        /// <summary>
        /// Predefines the valid properties and prepares
        /// the property collection.
        /// </summary>
        static ThingElement()
        {
            // Predefine properties here
            s_propName = new ConfigurationProperty(
                "name",
                typeof(string),
                null,
                ConfigurationPropertyOptions.IsRequired
            );
        }
        #endregion

        #region Static Fields
        private static ConfigurationProperty s_propName;
        private static Dictionary<string, SpecialThing> elements = new Dictionary<string, SpecialThing>();
        #endregion


        #region Properties
        /// <summary>
        /// Gets the Name setting.
        /// </summary>
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return (string)base[s_propName]; }
        }

        public SpecialThing Thing { get { return elements[Name]; } }

        protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
        {
            return Thing.ProxySerializeElement(writer, serializeCollectionKey);
        }

        protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            SpecialThing obj = null;
            string name = reader.GetAttribute("name");
            string type = reader.GetAttribute("type");
            switch (type)
            {
                case "one":
                    obj = new One();
                    break;
                case "two":
                    obj = new Two();
                    break;
                default:
                    throw new ArgumentException(string.Format("Invalid type: {0}", type));
            }

            base[s_propName] = name;
            if (!elements.ContainsKey(name))
                elements.Add(name, obj);
            obj.ProxyDeserializeElement(reader, serializeCollectionKey);
        }
        private Hashtable attributes;

        public Hashtable Attributes
        {
            get
            {
                if (attributes == null)
                    attributes = new Hashtable(StringComparer.OrdinalIgnoreCase);
                return attributes;
            }
        }

        protected override bool OnDeserializeUnrecognizedAttribute(String name, String value)
        {
            Attributes.Add(name, value);
            return true;
        }

        protected override void PreSerialize(XmlWriter writer)
        {
            if (attributes != null)
            {
                IDictionaryEnumerator e = attributes.GetEnumerator();
                while (e.MoveNext())
                {
                    string xmlValue = (string)e.Value;
                    string xmlName = (string)e.Key;

                    if ((xmlValue != null) && (writer != null))
                    {
                        writer.WriteAttributeString(xmlName, xmlValue);
                    }
                }
            }
        }

        #endregion
    }

SpecialThing.cs(父级 class,例如基础 class 如果你有其他派生)

    public class SpecialThing : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get
            {
                return (string)this["name"];
            }

            set
            {
                this["name"] = value;
            }
        }

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

            set
            {
                this["type"] = value;
            }
        }

        public virtual bool ProxySerializeElement(XmlWriter writer, bool serializeCollectionKey)
        {
            return SerializeElement(writer, serializeCollectionKey);
        }

        public void ProxyDeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            DeserializeElement(reader, serializeCollectionKey);
        }
    }

One.cs(父 class,例如基础 class 如果你有其他派生

    public class One : SpecialThing
    {
        public One() { }

        public One(string name, string type, string color)
        {
            base.Name = name;
            base.Type = type;
            Color = color;
        }

        [ConfigurationProperty("color")]
        public string Color
        {
            get { return (string)this["color"]; }
            set { this["color"] = value; }
        }
    }

Two.cs

    public class Two : SpecialThing
    {
        public Two() { }

        public Two(string name, string type)
        {
            base.Name = name;
            base.Type = type;
        }
    }