如何序列化/反序列化数据集,保持其元数据从.NET Framework 4.7.2中的客户端应用程序发送到Core 3.1中的gRPC服务
要求将使用数据集的WCF服务迁移到gRPC服务,在将数据从客户端发送到服务时保留元数据,以识别行修改,以便我们可以相应地更新数据库 我正在使用PoC序列化数据集,如下所示:如何序列化/反序列化数据集,保持其元数据从.NET Framework 4.7.2中的客户端应用程序发送到Core 3.1中的gRPC服务,.net,wcf,dataset,grpc,protobuf-net,.net,Wcf,Dataset,Grpc,Protobuf Net,要求将使用数据集的WCF服务迁移到gRPC服务,在将数据从客户端发送到服务时保留元数据,以识别行修改,以便我们可以相应地更新数据库 我正在使用PoC序列化数据集,如下所示: using (MemoryStream ms = new MemoryStream()) { dataSet.RemotingFormat = SerializationFormat.Binary; BinaryFormatter fmt = new BinaryFormatter();
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:指定的强制转换无效。
注意,我需要传输模式信息和信息。当我尝试使用XML传输数据集时,如所示:
我发现后者似乎没有被转移。这一点得到了国际社会的证实
我知道BinaryFormatter不是最好的选择,有什么更好的方法,或者有什么方法可以让它工作吗?我可以在这里重现您的问题:。您需要序列化
数据集
的信息,该数据集似乎仅由BinaryFormatter
序列化,而不是由XML序列化,即使在使用XmlWriteMode.WriteSchema
编写和使用XmlReadMode.ReadSchema
读取时也是如此
现在,根据Dino Esposito在2004年10月写的,BinaryFormatter实际序列化的是数据集的长度加上数据集的长度。由于DataSet
具有读取和写入其模式和diffgram的方法,因此应该可以在不丢失RowState
信息的情况下将数据集与包含这两个属性的DTO进行转换
以下DTO执行此任务:
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;
}
}
如果我使用以下方法往返一个数据集
:
static DataSet RoundTripViaDTO(DataSet set) => DataSetDTO.FromDataSet(set).ToDataSet();
架构、行数据和RowState
信息都已成功反序列化。此处演示:
注:
- DTO可以使用您喜欢的任何序列化程序在客户机和服务器之间进行通信。但是,不要使用
,原因和状态如下 BinaryFormatter不安全,无法使其安全。有关更多信息,请参阅 您当前正在使用.NET Core 3.1,但是,展望未来,二进制格式化程序
在.NET 5中被标记为已过时,BinaryFormatter
默认情况下,ASP.NET应用程序禁止序列化。有关详细信息,请参阅BinaryFormatter
- 由于diffgram数据可能很详细,因此在将其写入字符串时,我禁用了格式设置。如果生成的字符串非常大,您可以考虑用某种流式解决方案替换DTO。
- 在for
,加载架构时,禁用外部引用解析数据集中
但是,从this.ReadXmlSchema(new XmlTextReader(new StringReader(strSchema)), true); internal void ReadXmlSchema(XmlReader reader, bool denyResolving) { //...
加载模式并拒绝解析的API是内部的。出于安全原因,您可能需要研究如何使用公开可用的API拒绝解析。有关从不受信任的源读取架构时可能出现的问题,请参阅XmlReader
- 此解决方案假定传入数据集的类型正好是
,而不是某个子类。如果使用类型化数据集,则需要增强DTO以包含类型信息DataSet
XmlWriteMode.WriteSchema
将DataSet
写入XML,然后使用XmlReadMode.ReadSchema
读取,您的模式将被保留,如所示。事实上,我认为这可能是重复的,同意吗?@MarcGravel感谢您的回答。这是一个遗留服务,不幸的是,它正被与数据集紧密耦合的windows窗体应用程序使用。因此,将其更改为POCOsThank you@dbc需要大量工作,我对其进行了测试,但是,尽管它保留了模式,但我需要向服务发送DataRow.RowState信息,以便识别任何修改。这确实帮助我解决了问题。多谢各位@dbc
this.ReadXmlSchema(new XmlTextReader(new StringReader(strSchema)), true);
internal void ReadXmlSchema(XmlReader reader, bool denyResolving) { //...