Protobuf-Net InvalidOperationException w / out错误消息

时间:2012-04-16 21:07:48

标签: c# serialization protocol-buffers protobuf-net invalidoperationexception

我使用以下类来序列化带有前缀的单个对象(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());            
    }
}

我得到一个默认的InvalidOperationException“当我尝试枚举IEnumerable时,由于对象的当前状态,操作无效”,无论是通过强制转换ToList()还是计算它的Count()等。具体来说,MoveNext ()方法将抛出错误:

enumerator.MoveNext()

在DeserializeItems(stream)返回之前,必须打开流,我确保是这种情况。但是一旦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()'Current'属性时在抛出错误时的第一个元素上。

我怀疑问题可能是IEnumerable&lt;&gt;不知道它的元素是什么时候因为它到达了流的末尾?我将使用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前缀方法工作也会很好。

2 个答案:

答案 0 :(得分:2)

256 /尾随空值是因为您正在使用MemoryStream.GetBuffer()。只要它与Length结合使用,此方法 fine ,因为缓冲区超大。如果您想要一个合适大小的数组,请改用ToArray()。或者,使用ArraySegment<byte>以避免复制任何数据。

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

答案 1 :(得分:1)

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

不要将标签/数据包ID用作0.解决所有问题。