Skip to content

Latest commit

 

History

History

README.md

Console.Data.PostgreSQL

Nano API application with postgresql data.
All lessons are complete, self-contained examples that include build and deployment setup.

⚠️ To run this solution, the Nano.Library repository must be checked out in the same root directory. Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder.

⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio.


Table of Contents

Summary

This application builds on Console.Blank.

This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented for the data parts, including Data Models, Data Mappings, and the Data Context.

The worker creates and retreives a Example entity using the Nano Data Repository. Run the application and observe the console output generated by the ExampleWorker.

📖 Learn more about Nano.Data.PostgreSQL.

Registration

The following data provider has been registered using ConfigureServices(...) in program.cs.

...
.ConfigureServices(services =>
{
    services
        .AddNanoData<PostgreSqlProvider, PostgreSqlDbContext>();
})
...

Also, an initial migration has been added to the project.

dotnet ef migrations add Initial --project Console.Data.PostgreSQL

Configuration

Configured the application with the necessary data setup.

"Data": {
  "BatchSize": 25,
  "BulkBatchSize": 500,
  "BulkBatchDelay": 1000,
  "QueryRetryCount": 0,
  "UseLazyLoading": false,
  "UseCreateDatabase": false,
  "UseMigrateDatabase": false,
  "UseSoftDeletetion": false,
  "UseSensitiveDataLogging": false,
  "UseAudit": false,
  "QuerySplittingBehavior": "SingleQuery",
  "DefaultCollation": null,
  "ConnectionString": null,
  "Repository": {
    "UseAutoSave": false,
    "QueryIncludeDepth": 4
  },
  "Identity": null,
  "ConnectionPool": null,
  "HealthCheck": null
}

...and appsettings.Development.json

"Data": {
  "UseMigrateDatabase": true,
  "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123"
}

Docker Compose

Added PostgreSQL as a service dependency in docker-compose.yml.

services:
  console.data.postgresql:
    depends_on:
      - database

  database:
    image: postgis/postgis:latest
    ports:
      - 5432:5432
    networks:
      - network
    environment:
      POSTGRES_USER: sa
      POSTGRES_PASSWORD: myPassword_123
      POSTGRES_DB: nanoDb

Kubernetes

Added the %SERVICE_NAME%-secret for the connectionstring to the cronjob.yaml.

spec:
  template:
    spec:
      containers:
        env:
        - name: Data__ConnectionString
          valueFrom:
            secretKeyRef:
              name: %SERVICE_NAME%-data-secret
              key: data-connectionstring

GitHub Actions

Add the following environment variables to the buid-and-deply.yml.

env:
  DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }}
  DATA_NAME: nanoDb
  DATA_USER: api-data-postgre-user
  DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }}
  DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }}
  DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }}
  DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true
  DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true

Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed.

- name: Database Migration
  shell: pwsh
  run: |
    dotnet ef database update `
      --no-build `
      --startup-project $env:APP_NAME `
      --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `;

    if ($LastExitCode -ne 0)
    { 
        throw "error";
    };
         
    sudo apt-get update
    sudo apt-get install -y postgresql-client

    $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" `
        -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';"

    if ($userExists -ne "1")
    {
    psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" `
        -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';"
    }

    $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" `
        -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';"

    if ($userDbExists -ne "1")
    {
        psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" `
            -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;"

        psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" `
            -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;"

        psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" `
            -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;"

       psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" `
           -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;"
    }

Last, the application connectionstring must be added in a secret in Kuberntes. The Kubernetes Deploy step has been updated with the following.

sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -;
if ($LastExitCode -ne 0)
{ 
    throw "error";
};