Protobuf-net Error:Type is not expected, and no contract can be inferred: System.Data.DataTable

Protobuf-net Error:Type is not expected, and no contract can be inferred: System.Data.DataTable

我需要序列化我在运行时使用 ProBuf.Serializer 创建的数据 table,它在 protobuf-net Nuget 下可用。下面附上我使用过的代码示例。我收到错误 ProtoBuf.Serialzer.Serialize(流,table);有人可以帮我解决这个问题吗?

public static void Main()
    {
        try
        {
            Process process = new Process();
            process.StartInfo.FileName = @"E:\POC\Test\GrpcServer\bin\Debug\netcoreapp3.0\GrpcServer.exe";
            process.Start();
            List<ChannelOption> channelOptions = new List<ChannelOption>()
            {
                new ChannelOption(ChannelOptions.MaxSendMessageLength, int.MaxValue)
            };
            Channel channel = new Channel("localhost:5005", ChannelCredentials.Insecure, channelOptions);
            var client = new TestingService.TestingServiceClient(channel);
            DataTable table = CreateTable(100000);
            Console.WriteLine("Starting Serialization");
            DateTime serializationStartTime = DateTime.Now;

            MemoryStream stream = new MemoryStream();
            ProtoBuf.Serializer.Serialize(stream, table);
            stream.Seek(0, SeekOrigin.Begin);
            DateTime serializationEndTime = DateTime.Now;
            byte[] arr = stream.ToArray();
            ByteString data = ByteString.CopyFrom(arr);
            Console.WriteLine("Completed Serialization");
            Console.WriteLine("Started Communication with Grpc Server");
            DateTime startGrpcTime = DateTime.Now;
            client.RecieveData(new PBData() { Data = data });
            DateTime endGrpcTime = DateTime.Now;
            Console.WriteLine("Grpc communication ended");
            Console.WriteLine($"Serialization time :{(serializationEndTime - serializationStartTime).TotalSeconds}");
            Console.WriteLine($"Grpc Communication time :{(endGrpcTime - startGrpcTime).TotalSeconds}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error:{ex.Message}");
        }
    }`

编辑:下面的很多答案都假设您使用的是 protobuf-net.Grpc;在重新阅读 post 后,这可能不是一个有效的假设 - 尽管它实际上在您的情况下会很好地工作!但是:在您的情况下,也许更简单的解决方案只是将代码替换为:

DataTable table = CreateTable(100000);
Console.WriteLine("Starting Serialization");
DateTime serializationStartTime = DateTime.Now;

MemoryStream stream = new MemoryStream();
table.RemotingFormat = RemotingFormat.Binary;
table.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin); // not actually needed, note
DateTime serializationEndTime = DateTime.Now;
byte[] arr = stream.ToArray();
ByteString data = ByteString.CopyFrom(arr);
Console.WriteLine("Completed Serialization");

(当图书馆的作者说 "it might not be the best choice in your case",你应该 可能 听)


protobuf-net 是一个合同序列化程序 - 它有助于序列化 数据合同 类型(在一般意义上 - 不是在特定于 WCF 的意义上)。 DataTable 是数据契约的 相反 - 它没有可预测的定义形状,因此它不能很好地与 protobuf-net 一起使用。历史上我曾实验性尝试允许它们可序列化 - 有一些bits here,但那是支持的一部分protobuf-net 的库表面,坦率地说:预计它会失败。

我个人也会注意到:我强烈建议 反对 使用 DataTable 来交换数据——它几乎从来不是正确的类型 除非你正在编写一个临时查询系统,例如报告工具或类似SSMS/SEDE的东西——即采用一些查询语言和returns一些不能提前知道。

但是:这是一个附带话题,可能超出您的控制范围,所以:让我们谈谈 DataTable:

我认为我们应该忘记这里的 "protobuf-net" 部分,而专注于您实际尝试做的事情。我将在这里阅读几行之间的内容,并猜测你 实际上 之后是:你正在使用 protobuf-net.Grpc,并且你想交换DataTableDataSet。好消息是:protobuf-net.Grpc 实际上并没有以任何方式 绑定到 protobuf-net (默认情况除外)。您可以添加自己的 marshaller-factory(或多个工厂)来处理任何 类别 类型,或者对于单个类型,您可以 给它一个 marshaller,例如 - 完全未经测试,但这应该有效:

ProtoBuf.Grpc.Configuration.BinderConfiguration.Default.SetMarshaller(
    new Marshaller<DataTable>(SerializeDataTable, DeserializeDataTable));

// side note: you may want to try setting the RemotingFormat of the
// DataTable you are using to Binary before sending it to gRPC
static byte[] SerializeDataTable(DataTable value)
{
    using var ms = new MemoryStream();
    value.WriteXml(ms);
    return ms.ToArray();
}
static DataTable DeserializeDataTable(byte[] value)
{
    using var ms = new MemoryStream(value);
    var obj = new DataTable();
    obj.ReadXml(ms);
    return obj;
}

请注意,您需要将自定义编组器告知客户端和服务器。

另请注意:marshaller 选择是由 protobuf-net.Grpc 基于通过服务的数据做出的 - 我的意思是:数据的参数类型正在发送,接收数据的 return 类型。这意味着以上仅在 DataTable 是这两者之一时才有效;所以这些会起作用:

ValueTask<SomeContractType> SendAsync(DataTable blah);
ValueTask<DataTable> ReceiveAsync(SomeContractType blah);
ValueTask<DataTable> SendAndReceiveAsync(DataTable blah);

但这不会:

ValueTask<HazDataTable> WrappedAsync(HazDataTable blah);
// ...
[ProtoContract]
public class HazDataTable {
    [DataMember(1)]
    public DataTable TheData {get;set;}
}

因为在 Wrapped 的情况下,protobuf-net.Grpc 解析了 HasDataTable 的编组器并且从不查看它的内部 - 这意味着marshaller 将是 protobuf-net - protobuf-net 不适用于 DataTable.