-1
    /// Serialization
    /// Code 2012.05.23, [...] following Jani Giannoudis' examples
    /// CodeProject Article "User Settings Applied", 
    /// http://www.codeproject.com/Articles/25829/User-Settings-Applied
    /// </summary>

I'm using the above mentioned codeproject.com Code since a number of years successfully in different projects. A few days ago, I converted one of those projects from .NET 4.x to .NET 6.0 and the unmodified code immediately stopped working (details below) for example in the following snippet:

        // DataGridColumnSetting[] is based on System.Configuration.ApplicationSettingsBase
        // https://learn.microsoft.com/en-us/dotnet/api/system.configuration.applicationsettingsbase?view=dotnet-plat-ext-6.0
        private DataGridColumnSetting[] OriginalColumnSettings
        {
            get
            {
                return LoadValue(
                    Name,
                    typeof(DataGridColumnSetting[]),
                    SettingsSerializeAs.Binary,
                    null) as DataGridColumnSetting[];
            }
        }

Throwing a

System.NotSupportedException
  HResult=0x80131515
  Message=BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.
  Source=System.Configuration.ConfigurationManager
  StackTrace:
   at System.Configuration.SettingsProperty..ctor(String name, Type propertyType, SettingsProvider provider, Boolean isReadOnly, Object defaultValue, SettingsSerializeAs serializeAs, SettingsAttributeDictionary attributes, Boolean throwOnErrorDeserializing, Boolean throwOnErrorSerializing)
   at MyNamespace.Serialization.Setting.CreateSettingProperty(String name, Type type, SettingsSerializeAs serializeAs, Object defaultValue) in [...]MyNamespace\Serialization\Setting.cs:line 111

Since the very same code is working well in a .NET 4.8 project, I tried to find hints in the web and found

https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/binaryformatter-serialization-obsolete (and a few others) also saying

Warning "The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure."

Actual Question: Anyone else having the very same issue using the same code (from the above mentioned CodeProject Article "User Settings Applied"). (If not, I would start modifying (my personal flavor of) that code, and if successful post an answer to my question assuming others might hopefully benefit.)

Stefan Wuebbe
  • 2,109
  • 5
  • 17
  • 28
  • _"Since the very same code is working well in a .NET 4.8 project"_ - to be fair, your code listing above may appear to be the same between builds, not all of that code is yours as it has external dependencies that you have limited control over. .NET Core was never 1:1 of .NET Framework and this is still true of .NET 5.x+. For this reason you should consider all implications before migrating from .NET Framework ---> .NET Core; .NET 5+ –  Feb 13 '22 at 02:33
  • 2
    All you can do is to follow [Recommended action](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/binaryformatter-serialization-obsolete#recommended-action) in the **very page you quoted** –  Feb 13 '22 at 02:38
  • Yes, I read what I posted. Would be interesting to see and compare what other people using that (very useful) article code might have done though :) – Stefan Wuebbe Feb 13 '22 at 18:41

1 Answers1

0

Okay, apparently using JSON instead of Binary works as expected here in an http://www.codeproject.com/Articles/25829/User-Settings-Applied context (in .Net4.x as well as .Net 6). The basic concept there is having a particular Serialization class for each UI Control one wants to handle. And only some of the article samples are using the deprecated SettingsSerializeAs.Binary, for example the one made for WPF DataGridcontrols. The concept modification that works for me is using (NuGet) Newtonsoft.Json for serialization there. The typical pattern part where the article author is using SettingsSerializeAs as quoted in the question is now using SettingsSerializeAs.String instead of Binary:

private string OriginalColumnSettings
{
    get
    {
        return LoadValue(
                    Name,
                    typeof(string),
                    SettingsSerializeAs.String,
                    defaultValue: null) as string;
    }
}

Full real world (WPF) Sample for both .Net4.8 as well as .Net6:

