在 .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;
}
}

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

  • 我想要让 Cosmos DB 生成我的密钥是错误的吗?
  • 我是否应该继续显式创建密钥,而不是希望 CosmosDB 为我生成密钥?

我得到的错误是E11000没有显式创建对象ID。

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

经过进一步的开发和研究

在对解决方案进行了更多工作,并使我的API适用于POST,PATCH和GET之后,我注意到使用ObjectId属性很困难。 我还发现了一个帖子,指出建议在属性的 ObjectId类型上使用字符串。

序列化 Mongo 对象 ID 时 JSON.NET 转换错误 第三部分 佐克西沃的回答

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

示例代码

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

存储 库

我还更新了我的存储库以使用具体的类,示例,通过接口,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;
}
}

总结

总的来说,解决与我的方法相关的问题的结果使我学会了在我的类中使用字符串 Id 属性。 将字符串Id 属性与BsonId 和 BsonRepresentation(BsonType.ObjectId(数据批注结合使用,我的 CosmosDb 可以在帖子中为我的解决方案生成唯一键。 对于将来的数据模型,我会向其他开发人员推荐此解决方案,因为它还可以清理在GET上返回这些数据模型。

最新更新