我正在使用最新的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而从高速缓存反序列化中检索该值失败。
因此,当set为first时,序列化值的类型存储在typeCache变量中,然后从该字典中重新获取。但是,如果值存在于memcache中但未在当前线程中设置,则typeCache var不包含该类型,并在反序列化时抛出NullReference。
有没有办法解决这个问题或某些解决方法?
更深入的调查:
enyim的序列化/反序列化过程在ProtoTranscoder.cs
中实现。它包含NetTranscoder
类Dictionary<ArraySegment<byte>, Type> typeCache
。
所以当set第一个序列化类型(即List<MyClass>
)存储在typeCache
var中时,一切顺利。如果memcache中存在值,但在反序列化时未在当前应用程序/线程中设置,则此代码将对其进行检索:
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();
}
重现该错误:
以下是此错误的堆栈跟踪: [ArgumentNullException:Value不能为null。 参数名称:type]
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
答案 0 :(得分:3)
在Marc Gravell的帮助下,我们找到了&#34; bug&#34;在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等上失败
为避免这种情况,最好使用正则表达式来删除非必要的信息(如culture,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);
答案 1 :(得分:0)
我认为memcache不理解ProtoContract
,尝试将其标记为DataContract