MongoDB C#驱动程序 - 更改继承类的Id序列化

zor*_*pro 9 c# mongodb mongodb-.net-driver

我已经为我的集合实现了Repository模式和基本实体类.到现在为止所有集合了_idObjectId类型.在代码中,我需要将其表示Id为字符串.

这是EntityBase班级的样子

public abstract class EntityBase
{
    [BsonRepresentation(BsonType.ObjectId)]
    public virtual string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是映射:

BsonClassMap.RegisterClassMap<EntityBase>(cm =>
{
    cm.AutoMap();
    cm.MapIdProperty(x => x.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
    cm.IdMemberMap.SetSerializer(new StringSerializer().WithRepresentation(BsonType.ObjectId));
});
Run Code Online (Sandbox Code Playgroud)

现在我有一个语言集合,其中Id将是简单的字符串en-GB.

{
   "_id" : "en-GB",
   "Term1" : "Translation 1",
   "Term2" : "Translation 2"
}
Run Code Online (Sandbox Code Playgroud)

Languageclass继承了这个EntityBase

public class Language : EntityBase
{
    [BsonExtraElements]
    public IDictionary<string, object> Terms { get; set; }
    public override string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

问题是我可以以某种方式改变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));
});
Run Code Online (Sandbox Code Playgroud)

以下是我得到的例外情况:

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
Run Code Online (Sandbox Code Playgroud)

Fed*_*jdu 6

我的答案可能迟到了,但我遇到了同样的问题,找到了这个问题,但没有真正的答案如何解决以及为什么会发生。

发生的事情是 Mongo 驱动程序没有明确理解多态性,您必须手动提及每种类型。从同一类继承的所有类型都必须列出并解释为BsonClassMap.

在您的情况下,当您定义 EntityBase 时,您可以列出将被序列化的所有子项,它会使其正常工作。所以类似的东西:

[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(Language), typeof(OtherEntity))]
public abstract class EntityBase
{
    [BsonId]
    public virtual string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

应该管用。就我而言,我想要一个通用的实体保存方法,而我的实体不知道 Mongo 驱动程序,因此我无法使用属性。我在 EntityBase 中有我的 ID,所以这里有一个简单的通用 add 方法(只是相关但有效的部分):

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);
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我必须将基类映射到每个条目。然后对象被正确序列化并保存在数据库中。

  • 不幸的是,这迫使你在域层引用 MongoDB,这是不行的 (6认同)

die*_*aus 5

当您使用[BsonRepresentation(BsonType.ObjectId)]您告诉序列化程序的属性时,即使这是一个字符串 - 您希望它由ObjectId类型表示."en-GB"不是有效的ObjectId,序列化程序抛出异常.

[BsonId]如果要指定名称的属性Id必须是集合中的唯一标识符,请使用-attribute .

ObjectId驱动程序中还有一个-data类型,您可以实现它.

我会在你的EntityBase-class中添加一个泛型类型参数.

public abstract class EntityBase<TId>
{
    [BsonId]
    public TId Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您还可以实现EntityBase-class,而不使用泛型类型参数:

public abstract class EntityBase : EntityBase<ObjectId>
{
}
Run Code Online (Sandbox Code Playgroud)

然后为你的Language

public class Language : EntityBase<string>
{
    [BsonExtraElements]
    public IDictionary<string, object> Terms { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

驾驶员不应有序列化的一个字符串表示的任何麻烦ObjectIdObjectId; 但我会先尝试 - 只是为了确保.