Cloud Spanner: Gaming-Bestenliste mit C# erstellen

1. Übersicht

Google Cloud Spanner ist ein vollständig verwalteter, horizontal skalierbarer, global verteilter relationaler Datenbankdienst, der ACID-Transaktionen und SQL-Semantik bietet, ohne auf Leistung und Hochverfügbarkeit zu verzichten.

In diesem Lab erfahren Sie, wie Sie eine Cloud Spanner-Instanz einrichten. Sie führen die Schritte zum Erstellen einer Datenbank und eines Schemas aus, die für eine Gaming-Bestenliste verwendet werden können. Zuerst erstellen Sie eine Spielertabelle zur Speicherung von Spielerinformationen und eine Punktzahltabelle, in der die Punktzahlen der Spieler gespeichert werden.

Als Nächstes füllen Sie die Tabellen mit Beispieldaten. Am Ende des Labs führen Sie einige Top-10-Beispielabfragen aus und löschen schließlich die Instanz, um Ressourcen freizugeben.

Aufgaben in diesem Lab

  • Cloud Spanner-Instanz einrichten
  • Datenbank und Tabellen erstellen
  • So verwenden Sie eine Commit-Zeitstempelspalte.
  • Daten in eine Cloud Spanner-Datenbanktabelle mit Zeitstempeln laden.
  • Anleitung zum Abfragen Ihrer Cloud Spanner-Datenbank.
  • Cloud Spanner-Instanz löschen

Voraussetzungen

Wie möchten Sie diese Anleitung nutzen?

<ph type="x-smartling-placeholder"></ph> Nur bis zum Ende lesen Lies sie dir durch und absolviere die Übungen

Wie würden Sie Ihre Erfahrung mit der Google Cloud Platform bewerten?

<ph type="x-smartling-placeholder"></ph> Neuling Leicht fortgeschritten Kompetent

2. Einrichtung und Anforderungen

Umgebung für das selbstbestimmte Lernen einrichten

Wenn Sie noch kein Google-Konto (Gmail oder Google Apps) haben, müssen Sie eines erstellen. Melden Sie sich in der Google Cloud Platform Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.

Wenn Sie bereits ein Projekt haben, klicken Sie auf das Drop-down-Menü für die Projektauswahl oben links in der Konsole:

6c9406d9b014760.png

und klicken Sie auf „NEUES PROJEKT“, Schaltfläche zum Erstellen eines neuen Projekts:

f708315ae07353d0.png

Wenn Sie noch kein Projekt haben, sollten Sie ein Dialogfeld wie dieses sehen, um Ihr erstes zu erstellen:

870a3cbd6541ee86.png

Im nachfolgenden Dialog zur Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:

6a92c57d3250a4b3.png

Denken Sie an die Projekt-ID. Dies ist ein eindeutiger Name für alle Google Cloud-Projekte. Der oben angegebene Name ist bereits vergeben und funktioniert leider nicht für Sie. Sie wird in diesem Codelab später als PROJECT_ID bezeichnet.

Falls noch nicht geschehen, müssen Sie als Nächstes in der Developers Console die Abrechnung aktivieren, um Google Cloud-Ressourcen nutzen und die Cloud Spanner API aktivieren zu können.

15d0ef27a8fbab27.png

Dieses Codelab sollte nicht mehr als ein paar Euro kosten. Wenn Sie sich jedoch dazu entschließen, mehr Ressourcen zu verwenden oder diese weiter auszuführen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments), Die Preise für Google Cloud Spanner finden Sie hier.

Neue Google Cloud Platform-Nutzer haben Anspruch auf eine kostenlose Testversion mit 300$Guthaben, wodurch das Codelab in der Regel kostenlos sein sollte.

Google Cloud Shell einrichten

Sie können Google Cloud und Spanner über Ihren Laptop aus der Ferne bedienen. In diesem Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.

Diese Debian-basierte virtuelle Maschine verfügt über alle erforderlichen Entwicklungstools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser – ja, er funktioniert auf Chromebooks.

  1. Klicken Sie einfach auf Cloud Shell aktivieren gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A, um Cloud Shell über die Cloud Console zu aktivieren. Die Bereitstellung und Verbindung mit der Umgebung dauert einen Moment.

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und dass das Projekt bereits auf Ihre PROJECT_ID eingestellt ist.

gcloud auth list

Befehlsausgabe

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Befehlsausgabe

[core]
project = <PROJECT_ID>

Sollte das Projekt aus irgendeinem Grund nicht eingerichtet sein, geben Sie einfach den folgenden Befehl ein:

gcloud config set project <PROJECT_ID>

Du suchst dein Gerät (PROJECT_ID)? Sehen Sie nach, welche ID Sie bei den Einrichtungsschritten verwendet haben, oder rufen Sie sie im Dashboard der Cloud Console auf:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell legt außerdem standardmäßig einige Umgebungsvariablen fest, die bei der Ausführung zukünftiger Befehle nützlich sein können.

echo $GOOGLE_CLOUD_PROJECT

Befehlsausgabe

