Я использовал много баз данных NoSQL на основе документов. CosmosDB - безусловно, мой фаворит, главным образом потому, что это гораздо больше, чем простая база данных NoSQL. Такие вещи, как космический масштаб, различные параметры API, канал изменений или даже эмулятор, который предоставляет Microsoft, просто потрясают.

Однако, если вы не готовы посвятить много времени чтению об этом, интеграция может стать для некоторых кошмаром. Такие вещи, как его цена, его производительность, ключи секционирования и индексирование, и это лишь некоторые из них, могут быть слишком большими. Вдобавок к этому вы должны использовать SDK, предоставляемые Microsoft, что совсем неплохо, но если вы не знаете, когда использовать какой метод, у вас будет плохая производительность или вы будете платить намного больше, чем должны. Кроме того, все внутренне запечатано, поэтому модульное тестирование практически невозможно, если вы не хотите делать какие-то действительно хакерские вещи.

Здесь в игру вступает Космонавт.
Это библиотека-оболочка для SQL API CosmosDB, которая позволяет гибко использовать CRUD (и многое другое) на основе ваших объектов POCO. Кто-то может сказать, что SQL API SDK уже делает это, но, пожалуйста, продолжайте читать, и вы точно поймете, почему Cosmonaut - лучший вариант из двух.

Полное раскрытие: эта библиотека разработана и создана мной. Это открытый исходный код на Github по лицензии MIT. Предложения и отзывы приветствуются.

Установка

Космонавт публикуется на Нугет.
Вы можете установить его из браузера Nuget или из командной строки.

Install-Package Cosmonaut
or
dotnet add package Cosmonaut

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

Как только вы это сделаете, вы можете получить ICosmosStore<YourObject> от DI, и вы готовы к работе.
Кроме того, вы можете вручную создать объект CosmosStore.

В CosmosStoreSettings есть только три обязательных настройки:

  • DatabaseName
  • AuthKey
  • EndpointUrl

Можно настроить и другие параметры, например ConnectionPolicy или IndexingPolicy, но если они не установлены, по умолчанию будут использоваться значения CosmosDB по умолчанию.

Как использовать

По умолчанию Cosmonaut будет создавать / нуждаться в одной коллекции для каждого объекта. Однако в нем также есть логика для совместного использования коллекций между разными объектами. Об этом мы поговорим позже.

На данный момент все, что вам нужно знать, это то, что есть одно основное ограничение.

Для ваших объектов НЕОБХОДИМО установить один из следующих флажков:

  • Иметь свойство типа string с именем Id
  • Реализуйте интерфейс ICosmosEntity
  • Расширить класс CosmosEntity
  • Иметь свойство типа string с атрибутом [JsonProperty("id")]

Это необходимо для того, чтобы ваш объект можно было без проблем хранить, получать и обновлять в CosmosDB.

Если вы планируете выполнять какие-либо Select(x => x.Id) запросы, вам необходимо иметь атрибут [JsonProperty("id")] ИЛИ расширить класс CosmosEntity. Это связано с тем, что внутренний поставщик LINQtoCosmosSQL будет учитывать JsonProperty для динамического создания выражения.

Название коллекции, созданной Космонавтом (при отсутствии коллекции), формируется следующим образом. Если у объекта есть атрибут CosmosCollection, вы можете указать там имя коллекции. Если нет, то вместо этого будет использоваться версия имени объекта во множественном числе. Атрибут также очень полезен, если вы хотите добавить Cosmonaut в существующую коллекцию CosmosDB.

В CosmosStore есть следующие методы для извлечения объектов и управления ими:

  • AddAsync(TEntity entity) Добавляет объект в коллекцию CosmosDB.
  • UpdateAsync(TEntity entity) Обновляет существующий объект в коллекции CosmosDB.
  • UpsertAsync(TEntity entity) Обновляет существующий объект в коллекции CosmosDB или добавляет его, если его нет в коллекции
  • RemoveAsync(TEntity entity) Удаляет объект из коллекции CosmosDB.

Все вышеперечисленное также имеет метод Range, который позволяет выполнять действие для коллекции элементов. RemoveAsync также поддерживает удаление выражений на основе фильтра.

Ответы на операции также содержат ResourceResponse самого документа, чтобы разрешить получение низкоуровневой информации.

Что касается запросов…

… Вы можете просто вызвать метод .Query() и получить готовый к использованию IQueryable. Имейте в виду, что на уровне запроса CosmosDB поддерживает только Where, Select и SelectMany. Когда вы закончите с логикой запроса и будете готовы получить данные, у вас будет два варианта.

Вы можете использовать метод LINQ ToList(), но это синхронный вызов, который НЕ рекомендуется (да, даже Microsoft).

