Невозможно десериализовать до 'this' из конструктора по умолчанию?

Ситуация:

У меня есть ViewModel, привязанный к представлению настроек, где пользователь должен вводить множество персонализированных настроек и иметь возможность сохранять их в качестве предустановок и загружать их. Для этого ViewModel содержит Коллекции предустановленных моделей данных, которые сами содержат различные свойства, объекты классов и так далее. План состоит в том, чтобы добиться сохранения всех предустановок с помощью сериализации xml и десериализации всех ViewModels.

Код / Проблема

Из конструктора ViewModel я вызываю следующий метод:

private void InitializePresetsFromFile()
{
    if (!File.Exists(Info.GetDefaultColorPalettePresetsXml()))
    {
        SetupNewEmpty();
        SerializePresets(Info.GetDefaultColorPalettePresetsXml());
    }
    else
    {
        DeserializePresets(Info.GetDefaultColorPalettePresetsXml());
    }
}

Таким образом, Метод проверяет, существует ли файл, содержащий предустановку, - если нет, он должен установить пустой предустановку и сохранить его во вновь созданном файле, в противном случае он должен загрузить предустановку из существующего файла.

Процесс сериализации работает нормально, однако, поскольку я сериализую в this, возникает проблема с десериализацией:

private void DeserializePresets(string path)
{
    XmlSerializer deserializer = new XmlSerializer(typeof(LinearAxisColorPresetsViewModel));
    TextReader reader = new StreamReader(path);
    object obj = deserializer.Deserialize(reader);
    LinearAxisColorPresetsViewModel XmlData = (LinearAxisColorPresetsViewModel)obj;
    reader.Close();
    VolumePresetList = XmlData.VolumePresetList;
    WaveShapePresetList = XmlData.WaveShapePresetList;
    VolumePresetSelectedIndex = XmlData.VolumePresetSelectedIndex;
    WaveShapePresetSelectedIndex = XmlData.WaveShapePresetSelectedIndex;
}

Проблема здесь в том, что, поскольку я вызываю метод InitializePresetsFromFile() непосредственно из конструктора, десериализатор вызывает себя в бесконечном цикле, что приводит к ошибке переполнения стека.

Итак, самым простым решением должно быть использование другого конструктора с параметром, где я вызываю InitializePresetsFromFile(), верно? Проблема здесь в том, что класс ViewModel напрямую создается в xaml соответствующего View:

<UserControl.Resources>
    <ResourceDictionary>
        <vm:LinearAxisColorPresetsViewModel x:Key="vm" />
    </ResourceDictionary>
</UserControl.Resources>

В этом сообщении второй ответ гласит, что по соглашению конструкторы, вызываемые из XAML, должны быть без параметров, и я хочу придерживаться этого.

Вопрос:

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


person Roland Deschain    schedule 04.03.2019    source источник


Ответы (2)


Вы действительно достигли точки, когда вам нужно решить, как продолжить. То, что вы делаете сейчас, не сработает. В этом случае как сериализатор XML, так и XAML используют конструктор по умолчанию. Вы не можете заставить его служить двум целям здесь.

Я бы посоветовал создать класс, который отражает свойства вашей модели представления, которые вы используете для десериализации XML-файла. Этому классу нужны только свойства, не более того.

Если класс модели представления на самом деле статический, вы можете использовать класс локатора привязать его к.

person Patrick Hofman    schedule 04.03.2019
comment
Хм, я боялся этого ответа ... Я изучу предложенное вами использование класса локатора и посмотрю, смогу ли я реализовать его в моем случае. Спасибо! - person Roland Deschain; 04.03.2019
comment
Будет использоваться этот подход к узнать, кто вызывает конструктор и вызывает только InitializePresetsFromFile(); хорошую практику? Мне все еще нужно его протестировать, но я думаю, он должен работать .. - person Roland Deschain; 04.03.2019
comment
Я бы не рекомендовал это. Сделайте это прозрачным для следующего коллеги, который видит этот код, что он делает, и не делает таких непонятных вещей ... - person Patrick Hofman; 04.03.2019

Во-первых, не следует вызывать метод InitializePresetsFromFile в конструкторе класса. Конструктор должен быть максимально быстрым и не вызывать побочных эффектов. Чтение файла в конструкторе - плохая практика: вы не можете создать экземпляр своего класса, не обращаясь к файловой системе. Это означает, что ваш код нельзя тестировать, он подвержен ошибкам (например, вы думали о внезапных UnauthorizedAccessExceptions?) И он медленный.

Вместо этого создайте общедоступный метод, который десериализует данные из файла. Это нарушит вашу бесконечную рекурсию.

Как вызвать этот метод?

  • Вам действительно нужен ваш LinearAxisColorPresetsViewModel экземпляр в словаре ресурсов? Если нет, просто назначьте десериализованный экземпляр свойству DataContext вашего представления.
  • Если вам это нужно, создайте в своей модели представления ICommand, например. InitializeCommand, который использует вышеупомянутый метод для инициализации внутреннего состояния из файла; выполнить эту команду при запуске приложения / просмотре отображения и т. д. Вы можете использовать, например, InvokeCommandAction для Loaded события для этого.
person dymanoid    schedule 04.03.2019
comment
Это действительные точки. Я, вероятно, попытаюсь поместить все данные, необходимые для сериализации, в отдельный класс, как было предложено Патриком Хофманом, и начать процесс десериализации с события loaded, как вы предложили. - person Roland Deschain; 04.03.2019