.NET 6 WebApi用CosmosRepository调用CreateAsync报400(1001)错误求助
问题场景
在基于.NET 6的WebApi应用中,使用7.0.1版本的Microsoft.Azure.CosmosRepository库调用CreateAsync方法向Azure Cosmos DB添加EmailTemplate项时,收到BadRequest (400)响应,子状态码为1001。当前GetAsync方法可正常查询已有数据。
错误日志
Cosmos query executed for item type EmailTemplate with a charge of 2.8
RUs Query: {"query":"SELECT VALUE root FROM root WHERE
(((root["NotificationType"] = 3) AND (root["NotificationSubType"]
= "")) AND (root["Language"] = "en-US")) AND ((NOT IS_DEFINED(root["Type"])) OR (root["Type"] =
"EmailTemplate")))} fail:
SFH.N3.WebApi.Controllers.EmailTemplates.EmailTemplateController[0]
An error occurred while updating email template, RequestId:338bcaa9-f8a3-418b-932b-4d88a7ab6a08
Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: BadRequest (400); Substatus: 1001;
ActivityId: 5242793b-f785-436d-9565-5d7c1e730ef4; Reason: ();
at Microsoft.Azure.Cosmos.ResponseMessage.EnsureSuccessStatusCode()
at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ProcessMessage[T](ResponseMessage
responseMessage, Func2 createResponse) at Microsoft.Azure.Cosmos.ContainerCore.CreateItemAsync[T](T item, ITrace trace, Nullable1 partitionKey, ItemRequestOptions
requestOptions, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ClientContextCore.RunWithDiagnosticsHelperAsync[TResult](String
containerName, String databaseName, OperationType operationType,
ITrace trace, Func2 task, Func2 openTelemetry, String operationName,
RequestOptions requestOptions)
at Microsoft.Azure.Cosmos.ClientContextCore.OperationHelperWithRootTraceAsync[TResult](String
operationName, String containerName, String databaseName,
OperationType operationType, RequestOptions requestOptions, Func2 task, Func2 openTelemetry, TraceComponent traceComponent, TraceLevel
traceLevel)
at Microsoft.Azure.CosmosRepository.DefaultRepository`1.CreateAsync(TItem
value, CancellationToken cancellationToken)
相关代码
添加邮件模板方法
private async Task AddEmailTemplate(AddEmailTemplateCommand command) { var emailTemplateExist = await _repository .GetAsync(x => x.NotificationType == command.NotificationType && x.NotificationSubType == command.NotificationSubType && x.Language == command.Language).FirstOrDefaultAsync(); // Insert email template if (emailTemplateExist == null) { var emailTemplate = new EmailTemplate { Id = Guid.NewGuid().ToString(), NotificationType = command.NotificationType, NotificationSubType = command.NotificationSubType, EmailSubject = command.EmailSubject, Language = command.Language, TemplateId = command.TemplateId, }; await _repository.CreateAsync(emailTemplate); } else { string message = $"Email template already exists for the specified notification type:{command.NotificationType}"; _logger.LogWarning(message); throw new EmailTemplateAlreadyExistsException(message); } }
EmailTemplate类定义
[Container(nameof(EmailTemplate))] [PartitionKeyPath("/NotificationType")] public class EmailTemplate : Item { [JsonProperty(PropertyName = "NotificationType")] public int NotificationType { get; set; } [JsonProperty(PropertyName = "NotificationSubType")] public string NotificationSubType { get; set; } [JsonProperty(PropertyName = "Language")] public string Language { get; set; } [JsonProperty(PropertyName = "TemplateId")] public string TemplateId { get; set; } [JsonProperty(PropertyName = "EmailSubject")] public string EmailSubject { get; set; } protected override string GetPartitionKeyValue() => NotificationType.ToString(); [JsonProperty(PropertyName = "_ts")] public long Timestamp { get; set; } [JsonIgnore] public DateTime TimestampAsDateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp).DateTime; }
配置文件(appsettings.json)
"RepositoryOptions": { "CosmosConnectionString": "AccountEndpoint={CosmosDbConnectionString}", "DatabaseId": "TestDb", "OptimizeBandwidth": true, "ContainerPerItemType": true, "AllowBulkExecution": true, "SerializationOptions": { "IgnoreNullValues": true, "PropertyNamingPolicy": "Default" } }
Cosmos DB容器配置

子状态码1001含义
Azure Cosmos DB的子状态码1001表示请求传递的分区键值与容器定义的分区键数据类型不匹配。例如容器分区键定义为数字类型,但请求传入的是字符串类型,就会触发该错误。
排查与解决
问题根源
从容器配置图可以看到,/NotificationType的分区键类型为Number,但在EmailTemplate类中重写了GetPartitionKeyValue方法,返回NotificationType.ToString()(字符串类型)。Microsoft.Azure.CosmosRepository库在执行CreateAsync时,会使用该方法的返回值作为分区键值传递给Cosmos DB,导致分区键值类型(字符串)与容器定义的类型(数字)不匹配,触发400-1001错误。
解决方案
删除EmailTemplate类中重写的GetPartitionKeyValue方法即可:
修改后的EmailTemplate类:
[Container(nameof(EmailTemplate))] [PartitionKeyPath("/NotificationType")] public class EmailTemplate : Item { [JsonProperty(PropertyName = "NotificationType")] public int NotificationType { get; set; } [JsonProperty(PropertyName = "NotificationSubType")] public string NotificationSubType { get; set; } [JsonProperty(PropertyName = "Language")] public string Language { get; set; } [JsonProperty(PropertyName = "TemplateId")] public string TemplateId { get; set; } [JsonProperty(PropertyName = "EmailSubject")] public string EmailSubject { get; set; } [JsonProperty(PropertyName = "_ts")] public long Timestamp { get; set; } [JsonIgnore] public DateTime TimestampAsDateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp).DateTime; }
原理说明
[PartitionKeyPath("/NotificationType")]特性已经指定了分区键的路径,Microsoft.Azure.CosmosRepository库会自动读取NotificationType属性的int值作为分区键值传递给Cosmos DB,该类型与容器定义的Number类型完全匹配,即可避免1001错误。
内容的提问来源于stack exchange,提问作者Rakesh Kumar

