Fermer

novembre 21, 2024

Travailler avec Cosmos DB dans ASP.NET Core

Travailler avec Cosmos DB dans ASP.NET Core


Cosmos DB est une base de données NoSQL de Microsoft qui prend en charge plusieurs modèles de données tels que des documents, des valeurs clés et des graphiques. Dans cet article, nous apprendrons comment intégrer une API ASP.NET Core et manipuler les données dans cette base de données cloud native.

Cosmos DB est une base de données gérée proposée en tant que service cloud par Microsoft via la plateforme Azure.

L’apprentissage des ressources cloud est une exigence de base pour tout développeur souhaitant approfondir ses connaissances dans la création d’applications à grande échelle. En tant que service cloud, Cosmos DB élimine le besoin de gérer l’infrastructure de base de données, facilitant ainsi le développement et l’exploitation d’applications modernes qui nécessitent des performances et une disponibilité à l’échelle mondiale.

Dans cet article, nous comprendrons comment fonctionne Cosmos DB et comment intégrer une application Web utilisant les ressources ASP.NET Core pour gérer la manipulation des données dans Cosmos DB.

Que sont les bases de données NoSQL ?

Les bases de données relationnelles sont apparues dans les années 1970 et sont connues pour utiliser le langage de requête structuré (SQL) pour manipuler et stocker des informations. Ces bases de données sont dites « relationnelles » car elles organisent les données en tables liées les unes aux autres via des clés, permettant à différents ensembles de données d’avoir des références les unes aux autres.

Cependant, maintenir ces relations peut générer des coûts élevés, notamment lorsque le couplage entre les données est facultatif. Dans ces cas-là, l’utilisation de bases de données relationnelles peut devenir problématique.

Les bases de données NoSQL ont émergé pour faire face à des situations dans lesquelles il n’est pas nécessaire de maintenir des références fortes entre les données, offrant ainsi une alternative à l’approche relationnelle traditionnelle. Contrairement aux bases de données relationnelles, les bases de données NoSQL ne reposent pas sur une structure relationnelle rigide, ce qui offre une plus grande flexibilité et évolutivité dans les scénarios où les relations entre les données sont moins importantes.

Les bases de données NoSQL les plus populaires sont MongoDB, Apache Cassandra, Redis, Couchbase, DynamDB et Cosmos DB. Chaque base de données a ses particularités, comme le type de données stockées. Par exemple, MongoDB stocke les données au format document, tandis que Redis utilise le format clé-valeur.

Les données sont stockées au format JavaScript Object Notation (JSON) dans des bases de données NoSQL qui utilisent des documents. L’image ci-dessous montre les principales différences entre les bases de données SQL et NoSQL qui utilisent des documents pour le stockage.

SQL et NoSQL

Connaître Cosmos DB

Qu’est-ce que Cosmos DB ?

Cosmos DB est une plateforme en tant que service (PaaS) intégrée aux services proposés par Azure, qui est la plateforme cloud de Microsoft.

En plus d’être une base de données NoSQL, Cosmos DB prend en charge plusieurs modèles de données, tels que les documents, les valeurs-clés, les graphiques et les colonnes larges, via des API disponibles pour les principales bases de données NoSQL d’aujourd’hui telles que MongoDB, Cassandra et Gremlin, en plus d’un API pour les tables.

Azure Cosmos DB est largement utilisé dans les applications à grande échelle, car il a été conçu pour répondre à des scénarios nécessitant une haute disponibilité et une faible latence, tels que les applications IoT, de jeux, de commerce électronique et de réseaux sociaux. Il permet la création d’applications pouvant être mises à l’échelle mondialement sans complications.

API Cosmos DB

Quand envisager d’utiliser Cosmos DB

L’utilisation de Cosmos DB est recommandée dans les scénarios où des réponses rapides et une mise à l’échelle horizontale efficace sont nécessaires.

Prenons un système de commerce électronique, dans lequel chaque produit possède une page présentant les caractéristiques du produit. Le client consultera probablement de nombreuses pages de produits jusqu’à choisir le bon produit à ajouter au panier. Par conséquent, chaque fois que l’utilisateur ouvre la page produit, les données doivent être prêtes à être affichées. S’il y a un retard dans le chargement des données en raison de la latence, l’expérience utilisateur peut être compromise.

De plus, les grandes sociétés de commerce électronique autorisent la vente de produits d’occasion, encourageant toute personne souhaitant vendre un produit à commencer à utiliser l’application avec un profil de vendeur. De cette façon, l’application peut évoluer considérablement, en fonction du nombre de vendeurs, et c’est quelque chose que Cosmos DB peut gérer efficacement, en ajoutant automatiquement des ressources selon les besoins.

