Cloud Spanner: crie um placar de jogos com C #

O Google Cloud Spanner é um serviço de banco de dados relacional totalmente gerenciado horizontalmente escalonável e distribuído globalmente que fornece transações ACID e semântica SQL sem abrir mão do desempenho e da alta disponibilidade.

Neste laboratório, você aprenderá a configurar uma instância do Cloud Spanner. Você seguirá as etapas de criação de um banco de dados e esquema que pode ser usado para um placar de jogo. Você começará criando uma tabela de jogadores para armazenar as informações dos jogadores e uma tabela de pontuação para armazenar as pontuações dos jogadores.

Em seguida, você preencherá as tabelas com dados de amostra. Em seguida, você concluirá o laboratório executando algumas das dez principais consultas de amostra e, por fim, excluindo a instância para liberar recursos.

O que você aprenderá

  • Como configurar uma instância do Cloud Spanner.
  • Como criar um banco de dados e tabelas.
  • Como usar uma coluna de carimbo de data / hora de commit.
  • Como carregar dados em sua tabela de banco de dados Cloud Spanner com carimbos de data / hora.
  • Como consultar seu banco de dados Cloud Spanner.
  • Como excluir sua instância do Cloud Spanner.

O que você vai precisar

Como você usará este tutorial?

Leia apenas na íntegra Leia e complete os exercícios

Como você avaliaria sua experiência com o Google Cloud Platform?

Novato Intermediário Proficiente

Configuração de ambiente individualizada

Se você ainda não tem uma Conta do Google (Gmail ou Google Apps), deve criar uma . Faça login no console do Google Cloud Platform ( console.cloud.google.com ) e crie um novo projeto.

Se você já tem um projeto, clique no menu suspenso de seleção de projeto no canto superior esquerdo do console:

6c9406d9b014760.png

e clique no botão 'NOVO PROJETO' na caixa de diálogo resultante para criar um novo projeto:

f708315ae07353d0.png

Se você ainda não tem um projeto, deverá ver uma caixa de diálogo como esta para criar o seu primeiro:

870a3cbd6541ee86.png

A caixa de diálogo de criação de projeto subsequente permite que você insira os detalhes de seu novo projeto:

6a92c57d3250a4b3.png

Lembre-se do ID do projeto, que é um nome exclusivo em todos os projetos do Google Cloud (o nome acima já foi usado e não funcionará para você, desculpe!). Ele será referido posteriormente neste codelab como PROJECT_ID .

Em seguida, se ainda não tiver feito isso, você precisará ativar o faturamento no Developers Console para usar os recursos do Google Cloud e ativar a API Cloud Spanner .

15d0ef27a8fbab27.png

Executar este codelab não deve custar mais do que alguns dólares, mas pode custar mais se você decidir usar mais recursos ou se deixá-los em execução (consulte a seção "limpeza" no final deste documento). Os preços do Google Cloud Spanner estão documentados aqui .

Novos usuários do Google Cloud Platform estão qualificados para uma avaliação gratuita de US $ 300 , o que deve tornar este codelab totalmente gratuito.

Configuração do Google Cloud Shell

Embora o Google Cloud e o Spanner possam ser operados remotamente de seu laptop, neste codelab usaremos o Google Cloud Shell , um ambiente de linha de comando executado na nuvem.

Esta máquina virtual baseada em Debian é carregada com todas as ferramentas de desenvolvimento de que você precisa. Ele oferece um diretório inicial persistente de 5 GB e é executado no Google Cloud, melhorando muito o desempenho e a autenticação da rede. Isso significa que tudo que você precisa para este codelab é um navegador (sim, ele funciona em um Chromebook).

  1. Para ativar o Cloud Shell no Cloud Console, basta clicar em Ativar Cloud Shell gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVjS3ZHK39edOlK3Juaq_goebOlK2Juaqb0gdOlK3Juaqb0gOlK3JuaqdOlK3JuaqdOlK3JuaqdOlK3JuaqdOlK3JuaqdOg3AxOg3JuaqdOlK3JuaqdOg3AxOg3Juaqb0gdOg3AxOg3JuaqdOg3AxOg3JuaqdOg3AxOg3JuaqdOg3AxOg3JuaqdS0g3Ax3Aq. (deve levar apenas alguns minutos para provisionar e conectar ao ambiente).

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjkvI-24Geffi6kIvI-24Ge-NtjkvI-24Ge78Ge-NtjkvuEx