<PROJECT_ID>
  1. Legen Sie schließlich die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f

Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.

Zusammenfassung

In diesem Schritt richten Sie Ihre Umgebung ein.

Nächstes Thema

Als Nächstes richten Sie eine Cloud Spanner-Instanz ein.

3. Cloud Spanner-Instanz einrichten

In diesem Schritt richten wir unsere Cloud Spanner-Instanz für dieses Codelab ein. Suchen Sie im linken Hamburger-Menü 3129589f7bc9e5ce.png nach dem Spanner-Eintrag 1a6580bd3d3e6783.png oder drücken Sie „/“, um nach Spanner zu suchen und geben Sie „Spanner“ ein

36e52f8df8e13b99.png

Klicken Sie als Nächstes auf 95269e75bc8c3e4d.png und füllen Sie das Formular aus. Geben Sie dazu den Instanznamen cloudspanner-leaderboard für Ihre Instanz ein, wählen Sie eine Konfiguration aus (wählen Sie eine regionale Instanz aus) und legen Sie die Anzahl der Knoten fest. Für dieses Codelab benötigen wir nur einen Knoten. Für Produktionsinstanzen und zur Einhaltung des Cloud Spanner-SLA müssen Sie mindestens drei Knoten in Ihrer Cloud Spanner-Instanz ausführen.

Klicken Sie abschließend auf „Erstellen“. und innerhalb von Sekunden steht eine Cloud Spanner-Instanz zur Verfügung.

dceb68e9ed3801e8.png

Im nächsten Schritt erstellen wir mithilfe der C#-Clientbibliothek eine Datenbank und ein Schema in der neuen Instanz.

4. Datenbank und Schema erstellen

In diesem Schritt erstellen wir die Beispieldatenbank und das Schema.

Erstellen wir mit der C#-Clientbibliothek zwei Tabellen. eine Spielertabelle mit Spielerinformationen und eine Punktzahltabelle zum Speichern der Punktzahlen der Spieler Gehen Sie dazu die Schritte zum Erstellen einer C#-Konsolenanwendung in Cloud Shell durch.

Klonen Sie zuerst den Beispielcode für dieses Codelab aus GitHub. Geben Sie dazu den folgenden Befehl in Cloud Shell ein:

git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git

Wechseln Sie dann in das Verzeichnis „applications“ Verzeichnis, in dem Sie Ihre Anwendung erstellen.

cd dotnet-docs-samples/applications/

Der gesamte Code, der für dieses Codelab erforderlich ist, befindet sich im vorhandenen dotnet-docs-samples/applications/leaderboard-Verzeichnis als ausführbare C#-Anwendung mit dem Namen Leaderboard. Sie dient als Referenz, während Sie das Codelab durchlaufen. Wir werden ein neues Verzeichnis erstellen und schrittweise eine Kopie der Leaderboard-Anwendung erstellen.

Erstellen Sie ein neues Verzeichnis mit dem Namen „codelab“. für die Anwendung und wechseln Sie mit dem folgenden Befehl in das entsprechende Verzeichnis:

mkdir codelab && cd $_

Eine neue .NET-C#-Konsolenanwendung namens "Leaderboard" erstellen mit dem folgenden Befehl:

dotnet new console -n Leaderboard

Mit diesem Befehl wird eine einfache Konsolenanwendung erstellt, die aus zwei primären Dateien besteht: der Projektdatei Leaderboard.csproj und der Programmdatei Program.cs.

und führen es aus. Wechseln Sie in das neu erstellte Leaderboard-Verzeichnis, in dem sich die Anwendung befindet:

cd Leaderboard

Geben Sie dann den folgenden Befehl ein, um ihn auszuführen.

dotnet run

Sie sollten die Anwendungsausgabe „Hello World!“ sehen.

Aktualisieren wir nun unsere Konsolen-App, indem wir Program.cs so bearbeiten, dass mithilfe der C#-Spanner-Clientbibliothek eine Bestenliste erstellt wird, die aus zwei Tabellen für Spieler und Punktzahlen besteht. Sie können dies direkt im Cloud Shell-Editor tun:

Öffnen Sie den Cloud Shell-Editor, indem Sie auf das unten hervorgehobene Symbol klicken:

73cf70e05f653ca.png

Öffnen Sie als Nächstes die Datei Program.cs im Cloud Shell-Editor und ersetzen Sie den vorhandenen Code der Datei durch den Code, der zum Erstellen der Datenbank leaderboard und der Tabellen Players und Scores erforderlich ist. Fügen Sie dazu den folgenden C#-Anwendungscode in die Datei Program.cs ein:

using System;
using System.Threading.Tasks;
using Google.Cloud.Spanner.Data;
using CommandLine;

namespace GoogleCloudSamples.Leaderboard
{
    [Verb("create", HelpText = "Create a sample Cloud Spanner database "
        + "along with sample 'Players' and 'Scores' tables in your project.")]
    class CreateOptions
    {
        [Value(0, HelpText = "The project ID of the project to use "
            + "when creating Cloud Spanner resources.", Required = true)]
        public string projectId { get; set; }
        [Value(1, HelpText = "The ID of the instance where the sample database "
            + "will be created.", Required = true)]
        public string instanceId { get; set; }
        [Value(2, HelpText = "The ID of the sample database to create.",
            Required = true)]
        public string databaseId { get; set; }
    }