Pratique avec Cosmos DB

Dans cet article, nous intégrerons une API ASP.NET Core à une base de données Cosmos DB. Mais ne vous inquiétez pas des licences Azure : dans cet exemple, nous utiliserons un outil gratuit pour tester Cosmos DB appelé Cosmos DB Emulator. Vous pouvez le télécharger à partir de ce lien dans la section « Installer l’émulateur » : Développer localement à l’aide de l’émulateur Azure Cosmos DB.

Après avoir installé l’outil sur votre machine, un serveur d’émulateur Cosmos DB sera disponible et vous pourrez y accéder via l’adresse https://localhost:8081/_explorer/index.html.

Démarrage rapide de l’émulateur Azure Cosmos DB

Notez que les données suivantes sont affichées sur la page d’accueil de l’émulateur :

  • URI : https://localhost:8081 adresse où le serveur Cosmos DB est exécuté
  • Clé primaire : Donne accès à toutes les fonctionnalités d’administration du compte de base de données
  • Chaîne de connexion principale : Chaîne de connexion composée de AccountEndpoint et AccountKey
  • Chaîne de connexion Mongo : Chaîne de connexion pour MongoDB

Création de la base de données et du conteneur

Dans Cosmos DB, un conteneur est une unité de stockage et d’évolutivité qui organise et stocke les données. Les conteneurs sont automatiquement partitionnés et distribués sur plusieurs serveurs pour une évolutivité horizontale (par opposition aux bases de données relationnelles qui évoluent verticalement). Chaque élément d’un conteneur est associé à une clé de partition, qui détermine dans quelle partition l’élément sera stocké et est utilisée pour gérer la partition appropriée où il sera écrit, mis à jour ou supprimé.

Pour créer un conteneur dans Cosmos DB Emulator, cliquez sur l’onglet Explorer, puis cliquez sur le bouton « Nouveau conteneur », puis remplissez les champs comme indiqué dans l’image ci-dessous :

Créer un nouveau conteneur

Notez qu’en plus de la base de données et du conteneur, nous définissons le nom de la clé de partition. Dans cet exemple, nous utiliserons le même identifiant que l’entité qui sera stockée dans la base de données. Par conséquent, il doit être écrit avec une lettre initiale minuscule, sinon cela générera une erreur.

Après avoir rempli les champs et cliqué sur OK, vous pouvez vérifier le conteneur et la base de données créés :

Vérification du nouveau conteneur

Création de l’application et intégration avec Cosmos DB

Dans cet article, nous allons créer une API Web ASP.NET Core pour gérer les données des pages produits. Nous l’intégrerons à Cosmos DB, et il aura deux points de terminaison, un POST pour insérer des données et un GET pour les récupérer, puis nous utiliserons Progress Telerik Fiddler Everywhere pour faire les requêtes.

Vous pouvez accéder au code source dans ce référentiel GitHub : Code source d’EasyStore.

Pour créer la solution applicative et le projet API, utilisez les commandes ci-dessous :

dotnet new sln -n EasyStore
dotnet new webapi -n Catalog.API
dotnet sln add Catalog.API/Catalog.API.csproj

Utilisez ensuite les commandes suivantes pour télécharger les dépendances pour Cosmos DB et CSV Helper (que nous utiliserons pour manipuler les fichiers CSV) :

cd Catalog.API
dotnet add package Microsoft..Cosmos
dotnet add package CsvHelperAzure

Ce didacticiel se concentrera sur les bases, en évitant les techniques avancées de séparation des préoccupations. Le but est de garder les choses aussi simples que possible. Alors, ouvrez l’application dans votre IDE, créez un nouveau dossier appelé « Modèles » et ajoutez-y la classe suivante pour représenter l’entité ProductPage :

using Newtonsoft.Json;
namespace Catalog.API.Models;

public class ProductPage
{
  [JsonProperty("id")]
  public string Id { get; set; }
  public string Title { get; set; }
  public string Description { get; set; }
  public string ImageUrl { get; set; }
  public string AdditionalInfo { get; set; }
  public DateTime PublishedDate { get; set; }
  public bool IsPublished { get; set; }
}

Notez que nous utilisons ici le [JsonProperty("id")] attribut nécessaire à la id propriété doit être égale à la clé de partition que nous avons configurée dans Cosmos DB.

Ensuite, créez un nouveau dossier appelé « Services » et à l’intérieur, créez la classe ci-dessous :

using Catalog.API.Models;
using Microsoft.Azure.Cosmos;
namespace Catalog.API.Services;

public class CatalogService
{
    private readonly Container _container;
    private readonly ILogger<CatalogService> _logger;