Captura de tela 14-06-2017 às 13.10.43 PM.png

Depois de conectado ao Cloud Shell, você verá que já está autenticado e que o projeto já está definido para seu PROJECT_ID .

gcloud auth list

Saída de comando

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

Saída de comando

[core]
project = <PROJECT_ID>

Se, por algum motivo, o projeto não estiver definido, basta emitir o seguinte comando:

gcloud config set project <PROJECT_ID>

Procurando seu PROJECT_ID ? Verifique qual ID você usou nas etapas de configuração ou procure-o no painel do Console do Cloud:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

O Cloud Shell também define algumas variáveis ​​de ambiente por padrão, o que pode ser útil ao executar comandos futuros.

echo $GOOGLE_CLOUD_PROJECT

Saída de comando

<PROJECT_ID>
  1. Finalmente, defina a zona padrão e a configuração do projeto.
gcloud config set compute/zone us-central1-f

Você pode escolher uma variedade de zonas diferentes. Para obter mais informações, consulte Regiões e zonas .

Resumo

Nesta etapa, você configura seu ambiente.

Próximo

Em seguida, você configurará uma instância do Cloud Spanner.

Nesta etapa, configuramos nossa instância do Cloud Spanner para este codelab. Procure a entrada do Spanner 1a6580bd3d3e6783.png no menu de hambúrguer superior esquerdo 3129589f7bc9e5ce.png ou pesquise por Spanner pressionando "/" e digite "Spanner"

36e52f8df8e13b99.png

Em seguida, clique em 95269e75bc8c3e4d.png e preencha o formulário inserindo o nome da instância cloudspanner-leaderboard para sua instância, escolhendo uma configuração (selecione uma instância regional) e defina o número de nós, para este codelab precisaremos apenas de 1 nó. Para instâncias de produção e para se qualificar para o SLA do Cloud Spanner, você precisará executar 3 ou mais nós em sua instância do Cloud Spanner.

Por último, mas não menos importante, clique em "Criar" e em segundos você terá uma instância do Cloud Spanner à sua disposição.

dceb68e9ed3801e8.png

Na próxima etapa, usaremos a biblioteca cliente C # para criar um banco de dados e esquema em nossa nova instância.

Nesta etapa, criaremos nosso banco de dados e esquema de amostra.

Vamos usar a biblioteca cliente C # para criar duas tabelas; uma tabela de jogadores para informações do jogador e uma tabela de pontuação para armazenar as pontuações dos jogadores. Para fazer isso, percorreremos as etapas de criação de um aplicativo de console C # no Cloud Shell.

Primeiro, clone o código de amostra para este codelab do Github digitando o seguinte comando no Cloud Shell:

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

Em seguida, mude o diretório para o diretório "aplicativos" onde você criará seu aplicativo.

cd dotnet-docs-samples/applications/

Todo o código necessário para este codelab está localizado no diretório dotnet-docs-samples/applications/leaderboard como um aplicativo C # executável chamado Leaderboard para servir como referência conforme você avança no codelab. Vamos criar um novo diretório e construir uma cópia do aplicativo Leaderboard em etapas.

Crie um novo diretório chamado "codelab" para o aplicativo e mude de diretório para ele com o seguinte comando:

mkdir codelab && cd $_

Crie um novo aplicativo de console .NET C # denominado "Leaderboard" usando o seguinte comando:

dotnet new console -n Leaderboard

Este comando cria um aplicativo de console simples que consiste em dois arquivos principais, o arquivo de projeto Leaderboard.csproj e o arquivo de programa Program.cs .

Vamos ver. Mude o diretório para o diretório Leaderboard recém-criado onde o aplicativo reside:

cd Leaderboard

Em seguida, digite o seguinte comando para executá-lo.

dotnet run

Você deve ver a saída do aplicativo "Hello World!".

Agora vamos atualizar nosso aplicativo de console editando Program.cs para usar a biblioteca de cliente C # Spanner para criar um placar que consiste em duas tabelas Jogadores e Pontuações. Você pode fazer isso diretamente no Editor do Cloud Shell:

Abra o Editor do Cloud Shell, clicando no ícone destacado abaixo:

