Erste Schritte mit Azure Table Storage und der Azure Cosmos DB-Tabellen-API mit F#

Azure Table Storage ist ein Dienst, bei dem strukturierte NoSQL-Daten in der Cloud gespeichert werden. Bei Table Storage handelt es sich um einen Schlüssel-/Attributspeicher mit einem schemalosen Design. Aufgrund der Schemalosigkeit von Table Storage ist es einfach, Ihre Daten an die Entwicklung Ihrer Anwendungen anzupassen. Der Datenzugriff ist für alle Arten von Anwendungen schnell und kostengünstig. Table Storage ist in der Regel erheblich günstiger als herkömmliche SQL-Lösungen für ähnliche Datenmengen.

Mit Table Storage können Sie flexible Datasets wie Benutzerdaten für Webanwendungen, Adressbücher, Geräteinformationen und jegliche Art von Metadaten speichern, die Ihr Dienst erfordert. Sie können eine beliebige Anzahl von Entitäten in einer Tabelle speichern, und ein Speicherkonto kann eine beliebige Anzahl von Tabellen enthalten (bis zur Speicherkapazitätsgrenze eines Speicherkontos).

Azure Cosmos DB stellt die Table-API für Anwendungen bereit, die für Azure Table Storage geschrieben wurden und Premium-Funktionen benötigen, z. B.:

  • Globale, sofort einsatzbereite Verteilung
  • Dedizierter Durchsatz weltweit.
  • Einstellige Latenzzeiten im Millisekundenbereich im 99. Perzentil.
  • Garantierte Hochverfügbarkeit.
  • Automatische sekundäre Indizierung

Anwendungen, die für Azure Table Storage geschrieben sind, können mit der Table-API ohne Codeänderungen zu Azure Cosmos DB migriert werden und Premium-Funktionen nutzen. Die Table-API verfügt über Client-SDKs, die für .NET, Java, Python und Node.js verfügbar sind.

Weitere Informationen finden Sie in der Einführung in Azure Cosmos DB for Table.

Informationen zu diesem Tutorial

In diesem Tutorial wird gezeigt, wie Sie F#-Code schreiben, um einige gängige Aufgaben mit Azure Table Storage oder der Tabellen-API von der Azure Cosmos DB auszuführen, z. B. Erstellen und Löschen einer Tabelle sowie Einfügen, Aktualisieren, Löschen und Abfragen von Tabellendaten.

Voraussetzungen

Um diesen Leitfaden verwenden zu können, müssen Sie zuerst ein Azure-Speicherkonto oder ein Azure Cosmos DB-Konto erstellen.

Erstellen eines F#-Skripts und Starten von F# Interactive

Die Beispiele in diesem Artikel können in einer F#-Anwendung oder in einem F#-Skript verwendet werden. Um ein F#-Skript zu erstellen, erstellen Sie eine Datei mit der Erweiterung .fsx, z. B. tables.fsx, in Ihrer F#-Entwicklungsumgebung.

Ausführen von Skripts

F# Interactive dotnet fsi kann interaktiv oder von der Befehlszeile aus gestartet werden, um ein Skript auszuführen. Die Befehlszeilensyntax lautet wie folgt:

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

Hinzufügen von Paketen in einem Skript

Verwenden Sie als Nächstes #rnuget:package name, um das Paket Azure.Data.Tables und die open-Namespaces zu installieren. Beispielsweise

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

Hinzufügen von Namespace-Deklarationen

Fügen Sie am Anfang der Datei tables.fsx die folgenden open-Anweisungen ein:

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

Abrufen der Azure Storage-Verbindungszeichenfolge

Wenn Sie eine Verbindung zum Azure Storage-Tabellendienst herstellen, benötigen Sie Ihre Verbindungszeichenfolge für dieses Tutorial. Sie können Ihre Verbindungszeichenfolge aus dem Azure-Portal kopieren. Weitere Informationen zu Verbindungszeichenfolgen finden Sie unter Konfigurieren von Azure Storage-Verbindungszeichenfolgen.

Abrufen der Azure Cosmos DB-Verbindungszeichenfolge

Wenn Sie eine Verbindung zu Azure Cosmos DB herstellen, benötigen Sie Ihre Verbindungszeichenfolge für dieses Tutorial. Sie können Ihre Verbindungszeichenfolge aus dem Azure-Portal kopieren. Wechseln Sie im Azure-Portal in Ihrem Cosmos DB-Konto zu Einstellungen>Verbindungszeichenfolge, und wählen Sie die Schaltfläche Kopieren aus, um Ihre primäre Verbindungszeichenfolge zu kopieren.

Für das Tutorial geben Sie die Verbindungszeichenfolge, wie im folgenden Beispiel gezeigt, in Ihr Skript ein:

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

Erstellen des Tabellendienstclients

Mit der TableServiceClient-Klasse können Sie Tabellen und Entitäten im Tabellenspeicher abrufen. Hier sehen Sie eine Möglichkeit zum Erstellen des Dienstclients:

let tableClient = TableServiceClient storageConnString

Jetzt können Sie Code schreiben, der Daten aus dem Tabellenspeicher liest und Daten in den Tabellenspeicher schreibt.

Erstellen einer Tabelle

Dieses Beispiel zeigt, wie Sie eine Tabelle erstellen, falls sie nicht bereits vorhanden ist:

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

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

Hinzufügen einer Entität zu einer Tabelle

Eine Entität muss über einen Typ verfügen, der ITableEntity implementiert. Sie können ITableEntity beliebig erweitern, der Typ muss aber einen parameterlosen Konstruktor aufweisen. Nur Eigenschaften, die sowohl über get als auch über set verfügen, werden in Ihrer Azure-Tabelle gespeichert.

