Prise en main du stockage de tables Azure et de l’API de table d’Azure Cosmos DB avec F#

Le stockage de tables Azure est un service qui stocke des données NoSQL structurées dans le cloud. Le stockage de tables est un magasin de clés/attributs doté d’une conception sans schéma. Comme le stockage de tables est sans schéma, il est aisé d’adapter vos données en fonction des besoins de votre application. L'accès aux données est rapide et peu coûteux pour tous les types d'applications. Normalement, le stockage de tables est considérablement moins coûteux que le SQL traditionnel pour des volumes de données similaires.

Vous pouvez utiliser le stockage de tables pour stocker des jeux de données flexibles, par exemple, des données utilisateur pour des applications Web, des carnets d'adresses, des informations sur les périphériques et tout autre type de métadonnées requis par votre service. Vous pouvez stocker un nombre quelconque d'entités dans une table, et un compte de stockage peut contenir un nombre quelconque de tables, jusqu'à la limite de capacité du compte de stockage.

Azure Cosmos DB fournit l’API de table aux applications qui sont écrites pour le stockage de tables Azure et qui demandent des fonctionnalités Premium telles que :

  • Distribution globale clé en main.
  • Un débit dédié partout dans le monde.
  • Des latences de quelques millisecondes au 99e centile.
  • Une haute disponibilité garantie.
  • Une indexation secondaire automatique.

Les applications écrites pour le stockage de tables Azure peuvent migrer vers Azure Cosmos DB à l’aide de l’API de table sans aucune modification de code, et tirer parti des fonctionnalités Premium. L’API de table a des kits de développement logiciel (SDK) pour .NET, Java, Python et Node.js.

Pour plus d’informations, consultez Présentation de l’API de table Azure Cosmos DB.

À propos de ce didacticiel

Ce didacticiel montre comment écrire du code F# pour effectuer des tâches courantes à l’aide du Stockage de tables Azure ou de l’API de table Azure Cosmos DB, notamment la création et la suppression d’une table et l’insertion, la mise à jour, la suppression et l’interrogation de données de table.

Prérequis

Pour utiliser ce guide, vous devez d’abord créer un compte de stockage Azure ou un compte Azure Cosmos DB.

Créer un script F# et démarrer F# interactif

Les exemples de cet article peuvent être utilisés dans une application F# ou un script F#. Pour créer un script F#, créez un fichier avec l’extension .fsx, par exemple, tables.fsx, dans votre environnement de développement F#.

Comment exécuter les scripts

F# Interactive, dotnet fsi, peut être lancé de façon interactive ou à partir de la ligne de commande pour exécuter un script. La syntaxe de la ligne de commande est

> dotnet fsi [options] [ script-file [arguments] ]

Ajouter des packages dans un script

Ensuite, utilisez #rnuget:package name pour installer le package Azure.Data.Tables et les espaces de noms open. Elle doit contenir

> #r "nuget: Azure.Data.Tables"
open Azure.Data.Tables

Ajout de déclarations d'espaces de noms

Ajoutez les instructions open suivantes au début du fichier tables.fsx :

open System
open Azure
open Azure.Data.Tables // Namespace for Table storage types

Obtenir votre chaîne de connexion de stockage Azure

Si vous vous connectez au service de stockage de tables Azure, vous aurez besoin de votre chaîne de connexion pour ce didacticiel. Vous pouvez copier votre chaîne de connexion à partir du Portail Azure. Pour plus d'informations sur les chaînes de connexion, consultez Configuration des chaînes de connexion de stockage.

Obtenir votre chaîne de connexion Azure Cosmos DB

Si vous vous connectez à Azure Cosmos DB, vous aurez besoin de votre chaîne de connexion pour ce didacticiel. Vous pouvez copier votre chaîne de connexion à partir du Portail Azure. Dans le Portail Azure, dans votre compte Cosmos DB, accédez à Paramètres>Chaîne de connexion, puis sélectionnez le bouton Copier pour copier votre chaîne de connexion principale.

Pour le didacticiel, entrez votre chaîne de connexion dans votre script, comme dans l’exemple suivant :

let storageConnString = "UseDevelopmentStorage=true" // fill this in from your storage account

Créer le client du service de table

La classe TableServiceClient vous permet de récupérer des tables et des entités dans le stockage de tables. Voici un moyen de créer le client du service :

let tableClient = TableServiceClient storageConnString

Vous êtes maintenant prêt à écrire du code qui lit et écrit des données dans Table Storage.

Créer une table

Cet exemple montre comment créer une table, si elle n’existe pas encore :

// Retrieve a reference to the table.
let table = tableClient.GetTableClient "people"

// Create the table if it doesn't exist.
table.CreateIfNotExists () |> ignore

Ajout d'une entité à une table

Une entité doit avoir un type qui implémente ITableEntity. Vous pouvez étendre ITableEntity comme vous le souhaitez, mais votre type doit avoir un constructeur sans paramètre. Seules les propriétés qui ont get et set sont stockées dans votre table Azure.

Les clés de partition et de ligne d’une entité identifient l’entité de façon unique dans la table. Les requêtes d’entités dont les clés de partition sont identiques sont plus rapides que celles d’entités dont les clés de partition sont différentes, mais le fait d’utiliser différentes clés de partition améliore l’extensibilité des opérations parallèles.

Voici un exemple d’un Customer qui utilise lastName comme clé de partition et firstName comme clé de ligne.

type Customer (firstName, lastName, email: string, phone: string) =
    interface ITableEntity with
        member val ETag = ETag "" with get, set
        member val PartitionKey = "" with get, set
        member val RowKey = "" with get, set
        member val Timestamp = Nullable() with get, set

    new() = Customer(null, null, null, null)
    member val Email = email with get, set
    member val PhoneNumber = phone with get, set
    member val PartitionKey = lastName with get, set
    member val RowKey = firstName with get, set