    public CatalogService(CosmosClient cosmosClient, string databaseName, string containerName, ILogger<CatalogService> logger)
    {
        _container = cosmosClient.GetContainer(databaseName, containerName);
        _logger = logger;
    }

    public async Task AddProductPage(ProductPage productPage)
    {
        try
        {
            var partitionKey = new PartitionKey(productPage.Id);

            await _container.UpsertItemAsync(productPage, partitionKey);
        }
        catch (CosmosException ex)
        {
            _logger.LogError($"CosmosDB Error: {ex.StatusCode} - {ex.Message}");
            _logger.LogError($"ActivityId: {ex.ActivityId}");
        }
        catch (Exception ex)
        {
            _logger.LogError($"Unexpected Error: {ex.Message}");
        }
    }

    public async Task<ProductPage?> GetProductPage(string id)
    {
        try
        {
            var response = await _container.ReadItemAsync<ProductPage>(id, new PartitionKey(id));
            return response.Resource;
        }
        catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            return null;
        }
    }
}

Nous avons ici la classe CatalogService qui a le AddProductPage() méthode qui reçoit un élément (productPage) en paramètre et appelle le UpsertItemAsync() méthode pour ajouter l’élément au conteneur Cosmos DB. De plus, il existe également des gestionnaires d’exceptions pour savoir s’il y a eu une exception provenant de Cosmos DB ou d’une autre source.

Le GetProductPage() La méthode reçoit un identifiant et recherche un élément via ReadItemAsync(). Si le retour est un code d’état 404, le retour est nul.

Ensuite, créons le contrôleur et les points de terminaison. Alors, créez un nouveau dossier appelé « Contrôleurs » et ajoutez-y le contrôleur suivant :

using Microsoft.AspNetCore.Mvc;
using Catalog.API.Models;
using Catalog.API.Services;
using CsvHelper;
using System.Globalization;

namespace Catalog.API.Controllers;

[ApiController]
[Route("api/[controller]")]
public class CatalogController : ControllerBase
{
    private readonly CatalogService _catalogService;

    public CatalogController(CatalogService catalogService)
    {
        _catalogService = catalogService;
    }

    [HttpPost("/product-page/upload")]
    public async Task<IActionResult> UploadProductPages([FromForm] IFormFile file)
    {
        if (file == null || file.Length == 0)
            return BadRequest("File is empty.");

        using var streamReader = new StreamReader(file.OpenReadStream());
        using var csvReader = new CsvReader(streamReader, CultureInfo.InvariantCulture);
        var productPages = csvReader.GetRecords<ProductPage>();

        foreach (var productPage in productPages)
        {
            await _catalogService.AddProductPage(productPage);
        }

        return Ok("Product pages have been uploaded and saved to the database.");
    }

    [HttpGet("/product-page")]
    public async Task<ActionResult<IEnumerable<ProductPage>>> GetProductPage([FromQuery] string id)
    {
        var productPage = await _catalogService.GetProductPage(id);
        return productPage != null ? Ok(productPage) : NotFound();
    }
}

Nous avons ici un contrôleur avec deux points de terminaison : le premier pour télécharger un fichier CSV avec les données de la page produit, et le second pour récupérer les données d’une page produit enregistrée par ID.

L’étape suivante consiste à créer les configurations dans la classe Program. Remplacez donc le contenu de la classe Program.cs par le code ci-dessous :

using Catalog.API.Services;
using Microsoft.Azure.Cosmos;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();

