Сериализация пользовательской конфигурации - PullRequest
0 голосов
/ 14 марта 2020

Мне нужна помощь по реализации способа сериализации пользовательской конфигурации. Я начал с содержимого следующего примера: Polymorphi c секция пользовательской конфигурации

Чтение конфигурации работает нормально, как и ожидалось, но я не могу сохранить изменения некоторых свойств конфигурации (например, изменение свойства P1, чтобы содержать другую строку). При отладке содержимое различных объектов выглядит нормально (раздел содержит коллекцию, которая содержит три прокси-элемента, которые сами содержат экземпляр класса Parent). Для элемента, который был изменен (P1 = ""), для флага isModified установлено значение true (как и ожидалось).

При вызове config.Save () возникает некоторое странное поведение и после трех дней исследования (даже базовые классы Microsoft) Мне не удается выяснить, где проблема. Вот некоторые из моих выводов:

Я добавил переопределение для каждого из методов SerializeX (SerializeSection, SerializeElement и SerializeToXmlElement) и пошагово отлаживал код в коде.

  • SerializeSection вызывается (как и ожидалось) с параметром parentElement, который является , а не секцией, которую я хочу сериализовать, поскольку свойство Collection пустое (я ожидаю, что оно будет иметь три экземпляра, которые часть файла конфигурации). Вызов base.SerializeSection с this вместо parentElement решает проблему

  • SerializeToXmlElement вызывается до SerializeElement и содержит экземпляр XmlWriter (как и ожидалось)

  • SerializeElement вызывается сразу после SerializeToXmlElement и не не больше не содержит экземпляр XmlWriter

  • При входе в методы сериализации объекта коллекции я ожидаю, что три элемента коллекций сериализуются. Но вместо трех элементов коллекция содержит только один элемент, который был недавно инициализирован и поэтому имеет свойство Parent , возвращающее ноль.

Я знаю, что необходимо быть пользовательским методом SerializeElement (вероятно, для класса Proxy), который затем вызывает _Parent.ProxySerializeElement (writer, serializeCollectionKey) для каждого элемента, как это происходит для десериализации. Но я не могу заставить его работать. Переопределение SerializeElement не работает, так как экземпляр XmlWriter всегда имеет значение null (даже несмотря на то, что класс Proxy имеет некоторый метод IsModified для проверки, изменился ли родительский объект). Кроме того, родительский объект всегда также равен нулю, как только я вхожу в этот пользовательский метод SerializeElement.

Вот фрагменты кода, которые я добавил в пример:

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));
}

Это сводит меня с ума, что я не могу заставить его работать. Может быть, кто-то еще может помочь мне.

Спасибо заранее!

Привет, Стефи

1 Ответ

0 голосов
/ 18 марта 2020

Наконец-то это сработало для меня. Может быть, это помогает кому-то, кто имеет ту же проблему. Я выложу полный код, чтобы сделать его простым. Возможно, это не решение первого класса, но оно работает. Я был бы признателен, если бы кто-то еще взглянул на это и предложил лучший способ.

Что помогло мне заставить его работать, так это статья: 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 (этот класс действует как прокси-элемент)

    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 (родительский класс, например, базовый класс, если у вас есть другой, производный от него)

    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 (родительский класс, например, базовый класс, если у вас есть другой, который наследует

    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;
        }
    }
...