    public class Program
    {
        enum ExitCode : int
        {
            Success = 0,
            InvalidParameter = 1,
        }

        public static object Create(string projectId,
            string instanceId, string databaseId)
        {
            var response =
                CreateAsync(projectId, instanceId, databaseId);
            Console.WriteLine("Waiting for operation to complete...");
            response.Wait();
            Console.WriteLine($"Operation status: {response.Status}");
            Console.WriteLine($"Created sample database {databaseId} on "
                + $"instance {instanceId}");
            return ExitCode.Success;
        }

        public static async Task CreateAsync(
            string projectId, string instanceId, string databaseId)
        {
            // Initialize request connection string for database creation.
            string connectionString =
                $"Data Source=projects/{projectId}/instances/{instanceId}";
            using (var connection = new SpannerConnection(connectionString))
            {
                string createStatement = $"CREATE DATABASE `{databaseId}`";
                string[] createTableStatements = new string[] {
                  // Define create table statement for Players table.
                  @"CREATE TABLE Players(
                    PlayerId INT64 NOT NULL,
                    PlayerName STRING(2048) NOT NULL
                  ) PRIMARY KEY(PlayerId)",
                  // Define create table statement for Scores table.
                  @"CREATE TABLE Scores(
                    PlayerId INT64 NOT NULL,
                    Score INT64 NOT NULL,
                    Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
                  ) PRIMARY KEY(PlayerId, Timestamp),
                      INTERLEAVE IN PARENT Players ON DELETE NO ACTION" };
                // Make the request.
                var cmd = connection.CreateDdlCommand(
                    createStatement, createTableStatements);
                try
                {
                    await cmd.ExecuteNonQueryAsync();
                }
                catch (SpannerException e) when
                    (e.ErrorCode == ErrorCode.AlreadyExists)
                {
                    // OK.
                }
            }
        }

        public static int Main(string[] args)
        {
            var verbMap = new VerbMap<object>();
            verbMap
                .Add((CreateOptions opts) => Create(
                    opts.projectId, opts.instanceId, opts.databaseId))
                .NotParsedFunc = (err) => 1;
            return (int)verbMap.Run(args);
        }
    }
}

Zur Verdeutlichung des Programmcodes hier ein Diagramm des Programms mit den beschrifteten Hauptkomponenten:

b70b1b988ea3ac8a.png

Mit der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step4 können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei Program.cs aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls create hinzugefügt haben.

Verwenden Sie als Nächstes den Cloud Shell-Editor, um die Projektdatei Leaderboard.csproj des Programms zu öffnen und zu bearbeiten. Aktualisieren Sie sie in den folgenden Code. Speichern Sie alle Änderungen mithilfe der Option "Datei" des Cloud Shell-Editors.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Cloud.Spanner.Data" Version="3.3.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\..\commandlineutil\Lib\CommandLineUtil.csproj" />
  </ItemGroup>

</Project>

Durch diese Änderung wurde ein Verweis auf das C# Spanner Nuget-Paket hinzugefügt, das für die Interaktion mit der Cloud Spanner API erforderlich ist. Google.Cloud.Spanner.Data. Mit dieser Änderung wird auch eine Referenz auf das Projekt CommandLineUtil hinzugefügt, das Teil des GitHub-Repositorys „dotnet-doc-samples“ ist und eine nützliche „verbmap“ bietet. Erweiterung der Open-Source-Version CommandLineParser; eine praktische Bibliothek für die Befehlszeileneingabe in Konsolenanwendungen.

Mit der Datei Leaderboard.csproj im Verzeichnis dotnet-docs-samples/applications/leaderboard/step4 können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei Leaderboard.csproj aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls create hinzugefügt haben.

Jetzt können Sie das aktualisierte Beispiel ausführen. Geben Sie Folgendes ein, um die Standardantwort der aktualisierten Anwendung anzusehen:

dotnet run

Die Ausgabe sollte etwa so aussehen:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  No verb selected.

  create     Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project.

  help       Display more information on a specific command.

  version    Display version information.

Dieser Antwort können wir entnehmen, dass dies die Anwendung Leaderboard ist, die mit einem der drei möglichen Befehle ausgeführt werden kann: create, help und version.

Lassen Sie uns den Befehl create ausprobieren, um eine Spanner-Datenbank und -Tabellen zu erstellen. Führen Sie den Befehl ohne Argumente aus, um die erwarteten Argumente des Befehls anzuzeigen.

dotnet run create

Sie sollten eine Antwort wie die folgende sehen:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  A required value not bound to option name is missing.

  --help          Display this help screen.

  --version       Display version information.

  value pos. 0    Required. The project ID of the project to use when creating Cloud Spanner resources.

  value pos. 1    Required. The ID of the instance where the sample database will be created.

  value pos. 2    Required. The ID of the sample database to create.

