Кастомное исключение IBsonSerializer, брошенное в драйвер mongodb C # - PullRequest
0 голосов
/ 24 апреля 2018

Я написал пользовательский 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 голосов
/ 24 апреля 2018

Мой обходной путь состоял в том, чтобы вместо 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;
    }
}
0 голосов
/ 24 апреля 2018

Следующий код должен обеспечить достаточную инкапсуляцию для регистрации и поиска сериализатора, которые вам понадобятся.

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, при условии, что вызывающая сторона использовала это, чтобы зарегистрировать их в первую очередь.

Дайте мне знать, если это не подходит, и я вернусь к чертежной доске.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...