我正在使用以下类来序列化带有前缀的单个对象 (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()
此外,必须在 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<> 不知道它什么时候没有元素,因为它到达了流的末尾?我将使用 SerializeWithLengthPrefix() 序列化的几个项目附加到单个字节 [],然后可能会有空值留在数组尾随。如果我将整个数组读入 DeserializeItems 方法,它是否需要知道如何终止或仅在检测到长度前缀时才继续(如我所料?)。
我还有一个问题是使用 SerializeWithLengthPrefix() 方法在多大程度上等同于使用 DataFormat.Group 枚举在一个操作中序列化一个列表,例如:
[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 前缀方法工作,那也很好。