Почему Cosmos DB выдает ошибку «Имя входа» {«неверно» при вызове CreateItemAsync - PullRequest
1 голос
/ 06 ноября 2019

Я работаю над ASP.Net Core 3.0 API с базой данных Azure Cosmos в качестве хранилища сохраняемости. Это моя первая попытка работы с Cosmos DB. Когда я пытаюсь создать новый элемент (документ), я получаю сообщение об ошибке в Почтальоне, в котором говорится ...

"Response status code does not indicate success: 400 Substatus: 0 
Reason: (Message: {\"Errors\":[\"The input name '{' is invalid. 
Ensure to provide a unique non-empty string less than '1024' characters."

Я не могу понять, что является причиной этой проблемы.

Я использую в своем проекте nuget Microsoft.Azure.Cosmos v3.4.0

В моем репозитории есть метод для добавления нового документа учетной записи.

public async Task AddAccountAsync(Account account)
{
    await _container.CreateItemAsync(account, new PartitionKey(account.Id));
}

Вот изображениезначения свойств при наведении курсора на объект «Учетная запись» в режиме отладки.

enter image description here

Мой контейнер в базе данных Cosmos настроен с / id в качестве ключа раздела.

Вот мое тело запроса в Postman;

{
  "id": "00000000-0000-0000-0000-000000000000",
  "accountName": "Test Company 1",
  "accountType": 1,
  "ownerId": "00000000-0000-0000-0000-000000000000",
  "isTaxExempt": false,
  "mailJobProxyId": "00000000-0000-0000-0000-000000000000",
  "salesPersonId": "00000000-0000-0000-0000-000000000000"

}

Вот класс учетной записи;

public class Account
{

    // Aggregate state properties
    [JsonProperty(PropertyName = "id")]
    public AccountId Id { get; set; }

    [JsonProperty(PropertyName = "accountName")]
    public AccountName AccountName { get; set; }

    [JsonProperty(PropertyName = "accountType")]
    public AccountTypes AccountType { get; set; }

    [JsonProperty(PropertyName = "ownerId")]
    public OwnerId OwnerId { get; set; }

    [JsonProperty(PropertyName = "isTaxExempt")]
    public bool IsTaxExempt { get; set; }

    [JsonProperty(PropertyName = "mailJobProxyId")]
    public MailJobProxyId MailJobProxyId { get; set; }

    [JsonProperty(PropertyName = "salesPersonId")]
    public SalesPersonId SalesPersonId { get; set; }

    [JsonProperty(PropertyName = "addresses")]
    public List<Address.Address> Addresses { get; set; }

    [JsonProperty(PropertyName = "contacts")]
    public List<Contact.Contact> Contacts { get; set; }

    [JsonProperty(PropertyName = "postagePaymentMethods")]
    public List<PostagePaymentMethod.PostagePaymentMethod> PostagePaymentMethods { get; set; }

    public Account(string id, string accountName, AccountTypes accountType, string ownerId, Guid mailJobProxyId, Guid salesPersonId, bool isTaxExempt)
    {
        Id = AccountId.FromString(id);
        AccountName = AccountName.FromString(accountName);
        AccountType = accountType;
        OwnerId = OwnerId.FromString(ownerId);
        MailJobProxyId = new MailJobProxyId(mailJobProxyId);
        SalesPersonId = new SalesPersonId(salesPersonId);
        IsTaxExempt = isTaxExempt;
        Addresses = new List<Address.Address>();
        Contacts = new List<Contact.Contact>();
        PostagePaymentMethods = new List<PostagePaymentMethod.PostagePaymentMethod>();
        Status = Status.Active;

    }
}

Пожалуйста, дайте мне знать, если вам нужны другие примеры кода.

ОБНОВЛЕНИЕ 6/6/19 в 6: 43p EST

Вот объект значения AccountId

   public class AccountId : Value<AccountId>
    {
        public string Value { get; internal set; }

        // Parameterless constructor for serialization requirements
        protected AccountId() { }

        internal AccountId(string value) => Value = value;

        // Factory pattern
        public static AccountId FromString(string accountId)
        {
            CheckValidity(accountId);
            return new AccountId(accountId);
        }

        public static implicit operator string(AccountId accountId) => accountId.Value;

        private static void CheckValidity(string value)
        {
            if (!Guid.TryParse(value, out _))
            {
                throw new ArgumentException(nameof(value), "Account Id is not a GUID.");
            }
        }
    }

А вот класс инициализации в Startup.cs, который устанавливаетбаза данных и контейнер.

private static async Task<AccountsRepository> InitializeCosmosClientAccountInstanceAsync(IConfigurationSection configurationSection)
{
    var databaseName = configurationSection.GetSection("DatabaseName").Value;
    string uri = configurationSection.GetSection("Uri").Value;
    string key = configurationSection.GetSection("Key").Value;
    CosmosClientBuilder clientBuilder = new CosmosClientBuilder(uri, key);
    CosmosClient client = clientBuilder
                        .WithConnectionModeDirect()
                        .Build();
    DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(databaseName);

    string containerName = configurationSection.GetSection("AccountsContainerName").Value;
    await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
    AccountsRepository cosmosDbService = new AccountsRepository(client, databaseName, containerName);

    return cosmosDbService;
} 

Вот трассировка стека от момента возникновения ошибки;

stackTrace": "   at Microsoft.Azure.Cosmos.ResponseMessage.EnsureSuccessStatusCode()\r\n
at Microsoft.Azure.Cosmos.CosmosResponseFactory.ToObjectInternal[T]
(ResponseMessage cosmosResponseMessage, CosmosSerializer jsonSerializer)\r\n
at Microsoft.Azure.Cosmos.CosmosResponseFactory. 
<CreateItemResponseAsync>b__6_0[T](ResponseMessage cosmosResponseMessage)\r\n
at Microsoft.Azure.Cosmos.CosmosResponseFactory.ProcessMessageAsync[T]
(Task`1 cosmosResponseTask, Func`2 createResponse)\r\n   at
Delivery.Api.Infrastructure.AccountsRepository.AddAccountAsync(Account
account) in 
C:\\AzureDevOps\\Delivery\\Delivery.Api\\Accounts\\AccountsRepository.cs:line 20\r\n 
at Delivery.Api.Accounts.AccountsApplicationService.HandleCreate(Create cmd) 
in C:\\AzureDevOps\\Delivery\\Delivery.Api\\Accounts\\AccountsApplicationService.cs:line 43\r\n 
at Delivery.Api.Infrastructure.RequestHandler.HandleCommand[T](T request, Func`2 handler, ILogger log) 
in C:\\AzureDevOps\\Delivery\\Delivery.Api\\Infrastructure\\RequestHandler.cs:line 16

1 Ответ

2 голосов
/ 07 ноября 2019

Вам может потребоваться создать свой собственный конвертер для AccountId, OwnerId и так далее.

Вот мой тест:

class AccountIdConverter

    class AccountIdConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(AccountId));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return AccountId.FromString(JToken.Load(reader).ToString());
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            JToken.FromObject(value.ToString()).WriteTo(writer);
        }
    }

class AccountId

Добавить метод toString и установить для использования пользовательский конвертер

    [JsonConverter(typeof(AccountIdConverter))]
    public class AccountId
    {
        public string Value { get; internal set; }
        protected AccountId() { }

        internal AccountId(string value) => Value = value;

        public static AccountId FromString(string accountId)
        {
            CheckValidity(accountId);
            return new AccountId(accountId);
        }

        public static implicit operator string(AccountId accountId) => accountId.Value;

        public override string ToString()
        {
            return Value;
        }
        private static void CheckValidity(string value)
        {
            if (!Guid.TryParse(value, out _))
            {
                throw new ArgumentException(nameof(value), "Account Id is not a GUID.");
            }
        }
    }

класс Account

    class Account
    {
        [JsonProperty(PropertyName = "id")]
        public AccountId Id { get; set; }

        public Account(string id)
        {
            Id = AccountId.FromString(id);
        }
    }

Test

    static void Main(string[] args)
    {
        // Test toString 
        AccountId accountId = AccountId.FromString(Guid.NewGuid().ToString());
        Console.WriteLine(accountId.ToString());

        // Test AccountIdConverter
        Console.WriteLine(JsonConvert.SerializeObject(accountId));

        // Test for serializing Account
        Account account = new Account(Guid.NewGuid().ToString());
        string accountJson = JsonConvert.SerializeObject(account);
        Console.WriteLine(accountJson);

        // Test for deserializing Account
        Account accountDeserialized = JsonConvert.DeserializeObject<Account>(accountJson);
        Console.WriteLine(accountDeserialized.Id);

        Console.ReadLine();
    }

Результат

enter image description here

Вы можете видеть, что объект Account, который содержит объект AccountId, может быть правильно сериализован и десериализован, как и ожидалось.

...