Protobuf-Net InvalidOperationException w/out error message

本文关键字:error message out InvalidOperationException Protobuf-Net | 更新日期: 2023-09-27 18:31:30

我使用以下类来序列化带有前缀的单个对象(T),然后尝试反序列化回带有DeserializeItems()方法的列表:

public class ProtobufSerializationProvider<T> : ISerializationProvider<T>
{
    readonly Type type; 
    readonly TypeModel model;
    public ProtobufSerializationProvider()
    {
        this.type = typeof(T); 
        this.model = createModel(this.type);
    }
    public byte[] Serialize(T instance)
    {
        byte[] buffer; 
        using (MemoryStream strm = new MemoryStream())
        {
            model.SerializeWithLengthPrefix
              (strm, instance, type, PrefixStyle.Base128, 1);
            buffer = strm.GetBuffer();              
        }
        return buffer; 
    }
    // here is the problem method
    public IEnumerable<T> DeserializeAll(MemoryStream stream)
    {
        return model.DeserializeItems<T>(stream, PrefixStyle.Base128, 1); 
    }
    TypeModel createModel(Type type)
    {
        try
        {
            RuntimeTypeModel runtimeModel = TypeModel.Create();
            this.addTypeToModel(runtimeModel, type);
            this.addTypePropertiesToModel(runtimeModel, type);
            return runtimeModel.Compile();
        }
        catch (Exception e)
        {
            throw e.InnerException;
        }
    }
    void addTypePropertiesToModel(RuntimeTypeModel typeModel, Type type)
    {
        PropertyInfo[] properties = type.GetProperties();
        for (int i = 0; i < properties.Length; i++)
        {
            Type innerType = properties[i].PropertyType;
            if (!innerType.IsPrimitive && !(innerType == typeof(string)))
            {
                addTypeToModel(typeModel, properties[i].PropertyType);
            }
        }
    }
    MetaType addTypeToModel(RuntimeTypeModel typeModel, Type t)
    {                
        var properties = t.GetProperties()
            .Select(p => p.Name)
            .OrderBy(name => name);
        return typeModel
            .Add(t, true)
            .Add(properties.ToArray());            
    }
}

当我尝试枚举 IEnumerable 时,无论是通过强制转换 ToList() 还是对其进行计数 Count() 等,我都会收到默认的 InvalidOperationException "由于对象的当前状态,操作无效",特别是 MoveNext() 方法将抛出错误:

enumerator.MoveNext()
此外,流

必须打开,直到反序列化项目(流)返回,我确保情况确实如此。但是一旦IEnumerable成功返回,我就无法使用它了。

不确定序列化项目是否存在问题。我还注意到我的每个项目 256 字节,尽管其中很大一部分字节只是尾随的空值。

这是我序列化为测试的类。请注意,我没有使用属性,因为我手动创建模型:

public class NestedFoo
{
    public string NestedFooStr { get; set; } 
}
public class Foo
{
    public string Foo1 { get; set; }
    public string Foo2 { get; set; }
    public string Foo3 { get; set; }
    public string Foo4 { get; set; }
    public NestedFoo NestedFoo { get; set; }
}

谢谢。

更新

从 MemoryStream.GetBuffer() 切换到 MemoryStream.ToArray() 后,消息被缩短,但尝试转换 IEnumerable 。ToList() 或任何其他操作都会产生相同的错误。

但是,我注意到切换到.ToArray() 枚举后 IEnumerable 的 'Current' 属性在抛出错误之前到达 IEnumerable 中的最后一个元素,而当使用 .GetBuffer() 抛出错误时第一个元素上的"当前"属性。

我怀疑问题可能是 IEnumerable<> 不知道它什么时候没有元素,因为它到达了流的末尾?我将几个使用 SerializeWithLengthPrefix() 序列化的项目附加到单个字节 [],然后可能会在数组尾随时留下空值。如果我将整个数组读入 DeserializeItems 方法,它是否需要知道如何终止,还是仅在检测到长度前缀时才继续(如我所期望的那样?

我的另一个问题是,使用 SerializeWithLengthPrefix() 方法在多大程度上等效于使用 DataFormat.Group 枚举在一个操作中序列化 List,如下所示:

[ProtoContract]
public class ProtoList<T>
{
    [ProtoMember(1, DataFormat = DataFormat.Group)]
    public List<T> List { get; set; }
    public ProtoList() { }
    public ProtoList(IEnumerable<T> items)
    {
        this.List = new List<T>(items); 
    }
}

使用这样的容器似乎表现得很好,所以在我想一次性序列化列表的情况下,我可能会选择这条路线。

但是,为了在添加项目时单独序列化项目,我目前正在使用自己的前缀函数,因此,如果我可以让 Protobuf 前缀方法正常工作,那也很好。

Protobuf-Net InvalidOperationException w/out error message

256/尾随空值是因为您使用的是MemoryStream.GetBuffer() 。这种方法只要与Length结合使用就可以,因为缓冲区过大。如果需要大小合适的数组,请改用ToArray()。或者使用ArraySegment<byte>以避免复制任何数据。

这也有可能修复异常(0 不是字段标题的有效值)。如果没有,请发表评论,以便我知道更详细地查看。如果它确实修复了它,也请告诉我,我会尝试使错误消息(对于 0 字段标题情况)更有帮助。

只是为了确认,遇到了同样的问题。

不要将标签/数据包 ID 用作 0。解决了一切。