Отключить объект как интерфейс с драйвером 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.
Мне нужно знать, какой из этих двух способов лучше и как его реализовать.
Примечание. Мне нужно решение, которое не изменяет интерфейсы, потому что их проект не может содержать никаких внешних ссылок.
Ответы
Ответ 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 с соответствующим конкретным типом, а не с интерфейсом.
Он также проверяет, что тип действительно относится к ожидаемому типу, и если он не просто находит сериализатор по умолчанию для типа.