【问题标题】:Persist derived objects using Mongo C# driver使用 Mongo C# 驱动程序持久化派生对象
【发布时间】:2011-06-12 03:57:06
【问题描述】:

我有以下类层次结构

[BsonKnownTypes(typeof(MoveCommand))]
public abstract class Command : ICommand
{
    public abstract string Name
    {
        get;
    }

    public abstract ICommandResult Execute();
}
    public class MoveCommand : Command
{
    public MoveCommand()
    {
        this.Id = ObjectId.GenerateNewId().ToString();
    }

    [BsonId]
    public string Id { get; set; }

    public override string Name
    {
        get { return "Move Command"; }
    }

    public override ICommandResult Execute()
    {
        return new CommandResult { Status = ExecutionStatus.InProgress };
    }
}

如果我像这样保存命​​令:

 Command c = new MoveCommand();
MongoDataBaseInstance.GetCollection<Command>("Commands").Save(c);

然后查询数据库,我没有看到派生属性持久化。

{ "_id" : "4df43312c4c2ac12a8f987e4", "_t" : "MoveCommand" }

我希望将 Name 属性作为文档中的键。 我做错了什么?

另外,有没有办法避免在基类上使用 BsonKnowTypes 属性来持久化派生实例?我不明白为什么基类需要了解派生类。这是糟糕的 OO 设计,并且被 BSON 库强加在我的类层次结构中。我在这里遗漏了什么吗?

【问题讨论】:

    标签: mongodb mongodb-.net-driver


    【解决方案1】:

    1.Name 属性未保存到数据库中,因为它还没有设置器。序列化器不会序列化没有设置器的属性(因为如果序列化器序列化此类属性,它将无法反序列化它)。所以如果你想序列化Name属性,那么只需添加假setter(到ICommand也需要添加它):

      public override string Name
      {
        get { return "Move Command"; }
        set{}
      }
    

    2.如果您不想使用 BsonKnownTypes 属性,还有另一种方法可以通知序列化程序在反序列化期间可能遇到的已知类型。只需在应用启动事件上注册一次地图:

    BsonClassMap.RegisterClassMap<MoveCommand>();
    //all other inherited from ICommand classes need register here also
    

    所以你应该为每个多态类使用或KnownTypes属性或注册BsonClassMap,否则在反序列化过程中会出现“未知描述符”错误:

      var commands = col.FindAllAs<ICommand>().ToList();
    

    3 你说:

    这是糟糕的 OO 设计,并且正在 强制我的类层次结构 BSON 库。

    即使没有KnownTypes,也可以通过BsonId 属性使用Bson lib 为您的代码添加属性。 如果你想避免它,你可以:

       BsonClassMap.RegisterClassMap<MoveCommand>(cm => {
            cm.AutoMap();
            cm.SetIdMember(cm.GetMemberMap(c => c.Id));
        });
    

    所以现在您可以从您的域代码库中删除对 Mongodb.Bson 库的引用。

    【讨论】:

    • 太棒了!谢谢一堆。如何注册 ID 生成器而不是使用 this.id = ObjectId.GenerateNewId().ToString();就像我在我的ctor里一样?有没有办法做到这一点?
    • 我回答了我自己的问题:cm.SetIdMember(cm.GetMemberMap(c => c.Id).SetIdGenerator (new StringObjectIdGenerator()));
    • @Andrew Orsich:我无法在界面上使用 FindAllAs。我尝试了 FindAllAs,如您的回答中所示,但出现以下错误:“没有为 ICommand 类型找到序列化程序”,如果我有一个基类而不是接口,似乎工作正常。有什么想法吗?
    • @AbhijeetPatel:我想您需要从数据库中删除旧数据并重试。一般来说,我尝试使用基类但没有使用接口。所以我稍后会尝试使用接口的findallas。
    • @Andrew:是的,我删除了所有旧数据并尝试过,但它不适用于接口,只能用于基类。我很想知道你的发现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-05
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    相关资源
    最近更新 更多