在 .Net Core 3.1 中使用 MongoDB API 在 Azure Cosmos DB 中重复密钥问题

Duplicate key issue in Azure Cosmos DB using MongoDB API in .Net Core 3.1

前言

我已经尝试了几种看起来完全相同的解决方案。我在将新文档插入 Azure Cosmos DB 时遇到问题。 我得到的错误是一个重复的密钥错误。我想要完成的是让 Azure Cosmos DB 为我自己生成密钥,而不是我自己必须 在代码中显式创建密钥。

我对在我的 .Net 逻辑中生成密钥的主要担心是我的 .Net 应用程序创建重复密钥的机会,因为它有时会短暂关闭,这可能会重置其内部服务器时钟。我不相信这会发生在 Azure 中,所以我希望通过让它在发生插入时继续生成密钥来利用这一点。

简单代码示例

我正在使用接口模型将我的对象存储在 Azure 数据库中。

interface IExample
{
    ObjectId Id { get; set; }
}

我也有一个具体的 class 设计来实现这个接口。

public class Example : IExample
{
    public ObjectId Id { get; set; }
}

带有尝试数据注释的示例

我已经尝试在界面和上面的 class 中为 Id 字段使用以下属性及其组合。

interface IExample
{
    [BsonId]
    ObjectId Id { get; set; }
}
public class Example : IExample
{
    [BsonId]
    public ObjectId Id { get; set; }
}

interface IExample
{
    [DatabaseGenerated(DatabaseGeneratedOption.Calculated)]
    ObjectId Id { get; set; }
}
public class Example : IExample
{
    [DatabaseGenerated(DatabaseGeneratedOption.Calculated)]
    public ObjectId Id { get; set; }
}

interface IExample
{
    [BsonId, DatabaseGenerated(DatabaseGeneratedOption.Calculated)]
    ObjectId Id { get; set; }
}
public class Example : IExample
{
    [BsonId, DatabaseGenerated(DatabaseGeneratedOption.Calculated)]
    public ObjectId Id { get; set; }
}

None 这些组合似乎在告诉 CosmosDB,当我让我的集合对象插入模型时,Id 应该由它自己生成。

存储库示例

当我将文档插入我的存储库文件中的集合时,当前正在使用以下内容。

private ICosmosExampleDBContext _dbContext { get; set; }
private readonly IMongoCollection<IExample> _collection;
public ExampleRepository(ICosmosExampleDBContext dbContext, IOptions<ExampleOptions> options)
{
    this._dbContext = dbContext;
    this._collection = this._dbContext.GetCollection<IExample>(options.Value.Collection);
}
public void CreateExample(IExample example)
{
    try
    {
        // A duplicate key error is created if I don't explicitly create the key here.
        // E11000 is encountered without this, even with above data annotations.
        example.Id = MongoDB.Bson.ObjectId.GenerateNewId();
        this._collection.InsertOne(example);
    }
    catch(Exception e)
    {
        throw e;
    }
}

我想我对这个总体问题的后续问题是:

我收到的错误是 E11000,没有明确创建对象 ID。

编辑:更新了尝试数据注释的代码,以显示 IExample.

的实现

经过进一步开发和研究

在进一步研究解决方案并让我的 API 适用于 POST、PATCH 和 GET 之后,我注意到使用 ObjectId[=37= 很困难] 属性。我还发现 post 指出建议对 属性 使用 ObjectId 类型的字符串。

JSON.NET cast error when serializing Mongo ObjectId 第三部分ZOXEXIVO的回答

使用带有 [BsonId, BsonRepresentation(BsonType.ObjectId)] 数据注释的字符串 Id 字段允许 CosmosDB 在输入时自行生成密钥。我更新了我的数据模型以将以下内容用于其 Id 属性.

示例代码

public interface IExample
{
    public string Id { get; set; }
}
public class Example : IExample
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
}

存储库

我还更新了我的存储库以使用具体的 class,Example,通过接口,IExample。这允许我在 Startup.cs.

中将我的数据类型注册为 Bson 文档
private ICosmosExampleDBContext _dbContext { get; set; }
private readonly IMongoCollection<Example> _collection;
public ExampleRepository(ICosmosExampleDBContext dbContext, IOptions<ExampleOptions> options)
{
    this._dbContext = dbContext;
    this._collection = this._dbContext.GetCollection<Example>(options.Value.Collection);
}
public void CreateExample(Example example)
{
    try
    {
        // Since Id is now a string, when it is empty, it gets populated for me.
        // example.Id = MongoDB.Bson.ObjectId.GenerateNewId();
        this._collection.InsertOne(example);
    }
    catch(Exception e)
    {
        throw e;
    }
}

总结

总的来说,解决与我的方法相关的问题的结果让我了解了如何在我的 Class 中使用字符串 ID 属性。使用 string Id 属性 与 BsonIdBsonRepresentation(BsonType.ObjectId) 数据注释允许我的 CosmosDb 为我在 posts 上的解决方案生成唯一键。对于未来的数据模型,我会向其他开发人员推荐这个解决方案,因为它还清理了在 GET 上返回这些数据模型。