Hier sehen Sie, dass die erwarteten Argumente des Befehls create die Projekt-ID, die Instanz-ID und die Datenbank-ID sind.

Führen Sie nun den folgenden Befehl aus. Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard

Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:

Waiting for operation to complete...
Operation status: RanToCompletion
Created sample database leaderboard on instance cloudspanner-leaderboard

Im Bereich „Cloud Spanner“ der Cloud Console sollten Sie im linken Menü Ihre neue Datenbank und Ihre Tabellen sehen.

ba9008bb84cb90b0.png

Im nächsten Schritt aktualisieren wir unsere Anwendung, um einige Daten in Ihre neue Datenbank zu laden.

5. Daten laden

Wir haben jetzt eine Datenbank mit dem Namen leaderboard, die zwei Tabellen enthält. Players und Scores. Verwenden wir nun die C#-Clientbibliothek, um unsere Tabelle Players mit Spielern und unsere Tabelle Scores mit Zufallsscores für jeden Spieler zu füllen.

Öffnen Sie den Cloud Shell-Editor, indem Sie auf das unten hervorgehobene Symbol klicken:

4d17840699d8e7ce.png

Bearbeiten Sie als Nächstes die Datei Program.cs im Cloud Shell-Editor, um einen insert-Befehl hinzuzufügen, mit dem 100 Spieler in die Tabelle Players eingefügt werden können. Sie können damit auch 4 Zufallspunktzahlen für jeden Spieler in der Tabelle Players in die Tabelle Scores einfügen.

Fügen Sie zuerst einen neuen insert-Befehlsblock in die Verbmap ein am Anfang des Programms unter dem vorhandenen create-Befehlsblock:

[Verb("insert", HelpText = "Insert sample 'players' records or 'scores' records "
        + "into the database.")]
    class InsertOptions
    {
        [Value(0, HelpText = "The project ID of the project to use "
            + "when managing Cloud Spanner resources.", Required = true)]
        public string projectId { get; set; }
        [Value(1, HelpText = "The ID of the instance where the sample database resides.",
            Required = true)]
        public string instanceId { get; set; }
        [Value(2, HelpText = "The ID of the database where the sample database resides.",
            Required = true)]
        public string databaseId { get; set; }
        [Value(3, HelpText = "The type of insert to perform, 'players' or 'scores'.",
            Required = true)]
        public string insertType { get; set; }
    }

