Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何序列化/反序列化数据集,保持其元数据从.NET Framework 4.7.2中的客户端应用程序发送到Core 3.1中的gRPC服务_.net_Wcf_Dataset_Grpc_Protobuf Net - Fatal编程技术网

如何序列化/反序列化数据集,保持其元数据从.NET Framework 4.7.2中的客户端应用程序发送到Core 3.1中的gRPC服务

如何序列化/反序列化数据集,保持其元数据从.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();

要求将使用数据集的WCF服务迁移到gRPC服务,在将数据从客户端发送到服务时保留元数据,以识别行修改,以便我们可以相应地更新数据库

我正在使用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:指定的强制转换无效。

注意,我需要传输模式信息和信息。当我尝试使用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,但是,展望未来,
    BinaryFormatter
    在.NET 5中被标记为已过时,
    BinaryFormatter
    默认情况下,ASP.NET应用程序禁止序列化。有关详细信息,请参阅

  • 由于diffgram数据可能很详细,因此在将其写入字符串时,我禁用了格式设置。如果生成的字符串非常大,您可以考虑用某种流式解决方案替换DTO。
  • 在for
    数据集中
    ,加载架构时,禁用外部引用解析

    this.ReadXmlSchema(new XmlTextReader(new StringReader(strSchema)), true);
    
    internal void ReadXmlSchema(XmlReader reader, bool denyResolving) { //...
    
    但是,从
    XmlReader
    加载模式并拒绝解析的API是内部的。出于安全原因,您可能需要研究如何使用公开可用的API拒绝解析。有关从不受信任的源读取架构时可能出现的问题,请参阅

  • 此解决方案假定传入数据集的类型正好是
    DataSet
    ,而不是某个子类。如果使用类型化数据集,则需要增强DTO以包含类型信息


老实说,发送数据集(甚至使用数据集)听起来才是真正的问题。数据集是一个很好的解决方案的问题很少。甚至有可能将它们更改为使用POCOs吗?您可以使用XML而不是二进制文件。只要使用
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) { //...