73cf70e05f653ca.png

Em seguida, abra o arquivo Program.cs no editor do Cloud Shell e substitua o código existente do arquivo pelo código necessário para criar o banco de dados do leaderboard e as tabelas Players e Scores colando o seguinte código do aplicativo C # no arquivo Program.cs :

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);
        }
    }
}

Para fornecer uma imagem mais clara do código do Programa, aqui está um diagrama do Programa com seus principais componentes rotulados:

b70b1b988ea3ac8a.png

Você pode usar o arquivo Program.cs no diretório dotnet-docs-samples/applications/leaderboard/step4 para ver um exemplo de como seu arquivo Program.cs deve ficar depois de adicionar o código para habilitar o comando create .

Em seguida, use o editor do Cloud Shell para abrir e editar o arquivo de projeto Leaderboard.csproj , atualizando-o para se parecer com o código a seguir. Certifique-se de salvar todas as alterações usando o menu "Arquivo" do editor do Cloud Shell.

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

Essa mudança adicionou uma referência ao pacote C # Spanner Nuget Google.Cloud.Spanner.Data que precisamos para interagir com a API Cloud Spanner. Essa alteração também adiciona uma referência ao projeto CommandLineUtil que faz parte do repositório Github dotnet-doc-samples e fornece uma extensão "verbmap" útil para o CommandLineParser código aberto; uma biblioteca útil para lidar com entrada de linha de comando para aplicativos de console.

Você pode usar o arquivo Leaderboard.csproj no diretório dotnet-docs-samples/applications/leaderboard/step4 para ver um exemplo de como o arquivo Leaderboard.csproj deve ficar depois de adicionar o código para habilitar o comando de create .

Agora você está pronto para executar sua amostra atualizada. Digite o seguinte para ver a resposta padrão do seu aplicativo atualizado:

dotnet run

Você deve ver uma saída como a seguinte:

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.

A partir dessa resposta, podemos ver que este é o aplicativo Leaderboard que pode ser executado com um dos três comandos possíveis: create , help e version .

Vamos tentar a create comando para criar um banco de dados Spanner e mesas. Execute o comando sem argumentos para ver os argumentos esperados do comando.

dotnet run create

Você deve ver uma resposta como a seguinte:

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.

Aqui, podemos ver que os argumentos esperados do comando de create são ID do projeto, ID da instância e ID do banco de dados.

Agora execute o seguinte comando. Certifique-se de substituir PROJECT_ID pelo ID do projeto que você criou no início deste codelab.

dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard

Após alguns segundos, você deverá ver uma resposta como a seguinte:

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

Na seção Cloud Spanner do Cloud Console, você deve ver seu novo banco de dados e tabelas surgindo no menu do lado esquerdo.

ba9008bb84cb90b0.png

Na próxima etapa, atualizaremos nosso aplicativo para carregar alguns dados em seu novo banco de dados.

Agora temos um banco de dados chamado leaderboard contendo duas tabelas; Players e Scores . Agora vamos usar a biblioteca # cliente C para preencher a nossa Players mesa com jogadores e nossa Scores tabela com contagens aleatórias para cada jogador.

Abra o Editor do Cloud Shell, clicando no ícone destacado abaixo:

4d17840699d8e7ce.png

Em seguida, edite o arquivo Program.cs no Editor do Cloud Shell para adicionar um comando de insert que pode ser usado para inserir 100 jogadores na tabela de Players ou pode ser usado para inserir 4 pontuações aleatórias na tabela de Scores para cada jogador na tabela de Players tabela.

Primeiro adicione um novo bloco de comando de insert no "Mapa de Verbos" no topo do Programa abaixo do bloco de comando de create existente:

[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; }
    }

Em seguida, adicione os seguintes métodos Insert , InsertPlayersAsync e InsertScoresAsync abaixo do método CreateAsync existente:

        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..."
                            );
                        }
                    }
                });
            }
        }

Em seguida, para tornar o comando de insert funcional, adicione o seguinte código ao método "Principal" do seu Programa:

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

Você pode usar o arquivo Program.cs no diretório dotnet-docs-samples/applications/leaderboard/step5 para ver um exemplo de como seu arquivo Program.cs deve ficar depois de adicionar o código para habilitar o comando insert .