Вместо этого вам следует использовать один из методов расширения, которые поставляются с Cosmonaut, например:

  • ToListAsync
  • CountAsync
  • FirstOrDefaultAsync
  • FirstAsync
  • SingleOrDefaultAsync
  • SingleAsync
  • MaxAsync
  • MinAsync

Эти методы будут использовать встроенную логику разбиения по страницам, чтобы ваше приложение не было заблокировано, пока Cosmonaut извлекает для вас документы.

Как вы понимаете, это дает вам практически все необходимое для начала работы.

Вам нравится SQL?

Что ж, Cosmonaut также поддерживает запросы SQL и десериализацию объектов. Просто используйте один из четырех методов запросов, связанных с SQL.

Ключ раздела

Ключ раздела - одна из самых важных вещей, которые вам нужно понимать в CosmosDB. В этом блоге не будет объяснения, что это такое и как работает, но вы узнаете, как с этим работает Cosmonaut. Подробнее о ключах разделов можно прочитать здесь.

О ключе раздела нужно знать несколько вещей:

  • После создания коллекции без ключа раздела вы НЕ МОЖЕТЕ добавить его.
  • После создания коллекции с ключом раздела вы НЕ МОЖЕТЕ изменить ее.

В необщедоступных коллекциях Cosmonaut по умолчанию не добавляет ключ раздела. Однако с помощью атрибута [CosmosPartitionKey] вы можете указать, какое свойство является вашим ключом раздела. Это будет использоваться для создания коллекции с ключом, если коллекция еще не создана.

Индексирование

Индексирование играет большую роль, когда дело доходит до запроса свойств вашего документа.
По умолчанию коллекция CosmosDB создается со следующими правилами сбора:

Это также не блог о самом индексировании, поэтому я не буду вдаваться в подробности, но вам нужно знать, что вы не можете запрашивать частично совпадающие строки или упорядочивание с использованием этого поля, если тип индекса - Hash. Вы можете только точно сопоставить их. Cosmonaut позволяет отменить это на уровне настроек. Изменение хэша на диапазон позволит таким вещам, как StartsWith, соответствовать нужным вам данным. Однако имейте в виду, что это требует дополнительных RU для этого запроса.

Пример: если тип данных String - Hash, то точное совпадение, подобное приведенному ниже, cosmoStore.Query().FirstOrDefaultAsync(x => x.SomeProperty.Equals($"Nick Chapsas") вернет элемент, если он существует в CosmosDB, но cosmoStore.Query().FirstOrDefaultAsync(x => x.SomeProperty.StartsWith($"Nick Ch") выдаст ошибку. Изменение хэша на диапазон заставит последнее работать.

Однако вы также можете переопределить это на уровне запроса, просто изменив EnableScanInQuery в FeedOptions на «true».

Подробнее об индексировании можно прочитать здесь.

Экономия денег

Понятно, RU / s страшные. Они напрямую отражаются на деньгах и производительности. Не волнуйтесь, Cosmonaut создан, чтобы избавиться от этого страха.

Видите ли, CosmosDB взимает с вас почасовую оплату PER. Однако, если вы измените свои RU / s в течение часа хотя бы на секунду, то с вас будет списана стоимость одного часа из самых высоких RU / s за этот час.

Это может выйти из-под контроля, и не каждую коллекцию нужно отделять от другой. Имейте в виду, что это база данных без схемы, так почему бы не поделиться коллекциями?

Cosmonaut имеет встроенную поддержку беспрепятственного обмена коллекциями.
Все, что вам нужно сделать, чтобы надежно обмениваться коллекциями без нарушения ваших операций, - это две вещи:

  • Украсьте свой объект атрибутом SharedCosmosCollection
  • Реализуйте интерфейс ISharedCosmosEntity

Вам также необходимо указать имя общей коллекции, которую этот объект будет использовать таким образом [SharedCosmosCollection("shared")].

Единственный компромисс заключается в том, что ваше индексирование не может быть очень конкретным, потому что вы делитесь коллекциями.

Что-то, что также является обязательным, заключается в том, что если вы разделяете коллекцию, свойство id автоматически станет вашим ключом раздела. Этот выбор подтверждается двумя причинами:

  • Вы не можете добавить ключ раздела после создания коллекции, и жаль, что нет хотя бы случайного распределения разделов
  • У вас не гарантируется наличие какой-либо другой общей собственности между вашими документами.

Код

Cosmonaut находится на Github с открытым исходным кодом по лицензии MIT.

Пожалуйста, подумайте о том, чтобы попробовать и сообщить о любых проблемах там. Мое сердце бьется от звезд GitHub.
Мы очень ценим обратную связь.

Больше возможностей .NET можно найти в моем личном блоге http://chapsas.com