我已经为我的集合实现了Repository模式和基本实体类。直到现在所有收藏品都有_id
类型的ObjectId
。在代码中,我需要将Id
表示为字符串。
以下是EntityBase
类的外观
public abstract class EntityBase
{
[BsonRepresentation(BsonType.ObjectId)]
public virtual string Id { get; set; }
}
这是映射:
BsonClassMap.RegisterClassMap<EntityBase>(cm =>
{
cm.AutoMap();
cm.MapIdProperty(x => x.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
cm.IdMemberMap.SetSerializer(new StringSerializer().WithRepresentation(BsonType.ObjectId));
});
现在我有一个语言集合,其中Id将是简单的字符串,如en-GB
。
{
"_id" : "en-GB",
"Term1" : "Translation 1",
"Term2" : "Translation 2"
}
Language
类继承了EntityBase
类
public class Language : EntityBase
{
[BsonExtraElements]
public IDictionary<string, object> Terms { get; set; }
public override string Id { get; set; }
}
问题是我可以以某种方式改变Id
仅为Language
类序列化的方式吗?
我不想改变EntityBase
类的行为,因为我有很多继承EntityBase
的其他集合。
这是我尝试过的例外情况。不确定我尝试过的是否可行。
BsonClassMap.RegisterClassMap<Language>(cm =>
{
cm.AutoMap();
cm.MapExtraElementsMember(c => c.Terms);
cm.MapIdProperty(x => x.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
cm.IdMemberMap.SetSerializer(new StringSerializer().WithRepresentation(BsonType.String));
});
以下是我得到的例外情况:
An exception of type 'System.ArgumentOutOfRangeException' occurred in MongoDB.Bson.dll but was not handled in user code
Additional information: The memberInfo argument must be for class Language, but was for class EntityBase.
at MongoDB.Bson.Serialization.BsonClassMap.EnsureMemberInfoIsForThisClass(MemberInfo memberInfo)
at MongoDB.Bson.Serialization.BsonClassMap.MapMember(MemberInfo memberInfo)
at MongoDB.Bson.Serialization.BsonClassMap`1.MapMember[TMember](Expression`1 memberLambda)
at MongoDB.Bson.Serialization.BsonClassMap`1.MapProperty[TMember](Expression`1 propertyLambda)
at MongoDB.Bson.Serialization.BsonClassMap`1.MapIdProperty[TMember](Expression`1 propertyLambda)
at Test.Utilities.MongoDbClassConfig.<>c.<Configure>b__0_1(BsonClassMap`1 cm) in F:\Development\Test\Utilities\MongoDbClassConfig.cs:line 23
at MongoDB.Bson.Serialization.BsonClassMap`1..ctor(Action`1 classMapInitializer)
at MongoDB.Bson.Serialization.BsonClassMap.RegisterClassMap[TClass](Action`1 classMapInitializer)
at Test.Utilities.MongoDbClassConfig.Configure() in F:\Development\Test\Utilities\MongoDbClassConfig.cs:line 20
at Test.Portal.BackEnd.Startup..ctor(IHostingEnvironment env) in F:\Development\Test\Startup.cs:line 43
当您使用属性[BsonRepresentation(BsonType.ObjectId)]
时,您告诉序列化程序,即使这是一个字符串 - 您希望它由ObjectId
类型表示。 "en-GB"
不是一个有效的ObjectId
,序列化器抛出异常。
如果要指定名为[BsonId]
的属性必须是集合中的唯一标识符,请使用Id
-attribute。
驱动程序中还有一个ObjectId
数据类型,您可以实现它。
我会在你的EntityBase
类中添加一个泛型类型参数。
public abstract class EntityBase<TId>
{
[BsonId]
public TId Id { get; set; }
}
你也可以实现一个EntityBase
-class,不带泛型类型参数:
public abstract class EntityBase : EntityBase<ObjectId>
{
}
然后为你的Language
级
public class Language : EntityBase<string>
{
[BsonExtraElements]
public IDictionary<string, object> Terms { get; set; }
}
驱动程序不应该将ObjectId
的字符串表示序列化为ObjectId
;但我会先尝试 - 只是为了确保。
我可能会迟到一个答案,但我有同样的问题,找到了这个问题,但没有真正的答案如何解决,为什么会发生。
发生的事情是Mongo驱动程序没有明确地理解多态性,您必须手动提及每种类型。必须列出从同一个类继承的所有类型并向BsonClassMap
解释。
在您定义EntityBase的情况下,您可以列出将被序列化的所有子项,并且它将使其正常工作。所以有些东西:
[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(Language), typeof(OtherEntity))]
public abstract class EntityBase
{
[BsonId]
public virtual string Id { get; set; }
}
应该管用。在我的情况下,我想有一个通用的实体保存方法,我的实体不知道Mongo驱动程序,所以我不能使用属性。我在EntityBase中有我的ID,所以这里是一个简单的通用添加方法(只是相关的,但工作部分):
public async Task Add<T>(T item) where T : EntityBase, new()
{
BsonClassMap.RegisterClassMap<EntityBase>(cm =>
{
cm.AutoMap();
cm.MapIdMember(p => p.Id);
cm.SetIsRootClass(true);
});
BsonClassMap.RegisterClassMap<T>();
await Db.GetCollection<T>(typeof(T).Name).InsertOneAsync(item);
}
如您所见,我必须将基类映射到每个条目。然后将对象正确序列化并保存在数据库中。
看看这个解决方案。对我来说它有效。为每个基类设置BsonRepresentation
并忽略Id
中的SuperBaseEntity
字段,并为SuperBaseEntity
派生的每个ID类型基类创建
public class SuperBaseEntity
{
[BsonIgnore]
public string Id { get; set; }
}
public class ObjectIdBaseEntity : SuperBaseEntity
{
[BsonRepresentation(BsonType.ObjectId)]
public new string Id
{
get => base.Id;
set => base.Id = value;
}
}
public class StringIdBaseEntity : SuperBaseEntity
{
[BsonRepresentation(BsonType.String)]
public new string Id
{
get => base.Id;
set => base.Id = value;
}
}
public class Language : StringIdBaseEntity
{
[BsonExtraElements]
public IDictionary<string, object> Terms { get; set; }
}
public class AllOtherEntities : ObjectIdBaseEntity
{
...
}