builder.Services.AddSingleton(s =>
{
    var cosmosClient = new CosmosClient(
        builder.Configuration["CosmosDb:Account"],
        builder.Configuration["CosmosDb:Key"]);

    var logger = s.GetRequiredService<ILogger<CatalogService>>();

    return new CatalogService(
        cosmosClient,
        builder.Configuration["CosmosDb:DatabaseName"],
        builder.Configuration["CosmosDb:ContainerName"],
        logger);
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.MapControllers();

app.Run();

Notez qu’ici nous configurons la chaîne de connexion avec Cosmos DB via des variables telles que CosmosDb:Account et "CosmosDb:Key qui sera configuré dans le fichier appsettings.json.

Dans appsettings.json, ajoutez le code suivant :

"CosmosDb": {
   "Account": "https://localhost:8081",
   "Key": "YOUR_AZURE_KEY",
   "DatabaseName": "Catalogs",
   "ContainerName": "ProductPage"
 },

Il est important de noter que le Key La variable doit être remplie avec la valeur présente dans la configuration du conteneur Cosmos DB, comme indiqué dans l’image ci-dessous :

Paramètres clés

Exécution de l’application et vérification des données

Toutes les configurations nécessaires sont prêtes, nous pouvons maintenant exécuter l’application et vérifier les données dans l’émulateur Cosmos DB.

Exécutez l’application et, dans Fiddler Everywhere, ouvrez l’onglet Composer et créez une requête (POST) vers le point de terminaison : https://localhost:PORT/product-page/upload. Dans l’onglet « Corps », sélectionnez « Form-Data » et « Bulk ». Insérez ensuite le code ci-dessous dans le corps de la requête :

Content-Type: multipart/form-data; boundary=productPageBoundary

--productPageBoundary
Content-Disposition: form-data; name="file"; filename="new_items.csv"
Content-Type: text/plain

Id,Title,Description,ImageUrl,AdditionalInfo,PublishedDate,IsPublished
P0001,"Wireless Headphones","High-quality wireless headphones with noise-cancellation feature.","https://example.com/images/headphones.jpg","Battery life: 20 hours",2024-08-01,true
P0002,"4K Ultra HD Smart TV","55-inch 4K Ultra HD Smart TV with built-in streaming services.","https://example.com/images/tv.jpg","Includes HDMI cable",2024-07-15,true
P0003,"Electric Scooter","Foldable electric scooter with a top speed of 25 km/h.","https://example.com/images/scooter.jpg","Max load: 120 kg",2024-07-10,true
P0004,"Fitness Tracker","Waterproof fitness tracker with heart rate monitor.","https://example.com/images/fitnesstracker.jpg","Compatible with iOS and Android",2024-07-20,false
P0005,"Smart Home Speaker","Voice-controlled smart speaker with integrated assistant.","https://example.com/images/smartspeaker.jpg","Supports multiple languages",2024-08-05,true
--productPageBoundary--

Ici, nous envoyons les données des pages produits sous forme de fichier CSV.

Le Content-Type l’en-tête définit le type de contenu de la requête comme multipart/form-data. Il comprend un boundary (délimiteur) appelé productPageBoundaryune chaîne qui sépare les différentes parties des données envoyées.

Chaque élément de contenu est ensuite délimité par le boundary. Le premier bloc de données commence par Content-Dispositionqui correspond aux données du formulaire, et identifie le nom du champ comme file et spécifie le nom du fichier comme new_items.csv.

Le contenu du fichier CSV est inclus dans la demande, contenant des lignes représentant les produits avec leurs informations, telles que l’ID, le titre, la description, l’URL de l’image, des informations supplémentaires, la date de publication et le statut de publication.

Finalement, la requête se termine par le même boundary délimiteur, qui marque la fin des données envoyées.

Ainsi, si vous exécutez la requête, vous obtiendrez la réponse suivante avec une phrase indiquant le succès :

Envoi CSV dans Fiddler

Maintenant, si vous demandez le point de terminaison https://localhost:PORT/product-page?id=P0001 vous pouvez vérifier l’enregistrement nouvellement créé :

Récupération de données dans Fiddler

Et si vous accédez à l’émulateur CosmosDB, vous pouvez vérifier les données créées dans le conteneur :

Accéder aux données dans l'émulateur Cosmos DB

Notez qu’en plus des informations envoyées dans la demande, dans le document portant l’identifiant « P0001 », il existe d’autres propriétés générées par Cosmos DB :

  • _rid: L’ID de la ressource (_rid) est un identifiant unique, utilisé en interne pour le positionnement et la navigation de la ressource documentaire.
  • _self: Il s’agit d’une propriété générée par le système. Il s’agit de l’URI (Uniform Resource Identifier) ​​unique de la ressource.
  • _etag: Il spécifie la ressource etag requise pour le contrôle de concurrence optimiste.
  • _attachments: Ceci spécifie le chemin d’adresse vers la ressource de pièce jointe.
  • _ts: Ceci spécifie le dernier horodatage enregistré.

Conclusion

Cosmos DB offre de nombreuses fonctionnalités pour travailler avec des applications Web et s’intègre parfaitement à l’écosystème .NET, car les deux sont des produits Microsoft. Dans cet article, nous avons exploré les bases de Cosmos DB et mis en évidence certains de ses services clés, tels que les API compatibles avec les bases de données populaires telles que MongoDB et Apache Cassandra. Nous avons également couvert la création d’une API Web dans ASP.NET Core et son intégration à la base de données Cosmos DB NoSQL à l’aide de l’émulateur Cosmos DB.

Cosmos DB constitue un excellent choix, en particulier lorsque les performances et l’évolutivité sont essentielles. Face à la demande croissante de solutions pouvant évoluer efficacement, les développeurs doivent se familiariser avec les capacités de Cosmos DB. Ces connaissances sont essentielles pour prendre de bonnes décisions lors de la création d’applications Web de moyenne à grande échelle.




Source link