简体   繁体   English

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

[英]MongoDB C# Driver - how to store _id as ObjectId but map to string Id property?

I'm having trouble getting my model to represent an entity's Id property as a string but have it auto-generated and represented internally by MongoDb as a native ObjectId .我在让我的模型将实体的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);
    }
}

For the line // map Id property here in the above code, I've tried numerous different ways of configuring the Id mapping and none have worked.对于上面代码中的// map Id property here行,我尝试了多种不同的方式来配置 Id 映射,但都没有奏效。 The ways I have tried, and the associated exceptions that are thrown when I call the Save method, are:我尝试过的方法以及调用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);

What am I doing wrong?我究竟做错了什么? I thought this was a standard use case for id handling?我认为这是 id 处理的标准用例?

This has changed, I'm using the latest 1.x driver (Nuget package <package id="mongocsharpdriver" version="2.0.0" targetFramework="net45" /> ) and instead of using SetRepresentation you set the serialiser.这已经改变了,我使用的是最新的 1.x 驱动程序(Nuget package <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));
});

Found the answer:找到答案:

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

This allows me to save as a native ObjectId and still have the Id represented in C# as a string.这允许我保存为本机 ObjectId 并且仍然将 Id 在 C# 中表示为字符串。 As a small gotcha, the id must be parsed before being queried upon:作为一个小问题,在查询 id 之前必须先解析它:

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

Edit May 2015 2015 年 5 月编辑
Apparently the driver has changed since I wrote this answer.显然,自从我写下这个答案以来,驱动程序已经改变了。 The other answer above is correct for newer versions, but this answer can still be referred to if using a legacy version of the driver.上面的另一个答案对于较新版本是正确的,但如果使用旧版本的驱动程序,仍然可以参考这个答案。

In case you want that very same kind of mapping across a whole range of entities without the need to repeat yourself over and over again you might want to use a convention:如果您希望在整个实体范围内进行完全相同的映射,而无需一遍又一遍地重复自己,您可能需要使用约定:

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));
        }
    }
}

...and then use it in lieu of all custom mappings: ...然后使用它代替所有自定义映射:

ConventionPack cp = new ConventionPack();
cp.Add(new StringObjectIdIdGeneratorConventionThatWorks());

ConventionRegistry.Register("TreatAllStringIdsProperly", cp, _ => true);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM