我正在开发一个使用 MongoDB (使用C#驱动程序)和 DDD 的项目。
我有一个类(聚合),它有一个类型是接口的属性。在另一个类中,我实现了这个接口。这个类有另一个属性,类型是一个接口,并用另一个实现的类设置。
以下代码更好地说明了:
// 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:反序列化时发生错误 类的Partner属性。用户:错误 在反序列化类的Phone属性时发生 .Partner:价值等级 .Mobile无法反序列化。 ---&GT; System.IO.FileFormatException:反序列化时发生错误 类的移动属性.Partner:Value class .Phone无法反序列化。 ---&GT; MongoDB.Bson.BsonSerializationException:值类 .Phone无法反序列化。
然后,我在互联网上搜索了如何将类型反序列化为接口,我想我必须要做的方法:使用强制转换,使用BsonClassMap.RegisterClassMap
或编写自定义BSON来映射属性串行器。
我需要知道这两种方法中的哪一种更好以及如何实现它。
注意:我需要一个不修改接口的解决方案,因为他们的项目不能包含任何外部引用。
答案 0 :(得分:12)
嗯,我在尝试得到这个答案时发现了很多问题。
首先,MongoDB C#驱动程序确实存在一些问题反序列化接口,就像Craig Wilson在这个问题评论中所说的那样,并且如the issue page中所述。
这个问题的安全实现,就像我之前说的那样,可能是使用BsonClassMap.RegisterClassMap
的自定义BSON序列化程序或特定的类映射。
所以,我已经实现了类映射并且问题仍然存在。
期待这个问题,我发现该异常与驱动程序的另一个问题有关:反序列化structs
时的问题。
我已将项目回滚到初始状态(没有类映射或自定义序列化程序),并将结构类型更改为类类型,并且工作正常。
在恢复中,此异常错误与结构反序列化有关,而与反序列反序列化无关。
无论如何,这是一个真正的问题,第二个问题需要被视为一个错误,而不是改进,就像第一个问题一样。
您可以在以下链接中找到问题:
答案 1 :(得分:3)
[BsonSerializer(typeof(ImpliedImplementationInterfaceSerializer<IReviewExpert, ReviewExpert>))]
public IReviewExpert Expert { get; set; }
适合我
答案 2 :(得分:1)
我们在mongo司机的1.x分支上,遗憾的是没有Robert Baker建议的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;}
关于代码的一些注意事项 - 它真正做的就是懒惰地为相应的具体类型加载序列化程序,然后将所有序列化/反序列化调用传递给具有适当具体类型而不是接口的那些。
它还检查该类型实际上是否为预期类型,如果不是只找到该类型的默认序列化器。