.NET 5.0 SettingsSerializeAs.Binary 已过时
.NET 5.0 SettingsSerializeAs.Binary obsolete
/// Serialization
/// Code 2012.05.23, [...] following Jani Giannoudis' examples
/// CodeProject Article "User Settings Applied",
/// http://www.codeproject.com/Articles/25829/User-Settings-Applied
/// </summary>
多年来,我一直在不同的项目中成功使用上述 codeproject.com 代码。
几天前,我将其中一个项目从 .NET 4.x 转换为 .NET 6.0,未修改的代码立即停止工作(详情如下),例如以下代码段:
// DataGridColumnSetting[] is based on System.Configuration.ApplicationSettingsBase
// https://docs.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[];
}
}
投掷一个
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
由于完全相同的代码在 .NET 4.8 项目中运行良好,我尝试在网络上查找提示并找到了
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."
实际问题:
使用相同代码(来自上面提到的 CodeProject 文章“应用的用户设置”)的任何其他人都有相同的问题。
(如果没有,我将开始修改(我个人的风格)该代码,如果成功 post 我的问题的答案,假设其他人可能会受益。)
好吧,显然使用 JSON 而不是 Binary 在 http://www.codeproject.com/Articles/25829/User-Settings-Applied 上下文中(在 .Net4.x 和 .Net 6 中)按预期工作。
那里的基本概念是对每个想要处理的 UI 控件都有一个特定的序列化 class。并且只有部分文章示例使用了已弃用的 SettingsSerializeAs.Binary
,例如为 WPF DataGrid
控件制作的示例。对我有用的概念修改是使用 (NuGet) Newtonsoft.Json
在那里进行序列化。
问题中引用的文章作者使用 SettingsSerializeAs
的典型模式部分现在使用 SettingsSerializeAs.String
而不是 Binary
:
private string OriginalColumnSettings
{
get
{
return LoadValue(
Name,
typeof(string),
SettingsSerializeAs.String,
defaultValue: null) as string;
}
}
.Net4.8 和 .Net6 的完整真实世界 (WPF) 样本:
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
}
}
/// Serialization
/// Code 2012.05.23, [...] following Jani Giannoudis' examples
/// CodeProject Article "User Settings Applied",
/// http://www.codeproject.com/Articles/25829/User-Settings-Applied
/// </summary>
多年来,我一直在不同的项目中成功使用上述 codeproject.com 代码。 几天前,我将其中一个项目从 .NET 4.x 转换为 .NET 6.0,未修改的代码立即停止工作(详情如下),例如以下代码段:
// DataGridColumnSetting[] is based on System.Configuration.ApplicationSettingsBase
// https://docs.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[];
}
}
投掷一个
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
由于完全相同的代码在 .NET 4.8 项目中运行良好,我尝试在网络上查找提示并找到了
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."
实际问题: 使用相同代码(来自上面提到的 CodeProject 文章“应用的用户设置”)的任何其他人都有相同的问题。 (如果没有,我将开始修改(我个人的风格)该代码,如果成功 post 我的问题的答案,假设其他人可能会受益。)
好吧,显然使用 JSON 而不是 Binary 在 http://www.codeproject.com/Articles/25829/User-Settings-Applied 上下文中(在 .Net4.x 和 .Net 6 中)按预期工作。
那里的基本概念是对每个想要处理的 UI 控件都有一个特定的序列化 class。并且只有部分文章示例使用了已弃用的 SettingsSerializeAs.Binary
,例如为 WPF DataGrid
控件制作的示例。对我有用的概念修改是使用 (NuGet) Newtonsoft.Json
在那里进行序列化。
问题中引用的文章作者使用 SettingsSerializeAs
的典型模式部分现在使用 SettingsSerializeAs.String
而不是 Binary
:
private string OriginalColumnSettings
{
get
{
return LoadValue(
Name,
typeof(string),
SettingsSerializeAs.String,
defaultValue: null) as string;
}
}
.Net4.8 和 .Net6 的完整真实世界 (WPF) 样本:
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
}
}