MongoDB c#驱动程序-如何将_id存储为ObjectId,但映射到字符串Id属性

本文关键字:ObjectId 映射 属性 Id 字符串 存储 驱动程序 id MongoDB | 更新日期: 2023-09-27 18:10:00

我有麻烦让我的模型来表示一个实体的Id属性作为一个字符串,但有它自动生成和MongoDb作为一个本地ObjectId内部表示。

class Account
{
    public string Id { get; set; }
    ...
}
class AccountStore
{
    static AccountStore()
    {
        BsonClassMap.RegisterClassMap<Account>(cm =>
        {
            cm.AutoMap();
            cm.SetIgnoreExtraElements(true);
            // map Id property here
        });
    }
    public void Save(Account account)
    {
        _accounts.Save(account);
    }
}

对于上面代码中的// map Id property here行,我尝试了许多不同的方法来配置Id映射,但都没有成功。我尝试过的方法以及在调用Save方法时抛出的相关异常是:

// Exception: No IdGenerator found.
cm.IdMemberMap
  .SetRepresentation(BsonType.ObjectId);
// Exception: No IdGenerator found.
cm.IdMemberMap
  .SetRepresentation(BsonType.String);
// Exception: Unable to cast object of type 'MongoDB.Bson.ObjectId' to type 'System.String'.
cm.IdMemberMap
  .SetRepresentation(BsonType.ObjectId)
  .SetIdGenerator(ObjectIdGenerator.Instance);
// Exception: Unable to cast object of type 'MongoDB.Bson.ObjectId' to type 'System.String'.
cm.IdMemberMap
  .SetRepresentation(BsonType.String)
  .SetIdGenerator(ObjectIdGenerator.Instance);
// Exception: Unable to cast object of type 'MongoDB.Bson.ObjectId' to type 'System.String'.
cm.IdMemberMap
  .SetIdGenerator(ObjectIdGenerator.Instance);

我做错了什么?我以为这是id处理的标准用例?

MongoDB c#驱动程序-如何将_id存储为ObjectId,但映射到字符串Id属性

这已经改变了,我使用的是最新的1。x驱动程序(Nuget包<package id="mongocsharpdriver" version="2.0.0" targetFramework="net45" />),而不是使用SetRepresentation设置序列化器。

public class RegistrationAttempt
{
    public string AttemptId { get; set; }
}
BsonClassMap.RegisterClassMap<RegistrationAttempt>(cm =>
{
    cm.AutoMap();
    cm.MapIdProperty(c => c.AttemptId)
        .SetIdGenerator(StringObjectIdGenerator.Instance)
        .SetSerializer(new StringSerializer(BsonType.ObjectId));
});

找到答案了:

cm.IdMemberMap
  .SetRepresentation(BsonType.ObjectId)
  .SetIdGenerator(StringObjectIdGenerator.Instance);

这允许我保存为原生ObjectId,并且仍然以字符串的形式在c#中表示Id。作为一个小问题,在查询id之前必须先解析id:

public Account GetAccountById(string id)
{
    return _accounts.FindOneById(ObjectId.Parse(id));
}

编辑2015年5月
显然,自从我写下这个答案后,司机已经换了。上面的另一个答案对于较新的版本是正确的,但是如果使用旧版本的驱动程序,仍然可以引用这个答案。

如果您希望在整个实体范围内进行相同类型的映射,而不需要一遍又一遍地重复您可能需要使用约定:

public class 
StringObjectIdIdGeneratorConventionThatWorks : 
ConventionBase, IPostProcessingConvention
{
    /// <summary>
    /// Applies a post processing modification to the class map.
    /// </summary>
    /// <param name="classMap">The class map.</param>
    public void PostProcess(BsonClassMap classMap)
    {
        var idMemberMap = classMap.IdMemberMap;
        if (idMemberMap == null || idMemberMap.IdGenerator != null)
            return;
        if (idMemberMap.MemberType == typeof(string))
        {
            idMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance).SetSerializer(new StringSerializer(BsonType.ObjectId));
        }
    }
}

…然后使用它来代替所有自定义映射:

ConventionPack cp = new ConventionPack();
cp.Add(new StringObjectIdIdGeneratorConventionThatWorks());
ConventionRegistry.Register("TreatAllStringIdsProperly", cp, _ => true);