Fügen Sie als Nächstes unter der vorhandenen Methode CreateAsync die folgenden Methoden Insert, InsertPlayersAsync und InsertScoresAsync hinzu:

        public static object Insert(string projectId,
            string instanceId, string databaseId, string insertType)
        {
            if (insertType.ToLower() == "players")
            {
                var responseTask =
                    InsertPlayersAsync(projectId, instanceId, databaseId);
                Console.WriteLine("Waiting for insert players operation to complete...");
                responseTask.Wait();
                Console.WriteLine($"Operation status: {responseTask.Status}");
            }
            else if (insertType.ToLower() == "scores")
            {
                var responseTask =
                    InsertScoresAsync(projectId, instanceId, databaseId);
                Console.WriteLine("Waiting for insert scores operation to complete...");
                responseTask.Wait();
                Console.WriteLine($"Operation status: {responseTask.Status}");
            }
            else
            {
                Console.WriteLine("Invalid value for 'type of insert'. "
                    + "Specify 'players' or 'scores'.");
                return ExitCode.InvalidParameter;
            }
            Console.WriteLine($"Inserted {insertType} into sample database "
                + $"{databaseId} on instance {instanceId}");
            return ExitCode.Success;
        }

       public static async Task InsertPlayersAsync(string projectId,
            string instanceId, string databaseId)
        {
            string connectionString =
                $"Data Source=projects/{projectId}/instances/{instanceId}"
                + $"/databases/{databaseId}";

            long numberOfPlayers = 0;
            using (var connection = new SpannerConnection(connectionString))
            {
                await connection.OpenAsync();
                await connection.RunWithRetriableTransactionAsync(async (transaction) =>
                {
                    // Execute a SQL statement to get current number of records
                    // in the Players table to use as an incrementing value 
                    // for each PlayerName to be inserted.
                    var cmd = connection.CreateSelectCommand(
                        @"SELECT Count(PlayerId) as PlayerCount FROM Players");
                    numberOfPlayers = await cmd.ExecuteScalarAsync<long>();
                    // Insert 100 player records into the Players table.
                    SpannerBatchCommand cmdBatch = connection.CreateBatchDmlCommand();
                    for (int i = 0; i < 100; i++)
                    {
                        numberOfPlayers++;
                        SpannerCommand cmdInsert = connection.CreateDmlCommand(
                            "INSERT INTO Players "
                            + "(PlayerId, PlayerName) "
                            + "VALUES (@PlayerId, @PlayerName)",
                                new SpannerParameterCollection {
                                    {"PlayerId", SpannerDbType.Int64},
                                    {"PlayerName", SpannerDbType.String}});
                        cmdInsert.Parameters["PlayerId"].Value =
                            Math.Abs(Guid.NewGuid().GetHashCode());
                        cmdInsert.Parameters["PlayerName"].Value =
                            $"Player {numberOfPlayers}";
                        cmdBatch.Add(cmdInsert);
                    }
                    await cmdBatch.ExecuteNonQueryAsync();
                });
            }
            Console.WriteLine("Done inserting player records...");
        }

        public static async Task InsertScoresAsync(
            string projectId, string instanceId, string databaseId)
        {
            string connectionString =
            $"Data Source=projects/{projectId}/instances/{instanceId}"
            + $"/databases/{databaseId}";

            // Insert 4 score records into the Scores table for each player
            // in the Players table.
            using (var connection = new SpannerConnection(connectionString))
            {
                await connection.OpenAsync();
                await connection.RunWithRetriableTransactionAsync(async (transaction) =>
                {
                    Random r = new Random();
                    bool playerRecordsFound = false;
                    SpannerBatchCommand cmdBatch =
                                connection.CreateBatchDmlCommand();
                    var cmdLookup =
                    connection.CreateSelectCommand("SELECT * FROM Players");
                    using (var reader = await cmdLookup.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            playerRecordsFound = true;
                            for (int i = 0; i < 4; i++)
                            {
                                DateTime randomTimestamp = DateTime.Now
                                        .AddYears(r.Next(-2, 1))
                                        .AddMonths(r.Next(-12, 1))
                                        .AddDays(r.Next(-28, 0))
                                        .AddHours(r.Next(-24, 0))
                                        .AddSeconds(r.Next(-60, 0))
                                        .AddMilliseconds(r.Next(-100000, 0));
                                SpannerCommand cmdInsert =
                                connection.CreateDmlCommand(
                                    "INSERT INTO Scores "
                                    + "(PlayerId, Score, Timestamp) "
                                    + "VALUES (@PlayerId, @Score, @Timestamp)",
                                    new SpannerParameterCollection {
                                        {"PlayerId", SpannerDbType.Int64},
                                        {"Score", SpannerDbType.Int64},
                                        {"Timestamp",
                                            SpannerDbType.Timestamp}});
                                cmdInsert.Parameters["PlayerId"].Value =
                                    reader.GetFieldValue<int>("PlayerId");
                                cmdInsert.Parameters["Score"].Value =
                                    r.Next(1000, 1000001);
                                cmdInsert.Parameters["Timestamp"].Value =
                                    randomTimestamp.ToString("o");
                                cmdBatch.Add(cmdInsert);
                            }
                        }
                        if (!playerRecordsFound)
                        {
                            Console.WriteLine("Parameter 'scores' is invalid "
                            + "since no player records currently exist. First "
                            + "insert players then insert scores.");
                            Environment.Exit((int)ExitCode.InvalidParameter);
                        }
                        else
                        {
                            await cmdBatch.ExecuteNonQueryAsync();
                            Console.WriteLine(
                                "Done inserting score records..."
                            );
                        }
                    }
                });
            }
        }

Damit der insert-Befehl funktioniert, fügen Sie der Hauptseite Ihres Programms den folgenden Code hinzu: :

                .Add((InsertOptions opts) => Insert(
                    opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))

Mit der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step5 können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei Program.cs aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls insert hinzugefügt haben.

Führen Sie nun das Programm aus, um zu prüfen, ob der neue insert-Befehl in der Liste der möglichen Befehle des Programms enthalten ist. Führen Sie dazu diesen Befehl aus:

dotnet run

Der Befehl insert sollte jetzt in der Standardausgabe des Programms enthalten sein:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  No verb selected.

  create     Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project.

  insert     Insert sample 'players' records or 'scores' records into the database.

  help       Display more information on a specific command.

  version    Display version information.

Führen Sie nun den Befehl insert aus, um die Eingabeargumente aufzurufen. Geben Sie den folgenden Befehl ein.

dotnet run insert

Daraufhin sollte die folgende Antwort zurückgegeben werden:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  A required value not bound to option name is missing.

  --help          Display this help screen.

  --version       Display version information.

  value pos. 0    Required. The project ID of the project to use when managing Cloud Spanner resources.

  value pos. 1    Required. The ID of the instance where the sample database resides.

  value pos. 2    Required. The ID of the database where the sample database resides.

  value pos. 3    Required. The type of insert to perform, 'players' or 'scores'.

Sie können der Antwort entnehmen, dass neben der Projekt-ID, Instanz-ID und Datenbank-ID noch das Argument value pos. 3 erwartet wird, nämlich den „Typ der Einfügung“. die Sie ausführen können. Dieses Argument kann den Wert „players“ haben. oder "Punktzahlen".

Jetzt führen wir den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls create verwendet haben, und fügen „players“ hinzu. als zusätzlichen „Insert“-Typ . Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players

Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:

Waiting for insert players operation to complete...
Done inserting player records...
Operation status: RanToCompletion
Inserted players into sample database leaderboard on instance cloudspanner-leaderboard

