mongodb C#driver

时间:2018-04-24 13:46:29

标签: c# mongodb serialization

我为IBsonSerializer类编写了一个自定义System.Security.Claims.Claim,它非常臃肿,需要自定义反序列化。这在被称为一次性时起作用,但我在注册它并从单元测试中调用并获得可怕的异常时遇到问题"已经有一个序列化器注册了类型Claim"。

问题是,似乎无法检查序列化程序是否在没有创建序列化程序的情况下进行注册!这对我来说似乎完全错了。如果您致电BsonSerializer.LookupSerializer(),则代码会调用BsonSerializerRegistry.GetSerializer(),即GetOrAdd()。换句话说,我无法检查,然后添加我的自定义序列化程序,没有例外,如果我没有检查,如果第二次添加它,我可能会收到错误。

我错过了一些非常明显的东西吗?单元测试在多线程进程中调用,因此即使我的设置在Startup.cs中并且通常只调用一次,也可以在单元测试运行时并行调用。

lock (serializerLock)
{
    // I can't call the following, otherwise it creates a default serializer
    //if ( BsonSerializer.LookupSerializer<System.Security.Claims.Claim>().GetType() != typeof(MongoClaimSerializer))
    {
         BsonSerializer.RegisterSerializer(new MongoClaimSerializer());
    }
}

2 个答案:

答案 0 :(得分:1)

以下代码应为您需要的序列化程序注册和查找提供足够的封装。

public static class SafeBsonSerializerRegistry
{
    private static ConcurrentDictionary<Type, IBsonSerializer> _cache = new ConcurrentDictionary<Type,IBsonSerializer>();

    public static IBsonSerializer<T> LookupSerializer<T>()
    {
         return (IBsonSerializer<T>)LookupSerializer(typeof(T));
    }

    public static IBsonSerializer LookupSerializer(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        var typeInfo = type.GetTypeInfo();
        if (typeInfo.IsGenericType && typeInfo.ContainsGenericParameters)
        {
            var message = string.Format("Generic type {0} has unassigned type parameters.", BsonUtils.GetFriendlyTypeName(type));
            throw new ArgumentException(message, "type");
        }

        IBsonSerializer serializer;

        if(_cache.TryGetValue(type, out serializer))
            return serializer;
        return null; //Note, this is where the BsonSerializerRegistry would GetOrAdd().
    }



    public static void RegisterSerializer<T>(IBsonSerializer<T> serializer)
    {
        RegisterSerializer(typeof(T), serializer);
    }

    public static void RegisterSerializer(Type type, IBsonSerializer serializer)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (serializer == null)
            throw new ArgumentNullException("serializer");

        var typeInfo = type.GetTypeInfo();
        if (typeof(BsonValue).GetTypeInfo().IsAssignableFrom(type))
        {
            var message = string.Format("A serializer cannot be registered for type {0} because it is a subclass of BsonValue.", BsonUtils.GetFriendlyTypeName(type));
            throw new BsonSerializationException(message);
        }

        if (typeInfo.IsGenericType && typeInfo.ContainsGenericParameters)
        {
            var message = string.Format("Generic type {0} has unassigned type parameters.", BsonUtils.GetFriendlyTypeName(type));
            throw new ArgumentException(message, "type");
        }

        if (!_cache.TryAdd(type, serializer))
        {
            var message = string.Format("There is already a serializer registered for type {0}.", BsonUtils.GetFriendlyTypeName(type));
            throw new BsonSerializationException(message);
        }

        //call to the actual BsonSerializer
        BsonSerializer.RegisterSerializer(type, serializer);
    }

}

这不是替换现有的BsonSerializer或底层的BsonSerializerRegistry。它只保证LookupSerializer方法提供调用者使用它来首先注册它们。

如果这不合适,请告诉我,我会回到绘图板。

答案 1 :(得分:1)

我的解决方法是调用RegisterSerializer而不是使用新类调用RegisterSerializationProvider(new ClaimSerializationProvider()),而不会在解析期间直接向系统提供序列化程序。这不会检查重复项,因此如果在多线程方案中RegisterSerializationProvider被调用两次,则不会出错。提供者看起来像这样:

public class ClaimSerializationProvider : IBsonSerializationProvider
{
    /// <summary>
    /// <see cref="IBsonSerializationProvider.GetSerializer(Type)"/>
    /// </summary>
    public IBsonSerializer GetSerializer(Type type)
    {
        // We only provide a custom serializer for Claim
        if (type == typeof(Claim))
        {
            return new MongoClaimSerializer();
        }

        return null;
    }
}