Paweł Łukasiewicz
2023-11-20
Paweł Łukasiewicz
2023-11-20
Udostępnij Udostępnij Kontakt
Wprowadzenie

W poprzednich cyklach możecie znaleźć różne sposoby tworzenia tabeli w DynamoDB. Jednym z nich jest wykorzystanie konsoli AWS, której wykorzystanie pokazałem w tym wpisie: AWS - DynamoDB

Drugim sposobem jest wykorzystanie Visual Studio (po zainstalowaniu odpowiednich paczek) i połączenie się z naszą tabelą. Jedyne czego potrzebujecie na tym etapie to konto, które daje Wam dostęp do przeróżnych elementów infrastruktury AWS. Jeżeli jeszcze nie założyliście konta odsyłam do wpisu: AWS - tworzenie konta a następnie do krótkiego opisu jak utworzyć swoją pierwszą tabelę w DynamoDB.

W tym cyklu pójdziemy nieco dalej i wykorzystamy lokalną wersję DynamoDB, która jest idealna do testowania aplikacji w trybie offline oraz nie wymaga konieczności konfigurowania rzeczywistych tabel w chmurze. Podejście takie zapewni nam nieco mniejszą złożoność rozwiązania oraz zmniejszy (zniweluje) czas potrzebny na rozpoczęcie pracy z DynamoDB. Wykorzystamy obraz, który AWS udostępnił w Dockerze - dzięki niemu uruchomienie jest niezwykle proste. Jeżeli jeszcze nie korzystaliście z dockera (lub chcecie dowiedzieć się o nim paru słów) odsyłam do kilku wpisów:

Docker

Pomimo kilku poprzednich wpisów odnośnie Dockera przejdziemy powoli przez wszystkie kolejne punkty. Pierwszy z nich to instalacja Dockera na komputerze. Wszystkie niezbędne informacje wraz z pakietem instalacyjnym znajdziecie tutaj: https://www.docker.com/

Po poprawnej instalacji musimy dokonać pobrania obrazu. W tym celu posłużymy się poniższym poleceniem:

pull amazon/dynamodb-local
Po krótkiej chwili zobaczycie, że polecenie zostało wykonanie poprawnie a my możemy korzystać z najnowszej wersji: Docker: pobieranie obrazu

Po poprawnej instalacji możemy przejść do uruchomienia instancji kontenera wykorzystując poniższe polecenie:

docker run -p 8000:8000 amazon/dynamodb-local
Docker: uruchamianie kontenera

Polecenie to uruchomi kontener wykorzystujący obraz, który wcześniej ściągnęliśmy. Zwróćcie uwagę na uruchomienie kontenera na porcie 8000 – jeżeli używacie jakiejś innej aplikacji na tym porcie możecie określić inny numer portu. Od teraz lokalna instancja DynamoDB jest dostępna a my możemy przejść do kolejnych kroków.

Aplikacja konsolowa

Podobnie jak w poprzednim cyklu posłużymy się aplikacją konsolową. Dokonamy utworzenia prostej aplikacji z szablonu, zainstalujemy odpowiednie paczki oraz dodamy konfigurację deweloperską, która będzie korzystała z naszego obrazu.

Po utworzeniu domyślnego projektu z szablonu (aplikacja konsolowa) musimy dodać do niego dwie paczuszki:

  • AWSSDK.DynamoDBv2;
  • AWSSDK.Extensions.NETCore.Setup

Pierwsza z nich pozwala na interakcję z DynamoDB za pośrednictwem AWS .NET SDK. Druga z nich, AWSSDK.Extensions.NETCore.Setup, zawiera metody rozszerzające wspierające konfigurację i rejestrację usług AWS wykorzystując Dependency Injection.

Po poprawnej instalacji plik .csproj powinien wyglądać w poniższy sposób:

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

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.5.12" />
    <PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.2" />
  </ItemGroup>
</Project>

Koklejny krok to nieznaczne modyfikacje pliku konfiguracyjnego appsettings.json. Wprowadzone zmiany będą odpowiadały za obsługę zarówno domyślnego zachowania AWS SDK jak i opcje kontrolujące uruchamianie aplikacji względem lokalnego kontenera DynamoDB:

