C# 负载平衡系统中的Protobuf网络动态类型

C# 负载平衡系统中的Protobuf网络动态类型,c#,protobuf-net,C#,Protobuf Net,我在一个应用程序中使用protobuf net,该应用程序对来自第三方DLL的对象进行大量二进制序列化(无论出于何种目的)。因此,我不能在契约本身上使用[Proto-]属性,而是使用RuntimeTypeModel在遇到新类型时在运行时准备序列化程序。序列化程序包装器示例: public static class ProtobufSerializer { public static byte[] Serialize<T>(T obj) { Prepa

我在一个应用程序中使用protobuf net,该应用程序对来自第三方DLL的对象进行大量二进制序列化(无论出于何种目的)。因此,我不能在契约本身上使用[Proto-]属性,而是使用RuntimeTypeModel在遇到新类型时在运行时准备序列化程序。序列化程序包装器示例:

public static class ProtobufSerializer
{
    public static byte[] Serialize<T>(T obj)
    {
         PrepareSerializer(typeof(T));
         ProtoBuf.Serialize(memoryStream, obj); 
    }

    public static T Deserialize<T>(byte[] bytes)
    {
         PrepareSerializer(typeof(T));
         ProtoBuf.Serialize(memoryStream, obj); 
    }
}
以及实施:

public class Foo : IFoo
{
    public string Name {get;set;}
    public Bar Bar {get;set;}

    [OnDeserializing]
    public void OnDeserializing(Type t)
    {
        PrepareSerializer(typeof(Foo));
    }
}

public class Bar
{
    public int Baz {get;set;}
}
PrepareSerializer
方法基本上使用代理并生成大致相当于以下内容的模型:

// registered surrogate for IFoo
[ProtoContract]
public class IFooSurrogate
{
    [ProtoMember(1, DynamicType=true)]
    public object Value

    [OnSerializing]
    public void OnSerializing(Type t)
    {
        PrepareSerializer(this.Value.GetType());
    }
}
其中,
由隐式转换器设置为等于IFoo实例。这在序列化过程中工作得很好(在序列化过程中触发事件,并使我有机会为特定接口实现类型准备序列化程序)。在非分布式系统中,它也可以正常工作,在尝试反序列化该类型之前,我必须先运行序列化方法。然而,在分布式系统中进行反序列化时,当前节点以前从未见过Foo,ProtoBuf.Serializer会抛出一个
invalidoOperationException
,在运行Foo.ondeSerialization事件之前,抱怨缺少类型
Bar
的序列化程序(让我有机会告诉它如何反序列化Bar)


在protobuf net抱怨缺少序列化程序之前,有没有办法附加一个钩子来确保我的代码有机会了解“Foo”?

我没有尝试过这种情况,但是:为了允许一些灵活性,所有
Type
存储和再水化都通过
TypeModel.DynamicTypeFormatting
事件进行;因此,从理论上讲,您可以在
RuntimeTypeModel.Default
上挂接此事件,如下所示:

RuntimeTypeModel.DynamicTypeFormatting += (sender, args) => {
    if (args.FormattedName != null) { // meaning: rehydrating
        lock(SomeSyncLock) {
            if(NotYetKnown(args.FormattedName))
                Prepare(args.FormattedName);
        }
    }
};
此API的目的是允许您控制如何解析类型,但是。。。我想这也行吗



然而,我可以理解这样一个想法,即在第一次看到新的
类型时,更有意地将事件作为目标,从本质上替代/补充“应用默认行为”代码。不过,我认为它在今天并不存在。

这种方法非常有效。我在早些时候看到这个事件是为了优化动态类型的大小,但没有考虑到这一点。谢谢@MarcGravel如果我们将此DynamicTypeFormatting事件的处理程序添加到RuntimeTypeModel.Default中,是否会为所有序列化对象调用它,或者是否有其他RuntimeTypeModel实例需要挂接?另外,您能否确认“所有类型的存储和再水化都通过”这一点,而不仅仅是动态类型?我们已经实现了上述功能,从序列化流中省略了程序集版本,但看起来有些类型是使用默认名称编写的,所以我想知道这是否会被跳过。@zvolkov如果你有一个例子,它做了一些奇怪的事情,我很想在github看到它
RuntimeTypeModel.DynamicTypeFormatting += (sender, args) => {
    if (args.FormattedName != null) { // meaning: rehydrating
        lock(SomeSyncLock) {
            if(NotYetKnown(args.FormattedName))
                Prepare(args.FormattedName);
        }
    }
};