如何解决如何序列化/反序列化数据集以保留其元数据,从 .NET Framework 4.7.2 中的客户端应用程序发送到 Core 3.1 中的 gRPC 服务
要求是将使用数据集的 WCF 服务迁移到 gRPC 服务,在将数据从客户端发送到服务时保留元数据以识别行修改,以便我们可以相应地更新数据库。
我正在处理序列化 DataSet 的 PoC,如下所示:
using (MemoryStream ms = new MemoryStream()) {
dataSet.RemotingFormat = SerializationFormat.Binary;
BinaryFormatter fmt = new BinaryFormatter();
fmt.Binder = DataSetSerializationBinder.Default;
fmt.Serialize(ms,dataSet);
ms.Flush();
return ms.ToArray();
}
将字节数组从客户端发送到服务,然后在服务中使用以下内容进行反序列化:
using (MemoryStream ms = new MemoryStream(datasetBytes)) {
ms.Position = 0;
BinaryFormatter fmt = new BinaryFormatter();
fmt.Binder = DataSetSerializationBinder.Default;
var des = fmt.Deserialize(ms);
DataSet ds = (DataSet)des;
ms.Close();
}
像这样覆盖 SerializationBinder:
public override Type BindToType(string assemblyName,string typeName){
if (assemblyName.Equals("DSSerializer"))
return typeof(System.Data.DataSet);
else
return defaultBinder.BindToType(assemblyName,typeName);
}
public override void BindToName(Type serializedType,out string assemblyName,out string typeName)
{
assemblyName = "DSSerializer";
typeName = serializedType.FullName;
}
Serialize 和 Deserialize 方法在客户端和服务器引用的同一个库中, 但是当尝试反序列化时,它会抛出异常:未找到成员“XmlSchema”。 在添加 SerializationBinder 之前,异常是:BinaryFormatter.Deserialize: specified cast is not valid.
请注意,我需要传输架构信息和 DataRow.RowState
信息。当我尝试使用 this answer 所示的 XML 传输我的数据集到 Loading a datatable into a xml and xml back into a datatable 时:
dataSet.WriteXml(ms,XmlWriteMode.WriteSchema);
var dataSet = new DataSet();
dataSet.readxml(ms,XmlReadMode.ReadSchema);
我发现后者似乎没有被转移。这得到了 Preserving DataRowState when serializing DataSet using DataContractSerializer 的确认。
我知道 BinaryFormatter 不是最好的选择,什么是更好的方法,或者有什么方法可以让它工作?
解决方法
我可以在这里重现您的问题:fiddle #1。您需要序列化 DataSet
的 RowState
信息,它似乎只能由 BinaryFormatter
序列化,但即使在使用 XmlWriteMode.WriteSchema
写入和使用 {{1} 读取时也不能通过 XML }}。
现在,根据 Dino Esposito 在 2004 年 10 月写的 Cutting Edge: Binary Serialization of DataSets,BinaryFormatter 实际上序列化的是数据集的 schema 加上 diffgram数据集。由于 XmlReadMode.ReadSchema
具有读取和写入其架构和差异图的方法,因此应该可以在不丢失 DataSet
信息的情况下将数据集与包含这两个属性的 DTO 相互转换。>
以下 DTO 完成这项工作:
RowState
如果我使用以下方法往返 public class DataSetDTO
{
public string XmlSchema { get; set; }
public string XmlDiffGram { get; set; }
public static DataSetDTO FromDataSet(DataSet dataSet,bool indent = false)
{
var diffGram = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(new StringWriter(diffGram),new XmlWriterSettings { Indent = indent }))
dataSet.WriteXml(xmlWriter,XmlWriteMode.DiffGram);
var schema = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(new StringWriter(schema),new XmlWriterSettings { Indent = indent }))
dataSet.WriteXmlSchema(xmlWriter);
return new DataSetDTO
{
XmlSchema = schema.ToString(),XmlDiffGram = diffGram.ToString(),};
}
public DataSet ToDataSet()
{
var dataSet = new DataSet();
if (!string.IsNullOrEmpty(XmlSchema))
{
// TODO: determine whether and how to deny resolving external references,as is done in the reference source
// https://referencesource.microsoft.com/#system.data/system/data/DataSet.cs,388
dataSet.ReadXmlSchema(new StringReader(XmlSchema));
}
if (!string.IsNullOrEmpty(XmlDiffGram))
{
using var reader = XmlReader.Create(new StringReader(XmlDiffGram));
dataSet.ReadXml(reader,XmlReadMode.DiffGram);
}
return dataSet;
}
}
:
DataSet
schema、行数据和static DataSet RoundTripViaDTO(DataSet set) => DataSetDTO.FromDataSet(set).ToDataSet();
信息都反序列化成功。此处演示:fiddle #2。
注意事项:
-
DTO 可以使用您喜欢的任何序列化程序在您的客户端和服务器之间进行通信。但是,不要使用
RowState
,原因在 What are the deficiencies of the built-in BinaryFormatter based .Net serialization? 以及 documentation remarks 中说明BinaryFormatter 不安全,无法确保安全。有关详细信息,请参阅 BinaryFormatter security guide。
您当前使用的是 .NET Core 3.1,但展望未来,
BinaryFormatter
在 .NET 5 中被标记为过时,并且默认情况下 ASP.NET 应用程序禁止BinaryFormatter
序列化。有关详细信息,请参阅 here。 -
由于 diffgram 数据可能很冗长,所以我在将其写入字符串时禁用了格式设置。如果生成的字符串非常大,您可以考虑用某种流式解决方案替换 DTO。
-
在
BinaryFormatter
的 reference source 中,加载架构时,外部引用解析被禁用。DataSet
但是,用于从
this.ReadXmlSchema(new XmlTextReader(new StringReader(strSchema)),true); internal void ReadXmlSchema(XmlReader reader,bool denyResolving) { //...
加载架构并拒绝解析的 API 是内部的。出于安全原因,您可能需要调查如何拒绝使用公开可用的 API 进行解析。请参阅XmlSchemaSet Class: Security Considerations,了解从不受信任的来源读取架构可能出现的问题。 -
此解决方案假定传入数据集的类型恰好是
XmlReader
而不是某个 typed DataSet 子类。如果您使用类型化数据集,则需要增强 DTO 以包含类型信息。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。