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

问题描述 投票:9回答:3

我已经为我的集合实现了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
c# mongodb mongodb-.net-driver
3个回答
5
投票

当您使用属性[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;但我会先尝试 - 只是为了确保。


3
投票

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

发生的事情是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);
}

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


0
投票

看看这个解决方案。对我来说它有效。为每个基类设置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
{
    ...
}
© www.soinside.com 2019 - 2024. All rights reserved.