Verwenden wir nun die C#-Clientbibliothek, um unsere Tabelle Scores mit vier Zufallswerten und Zeitstempeln für jeden Spieler in der Tabelle Players zu füllen.

Die Spalte Timestamp der Tabelle „Scores“ wurde als „Commit-Zeitstempel“ definiert Spalte über die folgende SQL-Anweisung, die ausgeführt wurde, als wir zuvor den Befehl create ausgeführt haben:

CREATE TABLE Scores(
  PlayerId INT64 NOT NULL,
  Score INT64 NOT NULL,
  Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
    INTERLEAVE IN PARENT Players ON DELETE NO ACTION

Beachten Sie das Attribut OPTIONS(allow_commit_timestamp=true). Dadurch wird Timestamp zu einem „Commit-Zeitstempel“ und kann automatisch mit dem genauen Transaktionszeitstempel für INSERT- und UPDATE-Vorgänge in einer bestimmten Tabellenzeile gefüllt werden.

Sie können auch Ihre eigenen Zeitstempelwerte in einen Commit-Zeitstempel einfügen solange Sie einen Zeitstempel mit einem Wert in der Vergangenheit einfügen. In diesem Codelab machen wir das.

Führen wir nun den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des create-Befehls verwendet haben, wobei wir „scores“ hinzufügen. als zusätzlichen „Insert“-Typ . Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores

Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:

Waiting for insert players operation to complete...
Done inserting player records...
Operation status: RanToCompletion
Inserted players into sample database leaderboard on instance cloudspanner-leaderboard

insert mit dem „type of insert“ ausführen , das als scores angegeben ist, ruft die Methode InsertScoresAsync auf, die die folgenden Code-Snippets verwendet, um einen zufällig generierten Zeitstempel mit einem Datum und einer Uhrzeit einzufügen, die in der Vergangenheit liegen:

DateTime randomTimestamp = DateTime.Now
    .AddYears(r.Next(-2, 1))
    .AddMonths(r.Next(-12, 1))
    .AddDays(r.Next(-28, 0))
    .AddHours(r.Next(-24, 0))
    .AddSeconds(r.Next(-60, 0))
    .AddMilliseconds(r.Next(-100000, 0));
...
 cmdInsert.Parameters["Timestamp"].Value = randomTimestamp.ToString("o");

Um die Spalte Timestamp automatisch mit dem Zeitstempel zu füllen, der genau dem Zeitpunkt entspricht, an dem das Transaktion stattfindet, können Sie stattdessen die C#-Konstante SpannerParameter.CommitTimestamp wie im folgenden Code-Snippet einfügen:

cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;

Nachdem die Daten geladen wurden, überprüfen wir die Werte, die wir gerade in unsere neuen Tabellen geschrieben haben. Wählen Sie zuerst die Datenbank leaderboard und dann die Tabelle Players aus. Klicken Sie auf den Tab Data. Sie sollten sehen, dass sich in den Spalten PlayerId und PlayerName der Tabelle Daten befinden.

7bc2c96293c31c49.png

Prüfen Sie als Nächstes, ob die Tabelle „Scores“ auch Daten enthält. Klicken Sie dazu auf die Tabelle Scores und wählen Sie den Tab Data aus. Die Spalten PlayerId, Timestamp und Score der Tabelle sollten Daten enthalten.

d8a4ee4f13244c19.png

Gut gemacht! Wir möchten unser Programm aktualisieren und einige Abfragen ausführen, mit denen wir eine Gaming-Bestenliste erstellen können.

6. Abfragen zu Bestenlisten ausführen

Nachdem wir unsere Datenbank eingerichtet und Informationen in unsere Tabellen geladen haben, erstellen wir nun mit diesen Daten eine Bestenliste. Dazu müssen wir die folgenden vier Fragen beantworten:

  1. Welche Spieler sind die "Top Ten"? aller Zeiten?
  2. Welche Spieler sind die "Top Ten"? des Jahres?
  3. Welche Spieler sind die "Top Ten"? des Monats?
  4. Welche Spieler sind die "Top Ten"? der Woche?

Lassen Sie uns unser Programm aktualisieren, um die SQL-Abfragen auszuführen, die diese Fragen beantworten.

Wir fügen einen query-Befehl hinzu, der eine Möglichkeit bietet, die Abfragen auszuführen, um die Fragen zu beantworten, die die für unsere Bestenliste erforderlichen Informationen liefern.

Bearbeiten Sie die Datei Program.cs im Cloud Shell-Editor, um dem Programm einen query-Befehl hinzuzufügen.

Fügen Sie zuerst einen neuen query-Befehlsblock in die Verbmap ein am Anfang des Programms unter dem vorhandenen insert-Befehlsblock:

    [Verb("query", HelpText = "Query players with 'Top Ten' scores within a specific timespan "
        + "from sample Cloud Spanner database table.")]
    class QueryOptions
    {
        [Value(0, HelpText = "The project ID of the project to use "
            + "when managing Cloud Spanner resources.", Required = true)]
        public string projectId { get; set; }
        [Value(1, HelpText = "The ID of the instance where the sample data resides.",
            Required = true)]
        public string instanceId { get; set; }
        [Value(2, HelpText = "The ID of the database where the sample data resides.",
            Required = true)]
        public string databaseId { get; set; }
        [Value(3, Default = 0, HelpText = "The timespan in hours that will be used to filter the "
            + "results based on a record's timestamp. The default will return the "
            + "'Top Ten' scores of all time.")]
        public int timespan { get; set; }
    }

Fügen Sie als Nächstes unter der vorhandenen Methode InsertScoresAsync die folgenden Methoden Query und QueryAsync hinzu:

public static object Query(string projectId,
            string instanceId, string databaseId, int timespan)
        {
            var response = QueryAsync(
                projectId, instanceId, databaseId, timespan);
            response.Wait();
            return ExitCode.Success;
        }        

public static async Task QueryAsync(
            string projectId, string instanceId, string databaseId, int timespan)
        {
            string connectionString =
            $"Data Source=projects/{projectId}/instances/"
            + $"{instanceId}/databases/{databaseId}";
            // Create connection to Cloud Spanner.
            using (var connection = new SpannerConnection(connectionString))
            {
                string sqlCommand;
                if (timespan == 0)
                {
                    // No timespan specified. Query Top Ten scores of all time.
                    sqlCommand =
                        @"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
                            FROM Players p
                            JOIN Scores s ON p.PlayerId = s.PlayerId
                            ORDER BY s.Score DESC LIMIT 10";
                }
                else
                {
                    // Query Top Ten scores filtered by the timepan specified.
                    sqlCommand =
                        $@"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
                            FROM Players p
                            JOIN Scores s ON p.PlayerId = s.PlayerId
                            WHERE s.Timestamp >
                            TIMESTAMP_SUB(CURRENT_TIMESTAMP(),
                                INTERVAL {timespan.ToString()} HOUR)
                            ORDER BY s.Score DESC LIMIT 10";
                }
                var cmd = connection.CreateSelectCommand(sqlCommand);
                using (var reader = await cmd.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        Console.WriteLine("PlayerId : "
                          + reader.GetFieldValue<string>("PlayerId")
                          + " PlayerName : "
                          + reader.GetFieldValue<string>("PlayerName")
                          + " Score : "
                          + string.Format("{0:n0}",
                            Int64.Parse(reader.GetFieldValue<string>("Score")))
                          + " Timestamp : "
                          + reader.GetFieldValue<string>("Timestamp").Substring(0, 10));
                    }
                }
            }
        }

