用 Protobuf-net 替换 binaryformatter

Posted

技术标签:

【中文标题】用 Protobuf-net 替换 binaryformatter【英文标题】:Replace binaryformatter with Protobuf-net 【发布时间】:2012-11-19 04:04:38 【问题描述】:

我正在尝试用 Protobuf-net 替换我们使用二进制格式化程序进行序列化。我遇到了许多问题,并且想知道推荐的做事方式。 我有很多(很多)类要序列化。他们在一个解决方案中的多个项目中。

如果我尝试从要序列化的类所在的同一个项目中调用 Serialize,它可以工作,如果它们在不同的项目中,它会失败,不知道合同。我们的类还使用在其他项目中定义的类型(结构)。当我们尝试使用这个结构序列化一个类时,我们会遇到同样的问题,因为它不知道该类型。我可以使用 PrepareSerializer 手动添加类型,它会序列化......但是当我尝试反序列化时,我收到一个错误,即不允许部分受信任的调用者。

我也尝试使用预编译实用程序,但是我们的类使用带有公共访问器的私有字段,我收到一个错误,说它无法处理私有字段,所以我们不能用它来替换二进制格式化程序?

如何解决这些问题?

编辑 - 这是来自部分受信任的调用者异常的堆栈跟踪:

"That assembly does not allow partially trusted callers."
at proto_4(Object , ProtoReader )
   at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source)
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source)
   at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type)
   at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader)
   at proto_2(Object , ProtoReader )
   at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source)
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source)
   at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate)
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context)
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)
   at ProtoBuf.Serializer.Deserialize[T](Stream source)
   at BinarySerializationTest.Form1.button1_Click(Object sender, EventArgs e)
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at BinarySerializationTest.Program.Main()
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

【参考方案1】:

有趣。我对部分信任设置做的不多,但是如果你能让我知道堆栈跟踪在它爆炸时是什么(以及你从哪里调用 - 即调用序列化的程序集是否不同于调用反序列化的程序集),我也许可以在几个地方添加一些[AllowPartiallyTrustedCallers] 以使其工作。

预编译路线是我的另一个建议,但实际上这不适用于私有字段,因为独立程序集的可访问性更加严格。

第三种选择是将受影响的 private 字段转换为 internal 字段,并使用 [assembly:InternalsVisibleTo("NameOfGeneratedAssembly)] 使这些字段可访问 - 然后使用“预编译器”。然后可以访问这些字段,并且从 r602 开始支持(这是撰写本文时的最新版本)。

【讨论】:

谢谢 Marc,我会尝试预编译并告诉你它是如何进行的。另一个(可能是愚蠢的)问题 - 与预编译器有关。为什么它不能访问私有字段?如果一个类用 [Protocontract(AllFields)] 装饰(语法错误,但你知道我的意思),那么序列化程序可能通过反射访问私有字段?为什么预编译器也不能这样做? @Colin 因为这在程序集中是不合法的——它将无法通过验证并被 CLI 拒绝。但是,CLI 和预编译器都完全允许使用 internal + [InternalsVisibleTo(...)] 技巧。关于为什么它与CompileInPlace 一起工作:当你在内存中做事时,你可以作弊更多。对于信息,这也是为什么XmlSerializer 只允许公共成员 - 它生成一个程序集。 预编译正在我的测试项目中使用该技巧。将尝试将其插入完整的解决方案中,看看效果如何。 好的,另一个问题..我们的项目是强命名的,并且在 InternalsVisibleTo 中指定 PublicKey 不起作用 - 它说不能使用非公共成员。 @Colin 可以通过“sn”获取 internalsvisible 所需的密钥 - 例如,请参阅 MSDN 在 InternalsVisibleToAttribute 页面上

以上是关于用 Protobuf-net 替换 binaryformatter的主要内容,如果未能解决你的问题,请参考以下文章

Protobuf-net - 啥被序列化了?

protobuf-net 出现“已添加具有相同密钥的项目”错误

用于继承的 Protobuf-net .proto 文件生成

protobuf-net 中通用集合的序列化

为啥我的 List<Foo> 不能用 protobuf-net 序列化?

手工使用Protobuf-net工具来序列化对象