Agora, vamos executar o programa para confirmar se o novo comando de insert está incluído na lista de comandos possíveis do programa. Execute o seguinte comando:

dotnet run

Você deve ver o comando insert agora incluído na saída padrão do programa:

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.

Agora vamos executar o comando insert para ver seus argumentos de entrada. Digite o seguinte comando.

dotnet run insert

Isso deve retornar a seguinte resposta:

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'.

Você pode ver na resposta que, além do ID do projeto, ID da instância e ID do banco de dados, há outro value pos. 3 argumento value pos. 3 esperado, qual é o "tipo de inserção" a ser executado. Este argumento pode ter um valor de 'jogadores' ou 'pontuação'.

Agora vamos executar o comando insert com os mesmos valores de argumento que usamos quando chamamos o comando create , adicionando "players" como o argumento "type of insert" adicional. Certifique-se de substituir PROJECT_ID pelo ID do projeto que você criou no início deste codelab.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players

Após alguns segundos, você deverá ver uma resposta como a seguinte:

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

Agora vamos usar a biblioteca cliente C # para preencher nossa tabela de Scores com quatro pontuações aleatórias, juntamente com carimbos de data / hora para cada jogador na tabela de Players .

A coluna Timestamp da tabela Scores foi definida como uma coluna "commit timestamp" por meio da seguinte instrução SQL que foi executada quando executamos anteriormente o comando create :

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

Observe o atributo OPTIONS(allow_commit_timestamp=true) . Isso torna o Timestamp data e hora uma coluna de "registro de data e hora de confirmação" e permite que seja preenchido automaticamente com o registro de data e hora exato da transação para as operações INSERT e UPDATE em uma determinada linha da tabela.

Você também pode inserir seus próprios valores de carimbo de data / hora em uma coluna "carimbo de data / hora de commit", desde que insira um carimbo de data / hora com um valor que está no passado, que é o que faremos para o propósito deste codelab.

Agora vamos executar o comando insert com os mesmos valores de argumento que usamos quando chamamos o comando create adicionando "scores" como o argumento "type of insert" adicional. Certifique-se de substituir PROJECT_ID pelo ID do projeto que você criou no início deste codelab.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores

Após alguns segundos, você deverá ver uma resposta como a seguinte:

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

Executar a insert com o "tipo de inserção" especificado como scores chama o método InsertScoresAsync , que usa os seguintes trechos de código para inserir um carimbo de data / hora gerado aleatoriamente com uma data e hora ocorrendo no passado:

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");

Para preencher automaticamente a coluna Timestamp com o timestamp de exatamente quando a transação "Inserir" ocorre, você pode inserir a constante C # SpannerParameter.CommitTimestamp como no seguinte snippet de código:

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

Agora que completamos o carregamento de dados, vamos verificar os valores que acabamos de gravar em nossas novas tabelas. Primeiro selecione o banco de dados da leaderboard e, em seguida, selecione a mesa de Players . Clique na guia Data . Você deve ver que você tem dados em da tabela PlayerId e PlayerName colunas.

7bc2c96293c31c49.png

A seguir, vamos verificar se a tabela Pontuações também contém dados, clicando na tabela Scores e selecionando a guia Data . Você deve ver que há dados nas colunas PlayerId , Timestamp e Score da tabela.

d8a4ee4f13244c19.png

Bem feito! Vamos atualizar nosso programa para executar algumas consultas que podemos usar para criar um placar de jogos.

Agora que configuramos nosso banco de dados e carregamos as informações em nossas tabelas, vamos criar um placar usando esses dados. Para fazer isso, precisamos responder às seguintes quatro perguntas:

  1. Quais jogadores são os "dez melhores" de todos os tempos?
  2. Quais jogadores são os "dez primeiros" do ano?
  3. Quais jogadores são os "dez primeiros" do mês?
  4. Quais jogadores são os "dez primeiros" da semana?

Vamos atualizar nosso programa para executar as consultas SQL que responderão a essas perguntas.

Adicionaremos um comando de query que fornecerá uma maneira de executar as consultas para responder às perguntas que produzirão as informações necessárias para nosso placar.

Edite o arquivo Program.cs no Editor do Cloud Shell para atualizar o Programa e adicionar um comando de query .