Ajoutez maintenant Customer à la table. Pour ce faire, vous pouvez utiliser la méthode AddEntity().

let customer = Customer ("Walter", "Harp", "Walter@contoso.com", "425-555-0101")
table.AddEntity customer

Insertion d'un lot d'entités

Vous pouvez insérer un lot d'entités dans une table en une seule opération d'écriture. Les opérations de traitement par lots vous permettent de combiner des opérations en une seule exécution, mais elles présentent certaines restrictions :

  • Vous pouvez effectuer des mises à jour, des suppressions et des insertions dans la même opération de traitement par lots.
  • Une opération de traitement par lots peut inclure jusqu’à 100 entités.
  • Toutes les entités d'une opération de traitement par lots doivent avoir la même clé de partition.
  • Même s'il est possible d'exécuter une requête dans une opération de traitement par lots, il doit s'agir de la seule opération du lot.

Voici du code qui combine deux insertions dans une opération de traitement par lots :

let customers =
    [
        Customer("Jeff", "Smith", "Jeff@contoso.com", "425-555-0102")
        Customer("Ben", "Smith", "Ben@contoso.com", "425-555-0103")
    ]

// Add the entities to be added to the batch and submit it in a transaction.
customers
|> List.map (fun customer -> TableTransactionAction (TableTransactionActionType.Add, customer))
|> table.SubmitTransaction

Extraction de toutes les entités d'une partition

Pour exécuter une requête de table portant sur toutes les entités d’une partition, utilisez un objet Query<T>. Ici, vous filtrez les entités où « Smith » est la clé de partition.

table.Query<Customer> "PartitionKey eq 'Smith'"

Extraction d’un ensemble d’entités dans une partition

Si vous ne voulez pas exécuter une requête pour toutes les entités d'une partition, vous pouvez spécifier un ensemble en combinant le filtre de clé de partition avec un filtre de clé de ligne. Ici, vous utilisez deux filtres pour obtenir toutes les entités dans la partition « Smith » où la clé de ligne (prénom) commence par une lettre avant la lettre « M » dans l’alphabet.

table.Query<Customer> "PartitionKey eq 'Smith' and RowKey lt 'J'"

Extraction d'une seule entité

Pour récupérer une seule entité spécifique, utilisez GetEntityAsync pour spécifier le client « Ben Smith ». Au lieu d’une collection, vous récupérez un Customer. La méthode la plus rapide pour récupérer une seule entité dans le service de Table consiste à spécifier une clé de partition et une clé de ligne.

let singleResult = table.GetEntity<Customer>("Smith", "Ben").Value

Vous imprimez maintenant les résultats :

// Evaluate this value to print it out into the F# Interactive console
singleResult

Mise à jour d'une entité

Pour mettre à jour une entité, récupérez-la du service de Table, modifiez l’objet d’entité, puis enregistrez les modifications dans le service de Table à l’aide d’une opération TableUpdateMode.Replace. Ceci entraîne le remplacement complet de l’entité sur le serveur, sauf si cette dernière a été modifiée depuis sa récupération, auquel cas l’opération échoue. Cet échec empêche votre application de remplacer par inadvertance les modifications d’autres sources.

singleResult.PhoneNumber <- "425-555-0103"
try
    table.UpdateEntity (singleResult, ETag "", TableUpdateMode.Replace) |> ignore
    printfn "Update succeeded"
with
| :? RequestFailedException as e ->
    printfn $"Update failed: {e.Status} - {e.ErrorCode}"

Faire l’upsert d’une entité

Parfois, vous ne savez pas si une entité existe dans la table. Et si c’est le cas, les valeurs actuelles qui y sont stockées ne sont plus nécessaires. Vous pouvez utiliser la méthode UpsertEntity pour créer l’entité ou la remplacer si elle existe, quel que soit son état.

singleResult.PhoneNumber <- "425-555-0104"
table.UpsertEntity (singleResult, TableUpdateMode.Replace)

Interrogation d'un sous-ensemble de propriétés d'entité

Vous pouvez utiliser une requête de table pour récupérer uniquement quelques propriétés au lieu de l’intégralité de celles-ci. Cette technique, nommée projection, peut améliorer les performances des requêtes, notamment pour les entités volumineuses. Vous retournez ici uniquement les adresses e-mail à l’aide de Query<T> et Select. La projection n’est pas prise en charge sur l’émulateur de stockage local : ce code ne s’exécute donc que si vous utilisez un compte sur le service de Table.

query {
    for customer in table.Query<Customer> () do
    select customer.Email
}

Récupérer des entités dans les pages de manière asynchrone

Si vous lisez un grand nombre d’entités et souhaitez traiter les entités dès qu’elles sont récupérées au lieu d’attendre qu’elles aient toutes été renvoyées, vous pouvez utiliser une requête segmentée. Vous renvoyez ici les résultats dans des pages au moyen du flux de travail Async afin que l’exécution ne soit pas bloquée pendant que vous attendez le renvoi d’un ensemble de résultats volumineux.

let pagesResults = table.Query<Customer> ()

for page in pagesResults.AsPages () do
    printfn "This is a new page!"
    for customer in page.Values do
        printfn $"customer: {customer.RowKey} {customer.PartitionKey}"

Suppression d’une entité

Vous pouvez supprimer une entité après l’avoir récupérée. Comme pour la mise à jour d’une entité, cela échoue si l’entité a changé depuis que vous l’avez récupérée.

table.DeleteEntity ("Smith", "Ben")

Suppression d’une table

Vous pouvez supprimer une table d’un compte de stockage. Une table supprimée ne peut plus être recréée pendant un certain temps après la suppression.

table.Delete ()

Voir aussi