代码之家  ›  专栏  ›  技术社区  ›  Tim Pohlmann

将数组插入MongoDB中的子集合会忽略_t鉴别器

  •  3
  • Tim Pohlmann  · 技术社区  · 6 年前

    考虑到这些类别:

    public class Parent
    {
        public IEnumerable<IChild> Children { get; set; }
    }
    
    public interface IChild { }
    
    public class Child : IChild { }
    

    将Children属性作为数组插入,如下所示:

    using MongoDB.Driver;
    using System.Collections.Generic;
    
    namespace TestConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                var db = new MongoClient().GetDatabase("Test");
                var collection = db.GetCollection<Parent>("Parent");
                collection.InsertOne(new Parent { Children = new[] { new Child() } });
            }
        }
    }
    

    数据库中缺少_t鉴别器:

    {
        "_id":"5bf6aef6c0beccc414b70d45",
        "Child":[{}]
    }
    

    如果我使用列表:

    collection.InsertOne(new Parent { Children = new List<IChild> { new Child() } });
    

    鉴别器设置正确:

    {
        "_id":"5bf6b074c0beccc414b70dc2",
        "Children":[{"_t":"Child"}]
    }
    

    这似乎是一个bug,或者至少是一个非常不直观的行为。

    其他信息: 该行为是一个问题,因为缺少_tdiscriminator会在反序列化对象时导致异常:

    系统FormatException:“反序列化TestConsoleApp类的Children属性时出错。父级:无法为接口类型TestConsoleApp确定要反序列化的对象的实际类型。“伊希尔德。”

    2 回复  |  直到 6 年前
        1
  •  3
  •   Vadim Alekseevsky    6 年前

    问题的原因是mongo没有发现IChild接口的实现类。换句话说,mongo驱动程序不知道必须使用子类创建IChild实现。这就是为什么它增加了鉴别器。

    要解决这个问题,可以指定隐含序列化。

    public class Parent
    {
    [BsonSerializer(typeof(ImpliedImplementationInterfaceSerializer<IEnumerable<IChild>, IEnumerable<Child>>))]
        public IEnumerable<IChild> Children { get; set; }
    }
    

    有了这个属性,它不会产生歧视,但会使用子类进行反序列化。

    如果您想在动态实例上强制使用鉴别器,可以使用

    [BsonDiscriminator(Required = true)]
        public class Child : IChild { }
    

    请注意,此标题应添加到所有需要强制创建鉴别器的类中。

        2
  •  1
  •   yonBav    6 年前

    在我看来,正如你所说,这是一个bug,实际上考虑到你提到的例外情况,它可能是一个bug。 无论如何,这是该项目的GitHub存储库 https://github.com/mongodb/mongo-csharp-driver .

    在自述中。md您可以找到有关如何报告错误的说明(如果您不打算报告错误,请告诉我,我会这样做)。

    同时,我认为最好的解决办法是更换 IEnumerable<IChild> 具有 IList<IChild> 防止其他程序员以错误的方式插入数据。

    编辑:请检查IList是否解决了问题,因为这一行正在编译(至少对我来说)

        public static IList<int> Ints { get; set; }
    
        static void Main(string[] args)
        {
            Ints = new[] {1,2,3,4};
            Console.WriteLine("Hello World!");
        }
    

    如果它不能解决你的问题,我会用它 List<IChild> .虽然不好看,但会管用的。