Primeiro, adicione um novo bloco de comando de query no "Mapa de Verbos" no topo do Programa abaixo do bloco de comando de insert existente:

    [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; }
    }

Em seguida, adicione os seguintes métodos Query e QueryAsync abaixo do método InsertScoresAsync existente:

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));
                    }
                }
            }
        }

Em seguida, para tornar o comando de query funcional, adicione o seguinte código ao método "Principal" do seu Programa:

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

Você pode usar o arquivo Program.cs no diretório dotnet-docs-samples/applications/leaderboard/step6 para ver um exemplo de como seu arquivo Program.cs deve ficar depois de adicionar o código para habilitar o comando query .

Agora, vamos executar o programa para confirmar se o novo comando de query está incluído na lista de comandos possíveis do programa. Execute o seguinte comando:

dotnet run

Você deve ver o comando query agora incluído na saída padrão do programa como uma nova opção de comando:

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.

Agora vamos executar o comando de query para ver seus argumentos de entrada. Digite o seguinte comando:

dotnet run query

Isso retornará a seguinte resposta:

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.

Você pode ver na resposta que, além do ID do projeto, ID da instância e ID do banco de dados, há outro value pos. 3 argumento value pos. 3 esperado, o que nos permite especificar um intervalo de tempo em número de horas a ser usado para filtrar registros com base em seu valor na coluna Timestamp da tabela Scores . Este argumento tem um valor padrão de 0, o que significa que nenhum registro será filtrado por carimbos de data / hora. Portanto, podemos usar o comando query sem um valor de "intervalo de tempo" para obter uma lista de nossos "Dez Melhores" jogadores de todos os tempos.

Vamos executar o comando de query sem especificar um "intervalo de tempo", usando os mesmos valores de argumento que usamos quando executamos o comando de create . Certifique-se de substituir PROJECT_ID pelo ID do projeto que você criou no início deste codelab.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard

Você deve ver uma resposta que inclui os "Dez melhores" jogadores de todos os tempos, como a seguir:

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

Agora vamos executar o comando de query com os argumentos necessários para consultar os "Dez melhores" jogadores do ano, especificando um "intervalo de tempo" igual ao número de horas em um ano que é 8760. Certifique-se de substituir PROJECT_ID pelo ID do projeto que você criado no início deste codelab.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760

Você deverá ver uma resposta que inclui os "Dez melhores" jogadores do ano, como a seguir:

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

Agora vamos executar a query comando para consultar os jogadores "Top Ten" do mês, especificando um "período de tempo" igual ao número de horas em um mês, que é 730. Certifique-se de substituir PROJECT_ID com o ID do projeto que criou no início deste codelab.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730

Você deverá ver uma resposta que inclui os "Dez melhores" jogadores do mês, como a seguir:

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

Agora vamos executar a query comando para consultar os jogadores "Top Ten" da semana, especificando um "período de tempo" igual ao número de horas em uma semana que é 168. Certifique-se de substituir PROJECT_ID com o ID do projeto que criou no início deste codelab.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168

Você deverá ver uma resposta que inclui os "Dez melhores" jogadores da semana, como a seguir:

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

Excelente trabalho!

Agora, à medida que você adiciona registros, o Spanner dimensionará seu banco de dados para o tamanho que você precisar.

Não importa o quanto seu banco de dados cresça, a tabela de classificação do seu jogo pode continuar a crescer com precisão com o Spanner e sua tecnologia Truetime.

Depois de toda a diversão brincando com o Spanner, precisamos limpar nosso playground, economizando recursos e dinheiro preciosos. Felizmente, esta é uma etapa fácil, basta acessar o console do desenvolvedor e excluir a instância que criamos na etapa do codelab chamada "Configurar uma instância do Cloud Spanner".

O que cobrimos:

  • Instâncias, bancos de dados e esquema de tabela do Google Cloud Spanner para um placar
  • Como criar o aplicativo de console .NET Core C #
  • Como criar um banco de dados e tabelas do Spanner usando a biblioteca cliente C #
  • Como carregar dados em um banco de dados Spanner usando a biblioteca cliente C #
  • Como consultar os resultados dos "Dez principais" de seus dados usando carimbos de data / hora de commit do Spanner e a biblioteca cliente C #

Próximos passos:

Dê-nos a sua opinião

  • Por favor, dedique um momento para preencher nossa breve pesquisa