将数组插入 MongoDB 中的子集合会忽略 _t 鉴别器
Inserting an array into a child collection in MongoDB is omitting the _t Discriminator
鉴于这些 类:
public class Parent
{
public IEnumerable<IChild> Children { get; set; }
}
public interface IChild { }
public class Child : IChild { }
像这样将子项 属性 作为数组插入:
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() } });
_t 鉴别器设置正确:
{
"_id":"5bf6b074c0beccc414b70dc2",
"Children":[{"_t":"Child"}]
}
这似乎是一个错误,或者至少是一个非常不直观的行为。
附加信息:
该行为是一个问题,因为缺少的 _t 鉴别器在反序列化对象时导致异常:
System.FormatException: 'An error occurred while deserializing the Children property of class TestConsoleApp.Parent: Unable to determine actual type of object to deserialize for interface type TestConsoleApp.IChild.'
在我看来,这就是你所说的一个错误,实际上考虑到你提到的异常可能是一个错误。
无论如何,这是项目 https://github.com/mongodb/mongo-csharp-driver 的 GitHub 存储库。
在 README.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>
。它不漂亮,但它会起作用。
问题是因为 mongo 发现的 IChild 接口没有实现 class。换句话说,mongo driver 不知道必须使用 Child class 创建 IChild 实现。这就是它添加 _t 鉴别器的原因。
要解决此问题,您可以指定隐式序列化。
public class Parent
{
[BsonSerializer(typeof(ImpliedImplementationInterfaceSerializer<IEnumerable<IChild>, IEnumerable<Child>>))]
public IEnumerable<IChild> Children { get; set; }
}
使用此属性,它不会创建 _t 歧视,但会使用 Child class 进行反序列化。
如果你想在动态实例上强制使用鉴别器,你可以使用
[BsonDiscriminator(Required = true)]
public class Child : IChild { }
请注意,应将此 header 添加到您需要强制创建鉴别器的所有 classes。
鉴于这些 类:
public class Parent
{
public IEnumerable<IChild> Children { get; set; }
}
public interface IChild { }
public class Child : IChild { }
像这样将子项 属性 作为数组插入:
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() } });
_t 鉴别器设置正确:
{
"_id":"5bf6b074c0beccc414b70dc2",
"Children":[{"_t":"Child"}]
}
这似乎是一个错误,或者至少是一个非常不直观的行为。
附加信息: 该行为是一个问题,因为缺少的 _t 鉴别器在反序列化对象时导致异常:
System.FormatException: 'An error occurred while deserializing the Children property of class TestConsoleApp.Parent: Unable to determine actual type of object to deserialize for interface type TestConsoleApp.IChild.'
在我看来,这就是你所说的一个错误,实际上考虑到你提到的异常可能是一个错误。 无论如何,这是项目 https://github.com/mongodb/mongo-csharp-driver 的 GitHub 存储库。
在 README.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>
。它不漂亮,但它会起作用。
问题是因为 mongo 发现的 IChild 接口没有实现 class。换句话说,mongo driver 不知道必须使用 Child class 创建 IChild 实现。这就是它添加 _t 鉴别器的原因。
要解决此问题,您可以指定隐式序列化。
public class Parent
{
[BsonSerializer(typeof(ImpliedImplementationInterfaceSerializer<IEnumerable<IChild>, IEnumerable<Child>>))]
public IEnumerable<IChild> Children { get; set; }
}
使用此属性,它不会创建 _t 歧视,但会使用 Child class 进行反序列化。
如果你想在动态实例上强制使用鉴别器,你可以使用
[BsonDiscriminator(Required = true)]
public class Child : IChild { }
请注意,应将此 header 添加到您需要强制创建鉴别器的所有 classes。