MongoDB C#驱动程序类型鉴别器,generics类inheritance自非generics基类

我正在尝试使用官方C#驱动程序存储从mongodb中的非generics基类inheritance的generics类的对象列表。

我的代码如下所示:

abstract class MyAbstractClass {} class MyGenericClass: MyAbstractClass { [BsonRequired] public List Values = new List(); public MyGenericClass(IEnumerable values) { Values.AddRange(values); } } class MyContainerClass { [BsonId] public string Id; [BsonRequired] public List ValueCollections = new List(); public MyContainerClass() { Id = Guid.NewGuid().ToString(); } } 

在测试时,我创建了一个容器对象并用generics类的实例填充它,如下所示:

 var container = new MyContainerClass(); container.ValueCollections.Add(new MyGenericClass(new[]{"foo","bar","baz"})); 

当我将其保存到数据库时,添加的文档如下所示:

 { "_id": "c5cf5cd1-843f-4d5d-ba8f-5859ae62fd1d", "ValueCollections": [ { "_t": "MyGenericClass`1", "Values": [ "foo", "bar", "baz" ] } ] } 

类型鉴别器获取类型“MyGenericClass’1”而不是“MyGenericClass’1 [System.String]”,这意味着它不能再次反序列化。

此外,当尝试从DB加载这些对象时,我收到一条错误消息:无法创建抽象类的实例。 但是类型鉴别器(如果它是正确的)应该允许驱动程序看到它不应该创建MyAbstractClass类型的对象但是MyGenericClass

所以我的问题是:1。为什么我会收到此错误? 2.为什么不正确地序列化鉴别器?

感谢您的时间。

经过一些实验,我发现你可以编写自己的鉴别器约定。 我无法理解为什么,但默认的鉴别器约定似乎使用类型类的Name属性,而不是FullName,这使得它对generics类没用。

我最终使用此代码:

 class FooDiscriminatorConvention : IDiscriminatorConvention { public string ElementName { get { return "_t"; } } public Type GetActualType(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType) { if(nominalType!=typeof(MyAbstractClass)) throw new Exception("Cannot use FooDiscriminator for type " + nominalType); var ret = nominalType; var bookmark = bsonReader.GetBookmark(); bsonReader.ReadStartDocument(); if (bsonReader.FindElement(ElementName)) { var value = bsonReader.ReadString(); ret = Type.GetType(value); if(ret==null) throw new Exception("Could not find type " + value); if(!ret.IsSubclassOf(typeof(MyAbstractClass))) throw new Exception("Database type does not inherit from MyAbstractClass."); } bsonReader.ReturnToBookmark(bookmark); return ret; } public BsonValue GetDiscriminator(Type nominalType, Type actualType) { if (nominalType != typeof(MyAbstractClass)) throw new Exception("Cannot use FooDiscriminator for type " + nominalType); return actualType.FullName; } } 

并注册

 BsonSerializer.RegisterDiscriminatorConvention(typeof(MyGenericClass<>), new FooDiscriminatorConvention()); //is this needed? BsonSerializer.RegisterDiscriminatorConvention(typeof(MyAbstractClass), new FooDiscriminatorConvention()); 

我还必须使基类非抽象,以避免“无法克里特实例的抽象类”错误。 能够拥有一个抽象基类会很好,但由于派生类是通用的,我不能使用BsonKnownTypes。

也许它有点晚了,但是我们遇到了与generics类型相同的问题,它们被保存为Name类型而不是FullName。 我们最初使用BsonSerializer.RegisterDiscriminator为我们项目中的所有类型,甚至使所有类型都通用,但经过大量的测试和失败后,我们最终得到了BsonClassMap

非工作方法……

  KnownPayloadTypes .Concat(KnownMessageTypes) .ConcatOne(typeof(object)) .ConcatOne(typeof(Message)) .ForEach(t => BsonSerializer.RegisterDiscriminator(t, t.FullName)); 

工作…

  private static readonly IEnumerable KnownMessageTypes = KnownPayloadTypes .Select(t => typeof(Message<>).MakeGenericType(t)); KnownPayloadTypes .Concat(KnownMessageTypes) .ConcatOne(typeof(object)) .ConcatOne(typeof(Message)) .ForEach(t => { var bsonClassMap = new BsonClassMap(t); bsonClassMap.AutoMap(); bsonClassMap.SetDiscriminator(t.FullName); BsonClassMap.RegisterClassMap(bsonClassMap); } ); 

上面的工作对我们来说有一些小改动。 非常感谢你的帮助DukeOf1Cat! 并感谢杰夫拉斯穆森帮助我们解决这个问题! 我们在尝试创建自定义序列化程序,不同的RegisterClassMap lambda等之后终于找到了这个。

我们对上述解决方案的更改就在这里。

 class IRestrictionDiscriminatorConvention : IDiscriminatorConvention { public string ElementName { get { return "_t"; } } public Type GetActualType(BsonReader bsonReader, Type nominalType) { //Edit: added additional check for list if (nominalType != typeof(IRestriction) && nominalType != typeof(List)) throw new Exception("Cannot use IRestrictionDiscriminatorConvention for type " + nominalType); var ret = nominalType; var bookmark = bsonReader.GetBookmark(); bsonReader.ReadStartDocument(); if (bsonReader.FindElement(ElementName)) { var value = bsonReader.ReadString(); ret = Type.GetType(value); if (ret == null) throw new Exception("Could not find type from " + value); //Edit: doing the checking a little different if (!typeof(IRestriction).IsAssignableFrom(ret) && !ret.IsSubclassOf(typeof(IRestriction))) throw new Exception("type is not an IRestriction"); } bsonReader.ReturnToBookmark(bookmark); return ret; } public BsonValue GetDiscriminator(Type nominalType, Type actualType) { if (nominalType != typeof(IRestriction) && nominalType != typeof(List)) throw new Exception("Cannot use FooDiscriminator for type " + nominalType); /*Edit: had to change this because we were getting Unknown discriminator value 'PackNet.Common.Interfaces.RescrictionsAndCapabilities.BasicRestriction`1[[System.String, ... */ return actualType.AssemblyQualifiedName; } } 

我们遇到问题的课程是这样的:

 public class BasicRestriction : IRestriction { public T Value { get; set; } public BasicRestriction() { } public BasicRestriction(T value) { Value = value; } } 

并且包含Irestrictions列表的类永远不能被反序列化,因为当它到达I限制列表时,每个都具有generics类型的_t discriminator值“BasicRestriction`1”:

 public class Carton{ public List Restrictions { get; set; }}