用protobuf替换binaryformatter

时间:2014-05-12 22:44:46

标签: c# c#-4.0 protobuf-net

我需要替换一个在文件中存储数据的库(序列化/反序列化) 这个库目前使用BinaryFormatter,但对于大型列表来说速度很慢。 stackoverflow上的很多帖子都表明protobuf真的很高效,所以我试着用它。

为了在不重写大量代码的情况下进行替换,我的要求是:

  • 必须序列化通用数据,因为我要与Store<T>(T data)接口(大部分时间T都标有DataContract和/或Serializable属性)
  • 我不能修改所有不同的objets类来添加protobuf属性,所以我需要一个不使用这些属性的泛型方法
  • 我不需要向后兼容:如果T对象架构发生更改(即新属性),则所有生成的文件都已过时,将被删除/重新创建

我的第一个和天真的实现看起来像这样:

public bool Store<T>(string key, T data)
{
    var formatter = Serializer.CreateFormatter<T>();
    using (var fileStream = new FileStream(this.GetFilePath(key), FileMode.Create))
    {
        formatter.Serialize(fileStream, data);
        return true;
    }
}

但后来我得到了这个例外:

  

预计不会输入类型,也无法推断合同:   My.Application.Namespace.ShortcutData

目前我有点陷入困境,我没有找到关于如何使用protobuf-net的好教程。

使用protobuf可以达到这些要求吗?你有关于如何做到这一点的好教程吗?

编辑:

问题确实是我需要告诉protobuf如何(de)序列化数据。 这就是我现在所拥有的:

public bool Store<T>(string key, T data)
{
    this.Register<T>();
    var formatter = Serializer.CreateFormatter<T>();
    using (var fileStream = new FileStream(this.GetFilePath(key), FileMode.Create))
    {
        formatter.Serialize(fileStream, data);
        fileStream.Close();
        return true;
    }
}

主要代码在Register方法中:

protected void Register(Type type)
{
    if (type.IsGenericType)
    {
        var arguments = type.GetGenericArguments();
        foreach (var argument in arguments)
            this.Register(argument);
    }

    if (!this._registeredTypes.Contains(type) && !type.IsValueType)
    {
        this._registeredTypes.Add(type);
        var properties = type.GetProperties();
        foreach (var property in properties)
        {
            this.Register(property.PropertyType);
        }

        try
        {
            ProtoBuf.Meta.RuntimeTypeModel.Default
                .Add(type, false)
                .Add(properties
                    .Where( p => p.CanWrite)
                    .OrderBy(x => x.Name)
                    .Select(x => x.Name)
                    .ToArray());
        }
        catch
        {
            // I've a problem here: I sometime have an error for an already registered type (??)
        }
    }
}

我知道这不是一个干净的代码,但这只是对现有代码的替代,并且在第二步中将完全重写protobuf。

2 个答案:

答案 0 :(得分:2)

我刚刚开始玩Protobuf-net ..所以请不要认为我的回答无论如何都是&#34;正确的&#34;做事的方式。毫无疑问,Marc Gravell会在某些方面为您提供答案,您应该注意这一点。

..然而,我前几天写的概念验证要求我得到一堆类序列化并快速完成。为此......我想出了这个:

var assembly = Assembly.Load("Assembly.Name.Here");

foreach (var type in assembly.GetTypes().Where(x => typeof (IInterfaceEachClassImplements).IsAssignableFrom(x))) {
    RuntimeTypeModel
        .Default
        .Add(type, false)
        .Add(type.GetProperties()
            .Select(x => x.Name)
            .ToArray());
}

基本上,它基于它们全部实现的接口将每个类加载到protobuf-net类型模型中..而不是应用于它们的属性。

希望无论如何这都指向了正确的方向。

答案 1 :(得分:2)

什么是ShortcutData? protobuf格式不包含任何类型元数据,因此引擎必须具备有关如何映射数据的一些知识。这可以通过多种方式完成,包括通过各种属性。也可以在运行时通过涉及RuntimeTypeModel的代码指定映射。例如,如果我们的ShortcutData看起来像:

public class ShortcutData {
    public int Key {get;set;}
    public string Link {get;set;}
}

然后我们可以这样做:

[ProtoContract]
public class ShortcutData {
    [ProtoMember(1)]
    public int Key {get;set;}
    [ProtoMember(2)]
    public string Link {get;set;}
}

其中12将成为protobuf字段标识符。要序列化,您不需要使用formatter API。简单地:

public bool Store<T>(string key, T data)
{
    using (var fileStream = File.Create(GetFilePath(key)))
    {
        Serializer.Serialize<T>(fileStream, data);
        return true;
    }
}