Der Partitions- und Zeilenschlüssel einer Entität kennzeichnen eine Entität in der Tabelle eindeutig. Entitäten mit demselben Partitionsschlüssel können schneller abgerufen werden als Entitäten mit unterschiedlichen Partitionsschlüsseln, die Verwendung verschiedener Partitionsschlüssel ermöglicht jedoch eine größere Skalierbarkeit paralleler Vorgänge.

Hier sehen Sie ein Beispiel für Customer, in dem lastName als Partitionsschlüssel und firstName als Zeilenschlüssel verwendet wird.

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

Fügen Sie nun Customer zur Tabelle hinzu. Dazu können wir die AddEntity()-Methode verwenden.

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

Einfügen eines Entitätsbatchs

Sie können einen Batch von Entitäten in einer einzelnen Schreiboperation in eine Tabelle einfügen. Batchvorgänge bieten die Möglichkeit, Operationen in einer einzelnen Ausführung zu kombinieren. Dabei gelten jedoch einige Einschränkungen:

  • Sie können Aktualisierungs-, Lösch- und Einfügevorgänge in einer einzigen Batchoperation ausführen.
  • Eine Batchoperation kann bis zu 100 Entitäten umfassen.
  • Alle Entitäten in eine Batchoperation müssen über denselben Partitionsschlüssel verfügen.
  • Eine Abfrage kann als Batchoperation durchgeführt werden, dabei muss es sich jedoch um die einzige Operation im Batch handeln.

Der folgende Code kombiniert zwei Einfügevorgänge in einer Batchoperation kombiniert:

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

Abrufen aller Entitäten einer Partition

Verwenden Sie ein Query<T>-Objekt, um für eine Tabelle alle Entitäten einer Partition abzufragen. Hier filtern Sie nach Entitäten mit dem Partitionsschlüssel „Smith“.

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

Abrufen eines Entitätsbereichs in einer Partition

Wenn Sie nicht alle Entitäten in einer Partition abrufen möchten, können Sie einen Bereich angeben, indem Sie den Partitionsschlüsselfilter mit einem Zeilenschlüsselfilter kombinieren. Hier verwenden Sie zwei Filter, um alle Entitäten in der „Smith“-Partition abzurufen, in der der Zeilenschlüssel (Vorname) mit einem Buchstaben vor „M“ im Alphabet beginnt.

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

Abrufen einer einzelnen Entität

Um eine einzelne, bestimmte Entität abzurufen, verwenden Sie GetEntityAsync, um den Kunden „Ben Smith“ anzugeben. Anstelle einer Auflistung wird ein Customer zurückgegeben. Die Angabe von Partitions- und Zeilenschlüssel in einer Abfrage ist die schnellste Möglichkeit, um eine einzelne Entität aus dem Tabellendienst abzurufen.

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

Jetzt geben Sie die Ergebnisse aus:

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

Aktualisieren einer Entität

Um eine Entität zu aktualisieren, rufen Sie sie aus dem Tabellendienst ab, ändern Sie das Entitätsobjekt, und speichern Sie die Änderungen dann mit einer TableUpdateMode.Replace-Operation wieder im Tabellendienst. Dadurch wird die Entität auf dem Server vollständig ersetzt, es sei denn, dass die Entität auf dem Server seit dem letzten Abruf geändert wurde. In diesem Fall würde die Operation fehlschlagen. Dadurch soll verhindert werden, dass die Anwendung versehentlich Änderungen aus anderen Quellen überschreibt.

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}"

Upsert für eine Entität

Manchmal wissen Sie nicht, ob eine Entität in der Tabelle vorhanden ist. Und wenn dies der Fall ist, werden die aktuell gespeicherten Werte nicht mehr benötigt. Unabhängig vom Zustand können Sie die UpsertEntity-Methode verwenden, um die Entität zu erstellen oder zu ersetzen, wenn sie vorhanden ist.

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

Abfragen einer Teilmenge von Entitätseigenschaften

Mit einer Tabellenabfrage können nicht nur alle, sondern auch nur einige Eigenschaften aus einer Entität abgerufen werden. Diese Projektion genannte Technik kann die Abfrageleistung verbessern, insbesondere für große Entitäten. Hier geben Sie mit Query<T> und Select nur E-Mail-Adressen zurück. Projektion nicht im lokalen Speicheremulator unterstützt wird, sodass dieser Code nur ausgeführt wird, wenn Sie ein Konto für den Tabellendienst verwenden.

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

Asynchrones Abrufen von Entitäten in Seiten

Wenn Sie eine Vielzahl von Entitäten lesen und verarbeiten möchten, während sie abgerufen werden, statt zu warten, bis sie alle zurückgeben wurden, können Sie eine segmentierte Abfrage verwenden. Hier werden Sie Ergebnisse mit einem Async-Workflow seitenweise zurückgeben, sodass die Ausführung nicht blockiert ist, während Sie auf die Rückgabe umfangreicher Ergebnisse warten.

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}"

Löschen einer Entität

Sie können eine Entität löschen, nachdem sie abgerufen wurde. Wie beim Aktualisieren einer Entität schlägt dies fehl, wenn sich die Entität seit dem Abruf geändert hat.

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

Löschen einer Tabelle

Sie können eine Tabelle aus einem Speicherkonto löschen. Eine gelöschte Tabelle kann für eine gewisse Zeit nach dem Löschvorgang nicht neu erstellt werden.

table.Delete ()

Siehe auch