Cloud Spanner: C# ile oyun skor tablosu oluşturma

1. Genel Bakış

Google Cloud Spanner, performanstan ve yüksek kullanılabilirlikten ödün vermeden ACID işlemleri ve SQL semantiği sağlayan, tümüyle yönetilen, yatay olarak ölçeklenebilir, küresel olarak dağıtılmış bir ilişkisel veritabanı hizmetidir.

Bu laboratuvarda, Cloud Spanner örneği oluşturmayı öğreneceksiniz. Oyun skor tablosu için kullanılabilecek bir veritabanı ve şema oluşturma adımlarını inceleyeceksiniz. Oyuncu bilgilerini saklamak için bir Oyuncular tablosu, oyuncu skorlarını saklamak için bir Skor tablosu oluşturarak başlayın.

Ardından tabloları örnek verilerle dolduracaksınız. Ardından, kaynakları boşa çıkarmak için en iyi on örnek sorguyu çalıştırarak ve son olarak da örneği silerek laboratuvarı tamamlayacaksınız.

Neler öğreneceksiniz?

  • Cloud Spanner örneği oluşturma.
  • Veritabanı ve tablo oluşturma
  • Kaydetme zaman damgası sütunu nasıl kullanılır?
  • Cloud Spanner veritabanı tablonuza zaman damgalarıyla veri yükleme.
  • Cloud Spanner veritabanınızı sorgulama.
  • Cloud Spanner örneğinizi silme.

İhtiyacınız olanlar

Bu eğiticiden nasıl yararlanacaksınız?

Yalnızca okuma Okuyun ve alıştırmaları tamamlayın

Google Cloud Platform deneyiminizi nasıl değerlendirirsiniz?

Acemi Orta Yeterli

2. Kurulum ve Gereksinimler

Kendi hızınızda ortam kurulumu

Google Hesabınız (Gmail veya Google Apps) yoksa bir hesap oluşturmanız gerekir. Google Cloud Platform konsolunda ( console.cloud.google.com) oturum açın ve yeni bir proje oluşturun.

Zaten bir projeniz varsa konsolun sol üst köşesindeki proje seçimi açılan menüsünü tıklayın:

6c9406d9b014760.png

Sonra ‘YENİ PROJE’yi tıklayın. düğmesini tıklayın:

f708315ae07353d0.png

Henüz projeniz yoksa ilk projenizi oluşturmak için şuna benzer bir iletişim kutusu görmeniz gerekir:

870a3cbd6541ee86.png

Sonraki proje oluşturma iletişim kutusu yeni projenizin ayrıntılarını girmenize olanak tanır:

6a92c57d3250a4b3.png

Tüm Google Cloud projeleri için benzersiz bir ad olan proje kimliğini unutmayın (yukarıdaki ad daha önce alınmış ve size uygun olmayacaktır!). Bu kod laboratuvarın ilerleyen bölümlerinde PROJECT_ID olarak adlandırılacaktır.

Ardından, henüz yapmadıysanız Developers Console'da faturalandırmayı etkinleştirmeniz ve Google Cloud kaynaklarını kullanmanız ve Cloud Spanner API'yi etkinleştirmeniz gerekir.

15d0ef27a8fbab27.png

Bu codelab'i çalıştırmanın maliyeti birkaç dolardan fazla değildir. Ancak daha fazla kaynak kullanmaya karar verirseniz veya bu kaynakları çalışır durumda bırakırsanız daha yüksek ücret ödemeniz gerekebilir (bu belgenin sonundaki "temizlik" bölümüne bakın). Google Cloud Spanner fiyatlandırması burada açıklanmıştır.

Yeni Google Cloud Platform kullanıcıları, bu codelab'i tamamen ücretsiz hale getirecek 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.

Google Cloud Shell Kurulumu

Google Cloud ve Spanner, dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de bu codelab'de, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacağız.