Damit der query-Befehl funktioniert, fügen Sie der Hauptseite Ihres Programms den folgenden Code hinzu: :

                .Add((QueryOptions opts) => Query(
                    opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))

Mit der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step6 können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei Program.cs aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls query hinzugefügt haben.

Führen Sie nun das Programm aus, um zu prüfen, ob der neue query-Befehl in der Liste der möglichen Befehle des Programms enthalten ist. Führen Sie dazu diesen Befehl aus:

dotnet run

Der Befehl query sollte jetzt in der Standardausgabe des Programms als neue Befehlsoption enthalten sein:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  No verb selected.

  create     Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project.

  insert     Insert sample 'players' records or 'scores' records into the database.

  query      Query players with 'Top Ten' scores within a specific timespan from sample Cloud Spanner database table.

  help       Display more information on a specific command.

  version    Display version information.

Führen Sie nun den Befehl query aus, um die Eingabeargumente aufzurufen. Geben Sie den folgenden Befehl ein:

dotnet run query

Dadurch wird die folgende Antwort zurückgegeben:

Leaderboard 1.0.0
Copyright (C) 2018 Leaderboard

ERROR(S):
  A required value not bound to option name is missing.

  --help          Display this help screen.

  --version       Display version information.

  value pos. 0    Required. The project ID of the project to use when managing Cloud Spanner resources.

  value pos. 1    Required. The ID of the instance where the sample data resides.

  value pos. 2    Required. The ID of the database where the sample data resides.

  value pos. 3    (Default: 0) The timespan in hours that will be used to filter the results based on a record's timestamp. The default will return the 'Top Ten' scores of all time.

Sie können der Antwort entnehmen, dass neben der Projekt-ID, Instanz-ID und Datenbank-ID auch das Argument value pos. 3 erwartet wird, mit dem wir eine Zeitspanne in Stunden angeben können, die zum Filtern von Datensätzen basierend auf ihrem Wert in der Spalte Timestamp der Tabelle Scores verwendet werden soll. Dieses Argument hat den Standardwert 0, was bedeutet, dass keine Datensätze nach Zeitstempeln gefiltert werden. Wir können also den Befehl query ohne „timespan“ verwenden. um eine Liste der Top Ten zu erhalten, aller Zeiten.

Lassen Sie uns den Befehl query ohne Angabe einer Zeitspanne ausführen. Verwenden Sie dabei dieselben Argumentwerte, die wir bei der Ausführung des Befehls create verwendet haben. Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard

Sie sollten nun eine Antwort sehen, die die Top Ten enthält. wie die folgenden:

