首页 > 解决方案 > 在 .Net Core 3.1 中使用 MongoDB API 在 Azure Cosmos DB 中出现重复密钥问题

问题描述

前言

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

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

简单代码示例

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

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

我还设计了一个具体的类来实现这个接口。

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

尝试数据注释的示例

我已经尝试在上面的接口和类中为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; }
}

当我让我的集合对象插入模型时,这些组合似乎都没有告诉 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的实现。

标签: .netazureazure-cosmosdb.net-core-3.1azure-cosmosdb-mongoapi

解决方案


经过进一步的开发和研究

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

序列化 Mongo ObjectId 第 3 部分 ZOXEXIVO 的答案时出现 JSON.NET 转换错误

使用带有[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; }
}

存储库

我还更新了我的存储库以通过接口 IExample 使用具体类 Example。这允许我在我的 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;
    }
}

概括

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


推荐阅读