Bu Debian tabanlı sanal makine, ihtiyacınız olan tüm geliştirme araçlarıyla yüklüdür. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud'da çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Yani bu codelab'de ihtiyacınız olan tek şey bir tarayıcıdır (evet, Chromebook'ta çalışır).

  1. Cloud Shell'i Cloud Console'dan etkinleştirmek için Cloud Shell'i etkinleştir gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A simgesini tıklamanız yeterlidir (sağlanması ve ortama bağlanması yalnızca birkaç dakika sürer).

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

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

Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin PROJECT_ID olarak ayarlanmış olduğunu göreceksiniz.

gcloud auth list

Komut çıkışı

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

Komut çıkışı

[core]
project = <PROJECT_ID>

Herhangi bir nedenle proje ayarlanmamışsa şu komutu vermeniz yeterlidir:

gcloud config set project <PROJECT_ID>

PROJECT_ID cihazınızı mı arıyorsunuz? Kurulum adımlarında kullandığınız kimliği kontrol edin veya Cloud Console kontrol panelinden arayın:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell bazı ortam değişkenlerini de varsayılan olarak ayarlar. Bu değişkenler, gelecekte komut çalıştırdığınızda işinize yarayabilir.

echo $GOOGLE_CLOUD_PROJECT

Komut çıkışı

<PROJECT_ID>
  1. Son olarak, varsayılan alt bölgeyi ve proje yapılandırmasını ayarlayın.
gcloud config set compute/zone us-central1-f

Çeşitli farklı alt bölgeler seçebilirsiniz. Daha fazla bilgi için Bölgeler ve Bölgeler.

Özet

Bu adımda ortamınızı ayarlarsınız.

Sonraki bölüm

Şimdi bir Cloud Spanner örneği kuracaksınız.

3. Cloud Spanner Örneği Oluşturma

Bu adımda, bu codelab için Cloud Spanner örneğimizi oluşturduk. Soldaki Hamburger Menüsünde 1a6580bd3d3e6783.png Spanner girişini 3129589f7bc9e5ce.png veya "/" tuşuna basarak Spanner girişini arayın ve "Spanner" yazın.

36e52f8df8e13b99.png

Ardından 95269e75bc8c3e4d.png düğmesini tıklayın ve örneğiniz için cloudspanner-leaderboard örnek adını girip bir yapılandırma seçip (bölgesel bir örnek seçin) ve düğüm sayısını ayarlayın. Bu codelab için yalnızca 1 düğüme ihtiyacımız olacaktır. Üretim örnekleri için ve Cloud Spanner HDS'si (Hizmet Düzeyi Sözleşmesi) şartlarına uygun olmak üzere, Cloud Spanner örneğinizde en az 3 düğüm çalıştırmanız gerekir.

Son olarak, "Oluştur"u tıklayın. ve birkaç saniye içinde Cloud Spanner örneğiniz olur.

dceb68e9ed3801e8.png

Sonraki adımda yeni örneğimizde veritabanı ve şema oluşturmak için C# istemci kitaplığını kullanacağız.

4. Veritabanı ve şema oluşturma

Bu adımda örnek veritabanımızı ve şemamızı oluşturacağız.

Şimdi, C# istemci kitaplığını kullanarak iki tablo oluşturalım; Oyuncu bilgileri için Oyuncular tablosu, oyuncu skorlarını kaydetmek için de Skorlar tablosu. Bunu yapmak için Cloud Shell'de C# konsolu uygulaması oluşturma adımlarını inceleyeceğiz.

Öncelikle Cloud Shell'de aşağıdaki komutu yazarak GitHub'dan bu codelab için örnek kodu klonlayın:

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

Ardından dizini "applications" (uygulamalar) olarak değiştirin uygulamanızı oluşturacağınız dizindir.

cd dotnet-docs-samples/applications/

Bu codelab'de gerekli olan tüm kodlar, codelab'de ilerlerken referans olarak kullanılmak üzere Leaderboard adlı çalıştırılabilir bir C# uygulaması olarak mevcut dotnet-docs-samples/applications/leaderboard dizininde bulunmaktadır. Yeni bir dizin oluşturup Skor Tablosu uygulamasının bir kopyasını aşamalı olarak hazırlayacağız.

"codelab" adlı yeni bir dizin oluşturun dokunun ve aşağıdaki komutla dizini değiştirin:

mkdir codelab && cd $_

"Leaderboard" adlı yeni bir .NET C# konsol uygulaması oluşturun kullanabilirsiniz:

dotnet new console -n Leaderboard

Bu komut, iki birincil dosyadan (Leaderboard.csproj proje dosyası ve Program.cs program dosyası) oluşan basit bir konsol uygulaması oluşturur.

Haydi çalıştıralım. Dizini, uygulamanın yer aldığı yeni oluşturulan Leaderboard diziniyle değiştirin:

cd Leaderboard

Ardından dosyayı çalıştırmak için aşağıdaki komutu girin.

dotnet run

"Hello World!" uygulama çıkışını görürsünüz.

Şimdi Program.cs tablosunu C# Spanner istemci kitaplığını kullanarak iki tablo Oyuncu ve Skorlardan oluşan bir leaderboard oluşturmak için düzenlenecek şekilde düzenleyerek konsol uygulamamızı güncelleyelim. Bu işlemi doğrudan Cloud Shell Düzenleyici'de yapabilirsiniz:

Aşağıda vurgulanan simgeyi tıklayarak Cloud Shell Düzenleyici'yi açın:

73cf70e05f653ca.png

Ardından, Cloud Shell Düzenleyici'de Program.cs dosyasını açın ve aşağıdaki C# uygulaması kodunu Program.cs dosyasına yapıştırarak leaderboard veritabanını, Players ve Scores tablolarını oluşturmak için gereken kodla dosyanın mevcut kodunu değiştirin:

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

Program kodunu daha net bir şekilde açıklamak için aşağıda Program’ın ana bileşenlerinin etiketlenmiş bir diyagramına yer verilmiştir:

b70b1b988ea3ac8a.png

create komutunu etkinleştirme kodu eklendikten sonra Program.cs dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için dotnet-docs-samples/applications/leaderboard/step4 dizinindeki Program.cs dosyasını kullanabilirsiniz.

Ardından, Cloud Shell Düzenleyici'yi kullanarak Leaderboard.csproj programının proje dosyasını açıp düzenleyin ve aşağıdaki koda benzer şekilde güncelleyin. "Dosya"yı kullanarak tüm değişikliklerinizi kaydettiğinizden emin olun menüsünü kullanabilirsiniz.

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

Bu değişiklik, Cloud Spanner API ile etkileşim kurmamız gereken Google.Cloud.Spanner.Data C# Spanner Nuget paketine bir referans ekledi. Bu değişiklik, noktanet-doc-samples GitHub deposunun bir parçası olan ve kullanışlı bir "verbmap" sağlayan CommandLineUtil projesine bir referans da ekler CommandLineParser açık kaynak uzantısı; Konsol uygulamalarındaki komut satırı girişlerini yönetmek için kullanışlı bir kitaplıktır.

create komutunu etkinleştirme kodu eklendikten sonra Leaderboard.csproj dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için dotnet-docs-samples/applications/leaderboard/step4 dizinindeki Leaderboard.csproj dosyasını kullanabilirsiniz.

Artık güncellenmiş örneğinizi çalıştırmaya hazırsınız. Güncellenen uygulamanızın varsayılan yanıtını görmek için aşağıdakileri yazın:

dotnet run

Şuna benzer bir çıkış alırsınız:

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.

Bu yanıttan bu uygulamanın, olası üç komuttan biriyle çalıştırılabilecek Leaderboard uygulaması olduğunu görüyoruz: create, help ve version.

Spanner veritabanı ve tabloları oluşturmak için create komutunu deneyelim. Komutun beklenen bağımsız değişkenlerini görmek için komutu bağımsız değişken olmadan çalıştırın.

dotnet run create

Aşağıdaki gibi bir yanıt görürsünüz:

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.

Burada create komutunun beklenen bağımsız değişkenlerinin Proje Kimliği, Örnek Kimliği ve Veritabanı Kimliği olduğunu görebiliriz.

Şimdi aşağıdaki komutu çalıştırın. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

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

Cloud Console'un Cloud Spanner bölümünde, soldaki menüde yeni veritabanınızı ve tablolarınızı göreceksiniz.

ba9008bb84cb90b0.png

Sonraki adımda, yeni veritabanınıza bazı veriler yüklemek için uygulamamızı güncelleyeceğiz.

5. Verileri Yükle

Artık leaderboard adında iki tablo içeren bir veritabanımız var; Players ve Scores. Şimdi Players tablomuzu oyuncularla, Scores tablomuzu da her oyuncu için rastgele skorlarla doldurmak için C# istemci kitaplığını kullanalım.

Aşağıda vurgulanan simgeyi tıklayarak Cloud Shell Düzenleyici'yi açın:

4d17840699d8e7ce.png

Ardından, Players tablosuna 100 oyuncu eklemek üzere veya Players tablosundaki her oyuncu için Scores tablosuna 4 rastgele puan eklemek üzere kullanılabilecek bir insert komutu eklemek için Cloud Shell Düzenleyici'de Program.cs dosyasını düzenleyin.

Önce "Verbmap" (Yazım eşlemesi) içine yeni bir insert komut bloğu ekleyin (Program’ın üst kısmındaki mevcut create komut bloğunun altında):

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

Daha sonra, mevcut CreateAsync yönteminin altına aşağıdaki Insert, InsertPlayersAsync ve InsertScoresAsync yöntemlerini ekleyin:

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

Ardından, insert komutunu çalıştırmak için Program'ınızın "Main" (Ana) komutuna aşağıdaki kodu ekleyin yöntem:

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

insert komutunu etkinleştirme kodu eklendikten sonra Program.cs dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için dotnet-docs-samples/applications/leaderboard/step5 dizinindeki Program.cs dosyasını kullanabilirsiniz.

Şimdi yeni insert komutunun programın olası komutlar listesine eklendiğini onaylamak için programı çalıştıralım. Aşağıdaki komutu çalıştırın:

dotnet run

insert komutunun artık programın varsayılan çıkışına dahil olduğunu göreceksiniz:

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.

Şimdi giriş bağımsız değişkenlerini görmek için insert komutunu çalıştıralım. Aşağıdaki komutu girin.

dotnet run insert

Bu işlem aşağıdaki yanıtı döndürür:

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

Yanıttan; Proje Kimliği, Örnek Kimliği ve Veritabanı Kimliği'ne ek olarak, value pos. 3 tarafından "ek türü" türünde başka bir bağımsız değişkenin beklendiğini görebilirsiniz. yardımcı olur. Bu bağımsız değişkenin değeri "oyuncular" olabilir kullanabilirsiniz.

Şimdi insert komutunu, create komutunu çağırdığımızda kullandığımız bağımsız değişken değerleriyle çalıştırıp "oynatıcılar" ifadesini ekleyerek çalıştıralım ek "ek türü" olarak bağımsız değişkeninin önüne geçer. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

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

Şimdi Scores tablomuzu Players tablosundaki her oyuncu için zaman damgalarıyla birlikte dört rastgele puanla doldurmak üzere C# istemci kitaplığını kullanalım.

Scores tablosunun Timestamp sütunu, "taahhüt zaman damgası" olarak tanımlandı sütununu kontrol edin: daha önce create komutunu çalıştırdığımızda yürütülen aşağıdaki SQL deyimi:

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

OPTIONS(allow_commit_timestamp=true) özelliğine dikkat edin. Bu durumda Timestamp, "taahhüt zaman damgası" haline gelir. sütununu görüntüler ve belirli bir tablo satırındaki INSERT ve UPDATE işlemleri için tam işlem zaman damgasıyla otomatik olarak doldurulmasını sağlar.

Ayrıca, bir "kaydetme zaman damgası"na kendi zaman damgası değerlerinizi de ekleyebilirsiniz. geçmiş bir değere sahip bir zaman damgası eklemeniz gerekir. Bu codelab'in amacı budur.

Şimdi insert komutunu, "scores" ekleyerek create komutunu çağırdığımızda kullandığımız bağımsız değişken değerleriyle çalıştıralım. ek "ek türü" olarak bağımsız değişkeninin önüne geçer. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

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, "type of insert" (ekleme türü) ile çalıştırılıyor belirtilen scores yöntemi, rastgele oluşturulmuş bir zaman damgası (tarih/saat geçmişte olmak üzere) eklemek için aşağıdaki kod snippet'lerini kullanan InsertScoresAsync yöntemini çağırır:

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

Timestamp sütununun tam olarak "Insert" öğesinin zaman damgasıyla otomatik olarak doldurulması için işlemi gerçekleşirse bunun yerine aşağıdaki kod snippet'indeki gibi C# sabitini SpannerParameter.CommitTimestamp ekleyebilirsiniz:

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

Veri yüklemeyi tamamladığımıza göre artık yeni tablolarımıza yazdığımız değerleri doğrulayalım. Öncelikle leaderboard veritabanını ve ardından Players tablosunu seçin. Data sekmesini tıklayın. Tablonun PlayerId ve PlayerName sütunlarında veri bulunduğunu göreceksiniz.

7bc2c96293c31c49.png

Şimdi de Scores tablosunu tıklayıp Data sekmesini seçerek Puanlar tablosunun veri içerdiğini doğrulayalım. Tablonun PlayerId, Timestamp ve Score sütunlarında verileriniz olduğunu göreceksiniz.

d8a4ee4f13244c19.png

Tebrikler! Oyun skor tablosu oluşturmak için kullanabileceğimiz bazı sorgular çalıştırmak üzere Programımızı güncelleyelim.

6. Skor tablosu sorgularını çalıştırma

Veritabanımızı kurup bilgileri tablolarımıza yüklediğimize göre şimdi bu verileri kullanarak bir skor tablosu oluşturalım. Bunu yapmak için aşağıdaki dört soruyu cevaplamamız gerekiyor:

  1. "En İyi On" Oyuncular var mı?
  2. "En İyi On" Oyuncular ne durumda?
  3. "En İyi On" Oyuncular hakkında bilgi edindiniz?
  4. "En İyi On" Oyuncular merak ediyor musunuz?

Şimdi Programımızı, bu soruları cevaplayacak SQL sorgularını çalıştıracak şekilde güncelleyelim.

Skor tablosu için gereken bilgileri üretecek soruları yanıtlamak üzere, sorguların çalıştırılmasını sağlayacak bir query komutu ekleyeceğiz.

Program'ı query komutu ekleyerek güncellemek için Cloud Shell Düzenleyici'de Program.cs dosyasını düzenleyin.

Önce "Verbmap" (Yazım eşlemesi) içine yeni bir query komut bloğu ekleyin (Program’ın üst kısmındaki mevcut insert komut bloğunun altında):

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

Daha sonra, mevcut InsertScoresAsync yönteminin altına aşağıdaki Query ve QueryAsync yöntemlerini ekleyin:

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

Ardından, query komutunu çalıştırmak için Program'ınızın "Main" (Ana) komutuna aşağıdaki kodu ekleyin yöntem:

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

query komutunu etkinleştirme kodu eklendikten sonra Program.cs dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için dotnet-docs-samples/applications/leaderboard/step6 dizinindeki Program.cs dosyasını kullanabilirsiniz.

Şimdi yeni query komutunun programın olası komutlar listesine eklendiğini onaylamak için programı çalıştıralım. Aşağıdaki komutu çalıştırın:

dotnet run

query komutunun artık yeni bir komut seçeneği olarak programın varsayılan çıkışına dahil olduğunu göreceksiniz:

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.

Şimdi giriş bağımsız değişkenlerini görmek için query komutunu çalıştıralım. Şu komutu girin:

dotnet run query

Bu işlem, aşağıdaki yanıtı döndürür:

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.

Yanıttan; Proje Kimliği, Örnek Kimliği ve Veritabanı Kimliği'ne ek olarak, value pos. 3 adlı başka bir bağımsız değişkenin daha bulunduğunu görebilirsiniz. Bu bağımsız değişken, kayıtları Scores tablosunun Timestamp sütunundaki değerlerine göre filtrelemek için kullanılacak saat cinsinden bir zaman aralığı belirtmemize olanak tanır. Bu bağımsız değişkenin varsayılan değeri 0 olup hiçbir kayıt zaman damgalarına göre filtrelenmez. Böylece query komutunu "zaman aralığı" olmadan kullanabiliriz. "En İyi On" listemizi almak için "En İyi On" değerini tüm zamanların en iyi oyuncuları.

query komutunu, create komutunu çalıştırdığımızda kullandığımız bağımsız değişken değerlerini kullanarak, bir "zaman aralığı" belirtmeden çalıştıralım. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard

"İlk On" bilgisini içeren bir yanıt göreceksiniz oyuncuları beğenmişsinizdir:

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

Şimdi "İlk On"u sorgulamak için gerekli bağımsız değişkenlerle birlikte query komutunu çalıştıralım. yılın oyuncuları için bir "zaman aralığı" 8.760 olan bir yıldaki saat sayısına eşittir. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760

"İlk On" bilgisini içeren bir yanıt göreceksiniz en iyi performans gösteren oyuncuları şunlar:

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

Şimdi "İlk On"u sorgulamak için query komutunu çalıştıralım bir "zaman aralığı" belirleyerek ayın oyuncuları bir aydaki saat sayısına (730) eşittir. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730

"İlk On" bilgisini içeren bir yanıt göreceksiniz şunlar gibi ayın oyuncuları:

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

Şimdi "İlk On"u sorgulamak için query komutunu çalıştıralım "zaman aralığı" belirleyerek haftanın oyuncuları bir haftadaki 168 saat olan saat sayısına eşittir. PROJECT_ID yerine bu codelab'in başında oluşturduğunuz proje kimliğini emin olun.

dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168

"İlk On" bilgisini içeren bir yanıt göreceksiniz şunlar gibi haftanın oyuncuları:

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

Harika!

Artık siz kayıtları ekledikçe Spanner, veritabanınızı sizin ihtiyaç duyduğunuz büyüklükte ölçeklendirir.

Veritabanınız ne kadar büyürse gelişsin, oyununuzun skor tablosu Spanner ve Truetime teknolojisi ile doğrulukla ölçeklenmeye devam edebilir.

7. Temizleme

Spanner ile oynadığımız eğlencenin ardından, oyun alanını temizleyerek değerli kaynaklardan ve paradan tasarruf etmemiz gerekiyor. Neyse ki bu kolay bir adımdır. Bunun için geliştirici Konsolu'na gidip "Cloud Spanner Örneği Oluşturma" adlı codelab adımında oluşturduğumuz örneği silmeniz yeterlidir.

8. Tebrikler!

İşlediğimiz konular:

  • Leaderboard için Google Cloud Spanner Örnekleri, Veritabanları ve Tablo Şeması
  • .NET Core C# konsol uygulaması oluşturma
  • C# istemci kitaplığını kullanarak Spanner Veritabanı ve Tabloları oluşturma
  • C# istemci kitaplığını kullanarak Spanner Veritabanına veri yükleme
  • "İlk On"u sorgulama Spanner kaydetme zaman damgalarını ve C# istemci kitaplığını kullanarak verilerinizden sonuçlar

Sonraki Adımlar:

Geri bildiriminizi paylaşın

  • Lütfen çok kısa bir süre ayırarak anketimizi doldurun.