using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace WhatEverNamespace.Serialization
{
    /// <summary>
    /// Serialization
    /// Code 2012.05.23, [...] following Jani Giannoudis' examples
    /// CodeProject Article "User Settings Applied", 
    /// http://www.codeproject.com/Articles/25829/User-Settings-Applied
    /// </summary>
    public class DataGridSetting : Setting
    {
        public static readonly DependencyProperty SettingProperty =
            DependencyProperty.RegisterAttached(
                "Setting",
                typeof(string),
                typeof(DataGridSetting),
                new FrameworkPropertyMetadata(OnDataGridSettingChanged));

        #region Fields

        private static readonly Logger log = LogManager.GetCurrentClassLogger();

        private readonly DataGrid dataGrid;
        private readonly IList<DataGridColumn> dataGridColumns = new List<DataGridColumn>();
        private bool isLoaded;

        private readonly string name;
        private bool useWidth = true;
        private bool useDisplayIndex = true;

        #endregion Fields

        #region Constructors

        public DataGridSetting(DataGrid dataGrid) :
            this(dataGrid?.Name, dataGrid)
        {
        }

        public DataGridSetting(string name, DataGrid dataGrid)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException(nameof(name));

            this.name = name;
            this.dataGrid = dataGrid ?? throw new ArgumentNullException(nameof(dataGrid));
        }

        #endregion Constructors

        #region Properties

        public string Name { get { return name; } }

        public DataGrid DataGrid { get { return dataGrid; } }

        public bool UseWidth
        {
            get { return useWidth; }
            set { useWidth = value; }
        }

        public bool UseDisplayIndex
        {
            get { return useDisplayIndex; }
            set { useDisplayIndex = value; }
        }

        public override bool HasChanged
        {
            get
            {
                var json = OriginalColumnSettings;
                if (string.IsNullOrWhiteSpace(json))
                    return false;

                var originalColumnSettings = JsonConvert.DeserializeObject<DataGridColumnSetting[]>(json);
                DataGridColumnSetting[] columnSettings = ColumnSettings;
                if (json.Length != columnSettings.Length)
                    return true;

                for (int i = 0; i < originalColumnSettings.Length; i++)
                {
                    if (!originalColumnSettings[i].Equals(columnSettings[i]))
                        return true;
                }

                return false;
            }
        }

        private string OriginalColumnSettings
        {
            get
            {
                return LoadValue(
                    Name,
                    typeof(string),
                    SettingsSerializeAs.String,
                    defaultValue: null) as string;
            }
        }

        private DataGridColumnSetting[] ColumnSettings
        {
            get
            {
                if (dataGrid?.Columns.Any() != true)
                    return null;

                IList<DataGridColumnSetting> columnSettings =
                    new List<DataGridColumnSetting>(dataGrid.Columns.Count);
                foreach (DataGridColumn dataGridColumn in dataGrid.Columns)
                {
                    int index = dataGridColumns.IndexOf(dataGridColumn);
                    int displayIndex = dataGridColumn.DisplayIndex;
                    DataGridColumnSetting columnSetting = new DataGridColumnSetting
                    {
                        Index = index,
                        DisplayIndex = displayIndex,
                        Width = dataGridColumn.ActualWidth
                    };
                    columnSettings.Add(columnSetting);
                }

                return columnSettings.ToArray();
            }
        }

        #endregion Properties

        #region Methods

        public static string GetSetting(DependencyObject dependencyObject)
        {
            return dependencyObject?.GetValue(SettingProperty) as string;
        }

        public static void SetSetting(DependencyObject dependencyObject, string settingKey)
        {
            dependencyObject?.SetValue(SettingProperty, settingKey);
        }

        public override void Load()
        {
            // Initialized event does not work since it's running too early in a WPF DataGrid
            // ("dataGrid.Initialized += DataGridInitialized" in Jani's ListViewSettings.cs)
            if (isLoaded == false)
                SetupDataGridColumns();

            try
            {
                DataGrid dataGrid = this.dataGrid;
                if (dataGrid?.Columns.Any() != true)
                    return;

                var json = OriginalColumnSettings;
                var columnSettings = JsonConvert.DeserializeObject<DataGridColumnSetting[]>(json);
                if (columnSettings?.Any() != true)
                    return;

                for (int displayIndex = 0; displayIndex < columnSettings.Length; displayIndex++)
                {
                    DataGridColumnSetting columnSetting = columnSettings[displayIndex];
                    if (columnSetting.Index < 0 || columnSetting.Index >= dataGridColumns.Count)
                        continue;

                    DataGridColumn dataGridColumn = dataGridColumns[columnSetting.Index];
                    if (useWidth)
                        dataGridColumn.Width = new DataGridLength(columnSetting.Width);

                    if (useDisplayIndex && columnSetting.Index != columnSetting.DisplayIndex)
                        dataGridColumn.DisplayIndex = columnSetting.DisplayIndex;
                }
            }
            catch
            {
                if (ThrowOnErrorLoading)
                    throw;
            }
        }

        public override void Save()
        {
            try
            {
                DataGridColumnSetting[] columnSettings = ColumnSettings;
                if (columnSettings == null)
                    return;

                var json = JsonConvert.SerializeObject(columnSettings);

                SaveValue(
                    Name,
                    typeof(string),
                    SettingsSerializeAs.String,
                    json,
                    null);
            }
            catch
            {
                if (ThrowOnErrorSaving)
                    throw;
            }
        }

        public override string ToString()
        {
            return string.Concat(name, " (DataGrid)");
        }

        private void SetupDataGridColumns()
        {
            dataGridColumns.Clear();

            if (dataGrid == null)
                return;

            if (dataGrid.Columns.Count > 0)
                isLoaded = true;
            else
                return;

            for (int i = 0; i < dataGrid.Columns.Count; i++)
            {
                dataGridColumns.Add(dataGrid.Columns[i]);
            }
        }

        private static void OnDataGridSettingChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            if (!(dependencyObject is DataGrid dataGrid))
            {
                log.Warn(CultureInfo.InvariantCulture,
                    "{0}.{1}(): invalid property attachment",
                    nameof(DataGridSetting),
                    nameof(OnDataGridSettingChanged));
                return;
            }

            // search on the parent-tree for application settings
            ApplicationSettings applicationSettings = FindParentSettings(dependencyObject);
            if (applicationSettings == null)
            {
                log.Warn(CultureInfo.InvariantCulture,
                    "{0}.{1}(): missing application settings in parent hierarchy",
                    nameof(DataGridSetting),
                    nameof(OnDataGridSettingChanged));
                return;
            }

            applicationSettings.Settings.Add(new DataGridSetting(dataGrid));
        }

        private static ApplicationSettings FindParentSettings(DependencyObject element)
        {
            while (element != null)
            {
                if (element.ReadLocalValue(
                    DependencyPropertySetting.ApplicationSettingsProperty) is ApplicationSettings applicationSettings)
                    return applicationSettings;
                element = LogicalTreeHelper.GetParent(element);
            }
            return null;
        }

        #endregion Methods
    }
}
Stefan Wuebbe
  • 2,109
  • 5
  • 17
  • 28