{
  "Logging": {
    "LogLevel": {
       "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AWS": {
    "Region": "eu-west-1"
  },
  "DynamoDb": {
    "LocalMode": false
  }
}

Do pliku dodaliśmy sekcję AWS, która zawiera region z którego chcemy korzystać. Region ten będzie używany w momencie uruchomienia ‘rzeczywistej’ usługi DynamoDB. Na nasze potrzeby dodaliśmy sekcję kontrolującą lokalne użycie powyższego komponentu – ustawiamy flagę na false, ponieważ ustawienie deweloperskie (na potrzeby testów) zostanie ustawione w innym pliku.

W tym celu musimy posłużyć się plikiem appsettings.Development.json, który nadpisze (główne) wartości konfiguracyjne:

{
  "Logging": {
    "LogLevel": {
       "Default": "Debug",
        "System": "Information",
        "Microsoft": "Information"
    }
  },
  "DynamoDb": {
    "LocalMode": true,
    "LocalServiceUrl": "http://localhost:8000"
  }
}
Pamiętajcie, że w momencie uruchamiania aplikacji dochodzi do załadowania konfiguracji ze wstępnie określonej sekwencji źródeł. W naszym przypadku aplikacja zostanie uruchomiona w środowisku programistycznym (debuggowanie w Visual Studio), dojdzie do nadpisania konfiguracji przy wykorzystaniu dodatkowego pliku .json. Określiliśmy LocalMode na true oraz zdefiniowaliśmy adres url na którym uruchomiony jest nasz lokalny kontener. Jeżeli kontener został uruchomiony na innym porcie pamiętajcie o odpowiednich zmianach. Ostatnim krokiem jest zarejestrowanie odpowiednich serwisów.

Rejestrowanie usług DynamoDB

Przechodzimy do klasy Startup w której dokonujemy rejestracji usług DynamoDB. Dokonamy tego warunkowo na bazie poprzednio przygotowanej konfiguracji:

using Amazon.DynamoDBv2;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DynamoDBCheck {
  public class Startup {
    public Startup(IConfiguration configuration) {
      Configuration = configuration;
    }

    public IConfiguration Configuration {
      get;
    }

    // Metoda wywoływana przez środowisko wykonawcze. Używamy jej do dodania usług do kontenera.
    public void ConfigureServices(IServiceCollection services)
    {
      var dynamoDbConfig = Configuration.GetSection("DynamoDb");
      var runLocalDynamoDb = dynamoDbConfig.GetValue<bool>("LocalMode");
      if (runLocalDynamoDb) {
        services.AddSingleton<IAmazonDynamoDB>(sp => {
          var clientConfig = new AmazonDynamoDBConfig {
            ServiceURL = dynamoDbConfig.GetValue<string>("LocalServiceUrl")
          };

          // domyślne dane uwierzytelniające, nie potrzebujemy prawdziwych do naszych testów
          var credentials = new BasicAWSCredentials("xxx", "xxx");
          return new AmazonDynamoDBClient(clientConfig);
        });
      } else {
        services.AddAWSService<IAmazonDynamoDB>();
      }

      services.AddRazorPages();
    }

    // Metoda wywoływana przez środowisko wykonawcze. Używamy jej do konfiguracji żądań HTTP.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      } else {
        app.UseExceptionHandler("/Error");
        // Domyślna wartość HSTS to 30 dni. Wartość może zostać zmieniona dla scanariuszy produkcyjnych, 
        // więcej tutaj: https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
      }

      app.UseHttpsRedirection();
      app.UseStaticFiles();

      app.UseRouting();
      app.UseAuthorization();
      app.UseEndpoints(endpoints => {
        endpoints.MapRazorPages();
      });
    }
  }
}

W pierwszym kroku pobieramy sekcję DynamoDB z pliku konfiguracyjnego. W przypadku uruchomienia aplikacji w środowisku deweloperskim dojdzie do ostatecznego pobrania wartości z pliku appsettings.Development.json

W kolejnym kroku pobieramy wartość ustawienia LocalMode, która pozwoli nam na warunkowe zarejestrowanie usługi AWS DynamoDB.

W przypadku wartości LocalMode ustawionej na true dokonamy ręcznej rejestracji interfejsu IAMazonDynamoDB. Tutaj wykorzystujemy przeciążenie, które pozwala nam dostarczyć fabrykę implementacji, która zwróci nam rzeczywistą implementację. W naszym przypadku tworzymy instancję AmazonDynamoDBConfig przekazując LocalServiceUrl z konfiguracji. Następnie tworzymy i zwracamy AmazonDynamoDBClient używając tej konfiguracji. Serwis ten został zarejestrowany jako singleton więc kod ten zostanie uruchomiony raz gdy dana usługa będzie potrzebna.

W przypadku wartości LocalMode ustawionej na false użyjemy metody pomocniczej pozwalającej na dodanie usługi AWS SDK poprzez wywołanie metody AddAWSService. Taka implementacja jest możliwa ponieważ wcześniej dokonaliśmy instalacji pakietu AWSSDK.Extensions.NETCore.Setup.

Operacje na DynamoDB

Teraz, gdy dokonaliśmy rejestracji interfejsu IAmazonDynamoDB w naszej aplikacji możemy wymagać wstrzykiwania zależności, gdy tylko chcemy pracować z DynamoDB. W wersji deweloperskiej będziemy oczywiście łączyć się z lokalnym DynamoDB uruchomionym w Dockerze. W innym przypadku (realny przypadek użycia) będziemy łączyć się z realną instancją bazy danych. W tym drugim przypadku musimy jednak pamiętać o dodatkowym kroku konfiguracji wymaganym przez AWS SDK do poprawnego uwierzytelnienia. O tym pisałem w poprzednim cyklu, w którym skupiliśmy się na funkcji Lambda: AWS Lambda - konfiguracja środowiska – tego kroku nie musimy robić na potrzeby tej serii wpisów.

My tym czasem przechodzimy do kolejnego wpisu w którym wykorzystując ten projekt skupimy się na różnych przypadkach wykorzystania wcześniej wspomnianej paczki do integracji z DynamoDB.