Подтвердить что ты не робот

Отключить объект как интерфейс с драйвером MongoDB С#

Я разрабатываю проект, который использует MongoDB (с драйвером С#) и DDD.

У меня есть класс (aggregate), у которого есть свойство, тип которого является интерфейсом. В другом классе я реализовал этот интерфейс. Этот класс имеет другое свойство, тип которого является интерфейсом и настроен на другой реализованный класс.

Ниже приведен код ниже:

// Interfaces
public interface IUser {
    Guid Id { get; set;}
    IPartner Partner{ get; set; }
}

public interface IPartner {
    IPhone Mobile { get; set; }
}

public interface IPhone {
    string number { get; set; }
}

// Implemented Classes
public class User: IUser {
    [BsonId(IdGenerator = typeof(GuidGenerator))]
    public Guid Id { get; set; }

    [BsonIgnoreIfNull]
    public IPartner Partner { get; set; }
}

public struct Partner : IPartner {
    public IPhone Mobile { get; set; }
}

public struct Phone : IPhone {
    public string Number { get; set; }
}

Ну, когда я вызываю метод MongoCollection<User>.Insert(), он выдает два исключения:

System.IO.FileFormatException: при десериализации произошла ошибка свойство партнера класса .User: ошибка произошло при десериализации свойства Phone класса .Partner: класс ценности .Mobile нельзя десериализовать. --- > System.IO.FileFormatException: при десериализации произошла ошибка свойство Mobile класса .Partner: Value класс. Телефон нельзя десериализовать. --- > MongoDB.Bson.BsonSerializationException: класс значений. Телефон не может быть десериализован.

Затем я искал в Интернете, чтобы узнать, как десериализовать тип как интерфейс, и я думаю, что мне нужно это сделать: сопоставление свойства с литой, используя BsonClassMap.RegisterClassMap или запись пользовательского сериализатора BSON.

Мне нужно знать, какой из этих двух способов лучше и как его реализовать.

Примечание. Мне нужно решение, которое не изменяет интерфейсы, потому что их проект не может содержать никаких внешних ссылок.

4b9b3361

Ответ 1

Ну, я нашел много проблем при попытке получить этот ответ.

Прежде всего, драйвер MongoDB С# имеет некоторые проблемы при десериализации интерфейсов, как сказал Крейг Уилсон в этом вопросе, и как описано в страница вопроса.

Безопасная реализация для этой проблемы, как я уже говорил, действительно может быть обычным сериализатором BSON или конкретной картой классов, используя BsonClassMap.RegisterClassMap.

Итак, я реализовал карту классов, и проблема осталась.

В ожидании этой проблемы я нашел , что это исключение связано с другой проблемой драйвера: проблема при десериализации structs.

Я отбросил проект в исходное состояние (без карты классов или пользовательских сериализаторов) и изменил тип структуры на тип класса, и работал.

В резюме эта ошибка исключения связана с десериализацией структур, а не с десериализацией интерфейсов.


В любом случае, это настоящая проблема, и вторая проблема должна считаться скорее ошибкой, чем улучшением, например, первой проблемой.

Вы можете найти проблемы по этим ссылкам:

Ответ 2

[BsonSerializer(typeof(ImpliedImplementationInterfaceSerializer<IReviewExpert, ReviewExpert>))] public IReviewExpert Expert { get; set; }

работает для меня

Ответ 3

Мы находимся на ветке драйверов mongo 1.x и, к сожалению, нет ImpliedImplementationInterfaceSerializer, предложенного Робертом Бейкером, который, по-видимому, является хорошим решением. С этой целью я создал свой собственный сериализатор, который позволяет указать тип confcrete для члена интерфейса.

public class ConcreteTypeSerializer<TInterface, TImplementation> : BsonBaseSerializer where TImplementation : TInterface
{
    private readonly Lazy<IBsonSerializer> _lazyImplementationSerializer;

    public ConcreteTypeSerializer()
    {
        var serializer = BsonSerializer.LookupSerializer(typeof(TImplementation));

        _lazyImplementationSerializer = new Lazy<IBsonSerializer>(() => serializer);
    }

    public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
    {
        if (bsonReader.GetCurrentBsonType() == BsonType.Null)
        {
            bsonReader.ReadNull();
            return default(TInterface);
        }
        else
        {
            return _lazyImplementationSerializer.Value.Deserialize(bsonReader, nominalType, typeof(TImplementation), options);
        }
    }

    public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
    {
        if (value == null)
        {
            bsonWriter.WriteNull();
        }
        else
        {
            var actualType = value.GetType();
            if (actualType == typeof(TImplementation))
            {
                _lazyImplementationSerializer.Value.Serialize(bsonWriter, nominalType, (TImplementation)value, options);
            }
            else
            {
                var serializer = BsonSerializer.LookupSerializer(actualType);
                serializer.Serialize(bsonWriter, nominalType, value, options);
            }
        }
    }
}

Использование выглядит следующим образом:

[BsonSerializer(typeof(ConcreteTypeSerializer<IMyInterface,MyClass>))]
public IMyInterface MyProperty {get; set;}

Несколько примечаний к коду - все, что он действительно делает, - это лениво загружает сериализатор для соответствующего конкретного типа, а затем передает все вызовы serialize/deserialize с соответствующим конкретным типом, а не с интерфейсом.

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