Protobuf-net Error:Type is not expected, and no contract can be inferred: System.Data.DataTable
Posted
技术标签:
【中文标题】Protobuf-net Error:Type is not expected, and no contract can be inferred: System.Data.DataTable【英文标题】: 【发布时间】:2020-06-12 06:59:03 【问题描述】:我需要序列化我在运行时使用 ProBuf.Serializer 创建的数据表,ProBuf.Serializer 在 protobuf-net Nuget 下可用。下面附上我使用过的代码示例。我收到错误 ProtoBuf.Serialzer.Serialize(流,表);有人可以帮我解决这个问题吗?
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");
`
【问题讨论】:
在发布我的答案后,我偶然发现了这个:github.com/dotarj/protobuf-net-data - 注意:这不是我的代码 - 我从未使用过、评估过、审查过它,或者任何东西 - 所以它不是官方推荐:但是:粗略一看,它看起来可能会做你想做的事! 【参考方案1】:编辑:下面的很多答案都假设您使用的是 protobuf-net.Grpc;重新阅读这篇文章后,这可能不是一个有效的假设——尽管它实际上在你的情况下会很好地工作!但是:在您的情况下,也许一个更简单的解决方案就是将代码替换为:
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");
(当图书馆的作者说“它可能不是你的最佳选择”时,你应该可能听一下?)
protobuf-net 是一个合约序列化器 - 它有助于序列化 数据合约 类型(在一般意义上 - 不是 WCF 特定的意义上)。 DataTable
是数据合约的对立面 - 它没有可预测的定义形状,因此它不能很好地与 protobuf-net 配合使用。我历来实验性尝试让它们可序列化 - 有一些bits here,但那是不是 protobuf-net 支持的库表面的一部分,并且坦率地说:预计它会失败。
我个人也会注意到:我强烈建议反对使用DataTable
来交换数据 - 它几乎永远不是正确的类型除非您正在编写ad-hoc 查询系统,例如报告工具或类似 SSMS/SEDE 的东西 - 即采用某种查询语言并返回某种无法提前知道的数据形状。
但是:这是一个附带话题,可能超出你的控制范围,所以:让我们谈谈DataTable
:
我认为我们应该忘记这里的“protobuf-net”部分,并专注于您实际尝试做的事情。我将在这里阅读几行之间的内容,并猜测您实际上之后是:您正在使用 protobuf-net.Grpc,并且您想要交换 DataTable
或DataSet
。好消息是:protobuf-net.Grpc 实际上并没有以任何方式 绑定到 protobuf-net(默认情况下除外)。您可以添加自己的编组器工厂(或工厂)来处理任何类型的类别,或者对于单个类型,你可以给它一个编组器,例如- 完全未经测试,但这应该可以工作:
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 根据通过服务的数据做出的 - 我的意思是:正在发送的数据的参数类型和返回类型对于正在接收的数据。这意味着只有当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
解析编组器 并且从不查看它的内部 - 这意味着编组器将是 protobuf-net - 和 protobuf-net不适用于DataTable
。
【讨论】:
感谢马克的有用回复。你能告诉我,有没有办法发送更大的数据表,比如有 1000 万行的表,序列化并在最短的时间内发送到 grpc 服务器。您对在最短的时间内在客户端和服务器之间发送批量数据有任何想法吗? @SankarSai“发送更少的数据”通常对我有用......但是:我不确定这里的具体问题是什么?当然,像 protobuf 这样的二进制编码非常适合,但是如果您要发送非常大的有效载荷,您可能希望在 gRPC 中使用 streaming,分批发送数据(例如,1k 或 10k每批次的行数 - 不是单独的行);如果您使用的是 protobuf-net.Grpc:这意味着使用IAsyncEnumerable<T>
以上是关于Protobuf-net Error:Type is not expected, and no contract can be inferred: System.Data.DataTable的主要内容,如果未能解决你的问题,请参考以下文章
Protobuf-net:嵌套的 IEnumerable 对象