首页 > 解决方案 > 使用成员编号保存文档,而不是使用 protobuf-net 和 MongoDB 的名称

问题描述

我在某处看到使用 Go MongoDB 驱动程序可以使用订单号而不是字段名称保存文档。
他们最终在数据库中得到了这个:

{
   "3": "foo",
   "10": 1,
   "33": 123456
   "107": {
    "2": "bar",
    "1": "foo"
   }
}

我喜欢这个主意!因此,我试图找到一种对 MongoDB C# 驱动程序执行相同操作的方法。
我有下面的代码,但我不确定我应该从 protobut-net 带来什么来获取会员订单号。

var pack = new ConventionPack();
pack.AddMemberMapConvention("numbered", m => m.SetElementName( WHAT TO PUT HERE ));
ConventionRegistry.Register("numbered", pack, type => true);       

SetElementName接受一个字符串参数。
如何从 protobuf-net 中获取成员的订单号?
...Member.Order.ToString()
我不知道这整件事是否是个好主意,但我想测试一下。

谢谢

- 更新 -

只是为了添加更多信息。我正在为我的模型使用继承来使用泛型。

[BsonDiscriminator("Base", RootClass = true)]
[DataContract]
public abstract class Base
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    [ProtoMember(1)]
    public string Id { get; set; }
    
    [BsonDateTimeOptions]
    [ProtoMember(2)]
    public DateTime CreatedDate { get; private set; } = DateTime.UtcNow;

    [BsonDateTimeOptions]
    [ProtoMember(3)]
    public DateTime UpdatedDate { get; set; } = DateTime.UtcNow;
}       
        
[ProtoContract]
public class Todo : Base
{
    [ProtoMember(10)]
    public string Title { get; set; }
    [ProtoMember(20)]
    public string Content { get; set; }
    [ProtoMember(30)]
    public string Category { get; set; }
}      
 

我添加了这一行,如 protobuf-net 文档中所示:

RuntimeTypeModel.Default[typeof(Base)].AddSubType(42, typeof(Todo));

因此,有了这个以及 Marc 显示的获取成员编号的内容,我最终在 MongoDB 中拥有了一个自定义约定类,<T>因此我可以将它用于其他对象:

public class NumberedElementNameConvention<T> : ConventionBase, IMemberMapConvention where T : Base
{
    public void Apply(BsonMemberMap memberMap) 
    {
        var members = RuntimeTypeModel.Default[typeof(T)].GetFields();
        foreach (var member in members)
        {
            memberMap.SetElementName(member.FieldNumber.ToString());
        }
    }
}          

该公约的注册是这样完成的:

var pack = new ConventionPack { new NumberedElementNameConvention<Todo>() };
ConventionRegistry.Register("NumberedName", pack, type => true);

运行后我得到这个错误:

Grpc.AspNetCore.Server.ServerCallHandler[6] 执行服务方法“CreateOne”时出错。MongoDB.Bson.BsonSerializationException:类型为“Nnet.Models.Base”的属性“UpdatedDate”不能使用元素名称“30”,因为它已被属性“CreatedDate”使用...

另外,当我运行下面的代码时,我希望得到 Todo 对象的所有成员。

var members = RuntimeTypeModel.Default[typeof(Todo)].GetFields();
foreach (var member in members)
{
   Console.WriteLine($"{member.FieldNumber}: {member.Member.Name}");
}       

但是,我没有得到从 Base 对象继承的那些:

❯ dotnet run
10:标题
20:内容
30:类别

标签: mongodbmongodb-.net-driverprotobuf-netprotobuf-net.grpc

解决方案


protobuf-net 的字段元数据可从RuntimeTypeModelAPI 获得,例如:

var members = RuntimeTypeModel.Default[yourType].GetFields();
foreach (var member in members)
{
    Console.WriteLine($"{member.FieldNumber}: {member.Member.Name}");
}

.FieldNumber给出 protobuf 字段编号,并.Member给出MemberInfo相应字段或属性的。m => m.SetElementName( WHAT TO PUT HERE )如果对同一个值进行多次评估,您可能希望进行某种级别的缓存m,这样您就不会执行不必​​要的工作 - 但是:在您这样做之前,只需先向 lambda 添加一些日志记录,然后查看它获得的频率叫:如果不是太频繁,也许不用担心。

请注意,还有一个查找MetaType允许通过以下方式进行查询MemberInfo

var member = RuntimeTypeModel.Default[yourType][memberInfo];

推荐阅读