PlayerId : 1843159180 PlayerName : Player 87 Score : 998,955 Timestamp : 2016-03-23
PlayerId : 61891198 PlayerName : Player 19 Score : 998,720 Timestamp : 2016-03-26
PlayerId : 340906298 PlayerName : Player 48 Score : 993,302 Timestamp : 2015-08-27
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 857460496 PlayerName : Player 68 Score : 988,010 Timestamp : 2015-05-25
PlayerId : 1826646419 PlayerName : Player 91 Score : 984,022 Timestamp : 2016-11-26
PlayerId : 1002199735 PlayerName : Player 35 Score : 982,933 Timestamp : 2015-09-26
PlayerId : 2002563755 PlayerName : Player 23 Score : 979,041 Timestamp : 2016-10-25
PlayerId : 1377548191 PlayerName : Player 2 Score : 978,632 Timestamp : 2016-05-02
PlayerId : 1358098565 PlayerName : Player 65 Score : 973,257 Timestamp : 2016-10-30

Führen wir nun den Befehl query mit den erforderlichen Argumenten aus, um die Top Ten abzufragen. Spieler des Jahres durch Angabe einer „Zeitspanne“ entspricht der Anzahl der Stunden in einem Jahr, also 8.760. Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760

Sie sollten nun eine Antwort sehen, die die Top Ten enthält. Spieler des Jahres:

PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 228469898 PlayerName : Player 82 Score : 967,177 Timestamp : 2018-01-26
PlayerId : 1131343000 PlayerName : Player 26 Score : 944,725 Timestamp : 2017-05-26
PlayerId : 396780730 PlayerName : Player 41 Score : 929,455 Timestamp : 2017-09-26
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 634269851 PlayerName : Player 54 Score : 909,379 Timestamp : 2017-07-24
PlayerId : 821111159 PlayerName : Player 55 Score : 908,402 Timestamp : 2017-05-25
PlayerId : 228469898 PlayerName : Player 82 Score : 889,040 Timestamp : 2017-12-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 874,124 Timestamp : 2017-09-24
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24

Führen wir nun den Befehl query aus, um die Top Ten abzufragen. Spieler des Monats durch Angabe einer Zeitspanne der Anzahl der Stunden pro Monat, also 730. Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730

Sie sollten nun eine Antwort sehen, die die Top Ten enthält. Spieler des Monats:

PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 70762849 PlayerName : Player 77 Score : 598,074 Timestamp : 2018-04-22
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28
PlayerId : 1208850523 PlayerName : Player 21 Score : 216,008 Timestamp : 2018-04-30
PlayerId : 1587692674 PlayerName : Player 63 Score : 188,157 Timestamp : 2018-04-25
PlayerId : 992391797 PlayerName : Player 37 Score : 167,175 Timestamp : 2018-04-30

Führen wir nun den Befehl query aus, um die Top Ten abzufragen. der Woche durch Angabe einer „Zeitraum“-Zeitspanne der Anzahl der Stunden pro Woche (168). Ersetzen Sie PROJECT_ID durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168

Sie sollten nun eine Antwort sehen, die die Top Ten enthält. der Woche:

PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 228469898 PlayerName : Player 82 Score : 853,602 Timestamp : 2018-04-28
PlayerId : 1131343000 PlayerName : Player 26 Score : 695,318 Timestamp : 2018-04-30
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 671,827 Timestamp : 2018-04-27
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 816861444 PlayerName : Player 83 Score : 622,277 Timestamp : 2018-04-27
PlayerId : 162043954 PlayerName : Player 75 Score : 572,634 Timestamp : 2018-05-02
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28

Super!

Wenn Sie jetzt Einträge hinzufügen, skaliert Spanner Ihre Datenbank auf eine beliebige Größe.

Unabhängig davon, wie stark Ihre Datenbank wächst, kann die Bestenliste Ihres Spiels mit Spanner und seiner Truetime-Technologie weiterhin präzise skaliert werden.

7. Bereinigen

Nach so viel Spaß beim Spielen mit Spanner müssen wir unseren Spielplatz aufräumen und dabei kostbare Ressourcen und Geld sparen. Dies ist ein einfacher Schritt. Rufen Sie einfach die Developer Console auf und löschen Sie die Instanz, die Sie im Codelab-Schritt "Setup a Cloud Spanner Instance" (Cloud Spanner-Instanz einrichten) erstellt haben.

8. Glückwunsch!

Behandelte Themen:

  • Google Cloud Spanner-Instanzen, -Datenbanken und -Tabellenschema für eine Bestenliste
  • .NET Core-C#-Konsolenanwendung erstellen
  • Spanner-Datenbank und -Tabellen mit der C#-Clientbibliothek erstellen
  • Daten mithilfe der C#-Clientbibliothek in eine Spanner-Datenbank laden
  • Abfrage der „Top Ten“ Ergebnisse aus Ihren Daten mithilfe von Spanner-Commit-Zeitstempeln und der C#-Clientbibliothek

Vorgehensweise:

Feedback geben

  • Bitte nehmen Sie sich einen Moment Zeit, um an unserer kurzen Umfrage teilzunehmen.