问题 将对象反序列化为MongoDB C#Driver的接口


我正在开发一个使用的项目 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无法反序列化。 --->   System.IO.FileFormatException:反序列化时发生错误   类的移动属性.Partner:Value   class .Phone无法反序列化。 --->   MongoDB.Bson.BsonSerializationException:值类   .Phone无法反序列化。

然后,我在互联网上搜索了如何将类型反序列化为接口,我想我必须要做的方法:使用强制转换来映射属性,使用 BsonClassMap.RegisterClassMap 或编写自定义BSON序列化程序。

我需要知道这两种方式中哪一种更好,以及如何实现它。

注意:我需要一个不修改接口的解决方案,因为他们的项目不能包含任何外部引用。


7716
2018-01-28 12:13


起源

我会尝试两个,看看哪个更容易。显然,编写自定义序列化器将很困难。我们正在研究如何更好地反序化这种类型的结构。 - Craig Wilson
再次感谢你,克雷格。在尝试自定义序列化程序之前,我将尝试注册类映射。那么,驱动程序没有这个功能来反序列化接口?如果计划好了,你可以给我一个来自MongoDB Jira的链接,如LinQ投影吗? - Gustavo Gondim
jira.mongodb.org/browse/CSHARP-485 - Craig Wilson
非常感谢你。我现在正在投票并关注这个问题。 - Gustavo Gondim


答案:


好吧,我在试图得到这个答案时发现了很多问题。

首先,MongoDB C#驱动程序确实存在一些问题 在反序列化接口时就像克雷格威尔逊在这个问题评论中所说的那样,并且如中描述的那样 问题页面

像我之前所说的,这个问题的安全实现可能是一个自定义的BSON序列化器或特定的类映射,使用 BsonClassMap.RegisterClassMap

所以,我已经实现了类映射并且问题仍然存在。

期待着这个问题,我发现了 该异常与驱动程序的另一个问题有关:反序列化时的问题 structs

我已将项目回滚到初始状态(没有类映射或自定义序列化器)并将结构类型更改为类类型, 它工作

在恢复中,此异常错误与结构反序列化有关,而不与接口反序列化有关。


无论如何,这是一个真正的问题,第二个问题需要被视为一个错误而不是改进,就像第一个问题一样。

您可以在以下链接中找到问题:


12
2018-01-28 19:13



如上所述 jira.mongodb.org/browse/CSHARP-485 罗伯特·斯塔姆评论说,当我尝试使用接口类型插入时,我得到所有0的BSONID,所有其他值和_t值按预期存储。任何解决方案? - Hardik
建议 https://jira.mongodb.org/browse/CSHARP-485?focusedCommentId=876090&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-876090 适合我 - DenisL


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

适合我


3
2017-11-12 00:10





我们是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;}

关于代码的一些注意事项 - 它真正做的就是懒惰地为相应的具体类型加载序列化程序,然后将所有序列化/反序列化调用传递给具有适当具体类型而不是接口的序列化。

它还会检查该类型实际上是否为预期类型,如果不是,则只查找该类型的默认序列化程序。


1
2018-05-19 10:19