【问题标题】:Protobuf-net memcache provider null type error on deserialize反序列化时 Protobuf-net memcache provider null 类型错误
【发布时间】:2013-01-29 12:25:06
【问题描述】:

我正在使用最新的 protobuf-net lib 和 protobuf-net memcache 提供程序。我需要序列化自定义类型 MyClass 的列表

[ProtoContract]
public class MyClass{
  [ProtoMember(1)]
  public int a {get; set;}
  [ProtoMember(2)]
  public int b {get; set;}
}

所以我需要存储/检索:

List<MyClass> myList

当值通过 protobuf 存储然后从缓存中检索时一切顺利。但是,如果值存储在 memcache 中(以正确的 protobuf 格式),即来自另一个线程/应用程序,然后由于类型字段的 NullReferenceExceptions 从缓存反序列化中检索该值失败。

所以当先设置时,序列化值的类型存储在 typeCache 变量中,然后从该字典中检索。但是,如果值存在于内存缓存中但未在当前线程中设置,则 typeCache var 不包含该类型并在反序列化时抛出 NullReference。

有没有办法解决这个问题或一些变通方法?

更深入的调查: 在ProtoTranscoder.cs 中实现的 enyim 的序列化/反序列化过程。它包含具有Dictionary&lt;ArraySegment&lt;byte&gt;, Type&gt; typeCacheNetTranscoder 类。 因此,当 set 第一个序列化类型(即List&lt;MyClass&gt;)存储在typeCache var 中,一切顺利。如果值存在于内存缓存中,但未在当前应用程序/线程中设置反序列化,则此代码检索它:

type = Type.GetType(enc.GetString(buffer, keyOffset, len));
byte[] standaloneBuffer = new byte[len];
Buffer.BlockCopy(buffer, keyOffset, standaloneBuffer, 0, len);
key = new ArraySegment<byte>(standaloneBuffer, 0, len);
sync.EnterWriteLock();
try
   {
        // did somebody beat us to it?
        Type tmp;
        if (typeCache.TryGetValue(key, out tmp)) return tmp;
        typeCache.Add(key, type);
        return type;   <-- Here it returns null, if type not present in typeCache
   }
finally
   {
        sync.ExitWriteLock();
   }

重现该错误:

  1. 列表项
  2. 在内存缓存中创建和存储一些列表(使用已配置的原型转码器)
  3. 重启当前应用(或启动另一个线程)
  4. 尝试从那些“另一个线程”的 memcache 中按键获取值

这是此错误的堆栈跟踪: [ArgumentNullException:值不能为空。 参数名称:类型]

   ProtoBuf.Meta.TypeModel.PrepareDeserialize(Object value, Type& type) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:592
   ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:577
   ProtoBuf.Caching.Enyim.NetTranscoder.Enyim.Caching.Memcached.ITranscoder.Deserialize(CacheItem item) in c:\Users\akureniov\work\protobuf-net-1\protobuf-net.Enyim\protobuf-net.Enyim\ProtoTranscoder.cs:109
   Enyim.Caching.MemcachedClient.PerformTryGet(String key, UInt64& cas, Object& value) +179
   Enyim.Caching.MemcachedClient.TryGet(String key, Object& value) +42
   Enyim.Caching.MemcachedClient.Get(String key) +15

【问题讨论】:

  • 请具体说明:您使用的是哪个 dll 版本? “最新的 protobuf-net 库”不包括 enyim 转码器(“扩展”项目尚未针对 v2 进行修订,并且不构建 AFAIK,尽管它在我的列表中查看)。那么:您使用的是旧版本的转码器吗?您是否修补了“扩展”?还是您手动存储类型?
  • 您是否正在使用ProtoTranscoder NuGet 包?
  • 我正在使用从 svn 源代码编译的 protobuf-net-r622 lib 和 protobuf enyim 模块(它作为单独的项目进入 svn)。 Nuget 包不适用于 couchbase enyim.cache lib 女巫具有不同的公钥。
  • 您能准确地指出您正在使用/从源代码编译哪个“protobuf enyim 模块”吗?没有那个很难评论......
  • 它位于你的svn中。文件夹名为“protobuf-net.Enyim”。我已将参考 protobuf-net 库更新为 r622 版本,然后对其进行编译。此外,我将很快更新我的第一篇文章,提供更深入的错误痕迹。

标签: .net memcached protobuf-net


【解决方案1】:

Marc Gravell 的帮助下,我们在 ProtoTranscoder.cs 的类型修整中发现了“错误”:NetTranscoder: void WriteType(MemoryStream ms, Type type)。此代码块导致错误,因为它剪切太多:

int i = typeName.IndexOf(','); // first split
if (i >= 0) { i = typeName.IndexOf(',', i + 1); } // second split
if (i >= 0) { typeName = typeName.Substring(0, i); } // extract type/assembly only

它适用于简单类型,但在 List、Dictionary 等上却失败了。

为避免这种情况,最好使用正则表达式来减少不必要的信息(如文化、publickkeytoken 等)。所以这是一个替换(需要用这个替换上面的行),安静粗鲁,但在大多数情况下都有效:

typeName = Regex.Replace(typeName, @", Version=\d+.\d+.\d+.\d+", string.Empty);
typeName = Regex.Replace(typeName, @", Culture=\w+", string.Empty);
typeName = Regex.Replace(typeName, @", PublicKeyToken=\w+", string.Empty);

此正则表达式不会剪切类型的程序集,这是自定义类型所必需的。但是对于标准类型,它是mscorlib,并且在大多数情况下可以通过添加另一行来安全地删除它:

typeName = Regex.Replace(typeName, @", mscorlib", string.Empty);

【讨论】:

  • 最后一段应该说“程序集”,而不是“命名空间”——但看起来不错,谢谢!稍后我会看到合并它。非常感谢。
  • 修复了最后一段。谢谢。我还在复杂的列表和字典上测试了我的代码,似乎效果很好。删除 mscorlib 也可以正常工作。
  • 这个问题解决了吗?我想我在 protobuf-net v2.0.0.621 中看到了这个问题。这也很奇怪——我可以成功存储一些列表,但不能存储其他列表。它似乎依赖于 List 的泛型类型。
  • 我可以看到存档关闭。网站没有改变。所以你解决这个问题的方法:从 svn 获取源代码,替换这些行并编译它。到目前为止,此修复程序在我们拥有的所有类型的项目中都可以正常工作。
  • 奇怪——我抓住了源(对于 protobuf-net 和 protobuf-net.enyim),但不能序列化/反序列化正常的 [Serializable] 类(即:没有 [ProtoContract][DataContract]属性)。它适用于 Protobuf-net 和 Protobuf-net.Enyim 的 nuget 版本,但不适用于我下载的源代码。单步执行 pf-n.enyim 单元测试,我看到我的类型未能通过TypeModel.CanSerializeContractType。它适用于 nuget 版本,而不是我的开源版本。我错过了什么? (protobuf-net 的标准 .Net 4 版本)
【解决方案2】:

我认为memcache不理解ProtoContract,尝试将它们标记为DataContract

【讨论】:

  • 假设 OP 正在注册一个自定义转码器;如果是这样,enyim 可以为所欲为
猜你喜欢
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 2010-10-15
  • 2012-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多