我正在尝试使用官方C#驱动程序存储从mongodb中的非泛型基类继承的泛型类的对象列表。
我的代码如下所示:
abstract class MyAbstractClass {}
class MyGenericClass<T>: MyAbstractClass
{
[BsonRequired]
public List<T> Values = new List<T>();
public MyGenericClass(IEnumerable<T> values)
{
Values.AddRange(values);
}
}
class MyContainerClass
{
[BsonId]
public string Id;
[BsonRequired]
public List<MyAbstractClass> ValueCollections = new List<MyAbstractClass>();
public MyContainerClass()
{
Id = Guid.NewGuid().ToString();
}
}
在测试时,我创建了一个容器对象并用泛型类的实例填充它,如下所示:
var container = new MyContainerClass();
container.ValueCollections.Add(new MyGenericClass<string>(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.为什么不正确地序列化鉴别器?
感谢您的时间。
答案 0 :(得分:8)
经过一些实验,我发现你可以编写自己的鉴别器约定。我无法理解为什么,但默认的鉴别器约定似乎使用类型类的Name属性,而不是FullName,这使得泛型类无用。
我最终使用了这段代码:
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());
我还必须使基类非抽象,以避免&#34;不能克隆抽象类的实例&#34;错误。能够拥有一个抽象基类会很好,但由于派生类是通用的,我不能使用BsonKnownTypes。
答案 1 :(得分:3)
也许它有点晚了,但我们遇到了与泛型类型相同的问题,它们被保存为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<Type> 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);
}
);
答案 2 :(得分:1)
上面的内容对我们来说只有一些小改动。非常感谢你的帮助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<IRestriction>))
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<IRestriction>))
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<T> : IRestriction
{
public T Value { get; set; }
public BasicRestriction()
{
}
public BasicRestriction(T value)
{
Value = value;
}
}
包含Irestrictions列表的类永远不会被反序列化,因为当它到达I限制列表时,每个限制符都有一个_t判别符值“BasicRestriction`1”:
public class Carton{ public List<IRestriction> Restrictions { get; set; }}