Подключение к Dockerized базам данных из Asp.Net Core

В этой статье мы увидим, как мы подключаемся к докеризированному экземпляру PostgreSQL, Redis и MongoDB из Асп.Нет Основной проект. Мы будем использовать файл docker-compose для передачи строки подключения и других конфигураций в код, который будет переопределять значения, указанные в файле appsettings.json.

Мы будем использовать шаблон проекта веб-API в Visual Studio. Чтобы использовать файл docker-compose, мы добавим в проект поддержку оркестровки контейнеров. Это добавляет Dockerfile и докер-compose.dcproj проект к решению.

Dockerfile является стандартным и содержит минимальный код для запуска проекта на основе rest API, и никаких изменений в нем не требуется.

Он будет содержать ключи, с помощью которых мы можем получить строки подключения для трех ресурсов, которые мы планируем подключить. Поскольку мы будем переопределять их из файла docker-compose, мы можем просто определить их без каких-либо значений.

"CacheSettings": { "RedisCache": "" }, "DatabaseSettings": { "PostgressConnectionString": "", "MongoConnectionString": ""
  }

Следующим шагом будет чтение этих параметров подключения в файле startup.cs, чтобы клиенты могли быть настроены для внедрения.

var mongoConnectionString = Configuration.GetValue<string>("DatabaseSettings:MongoConnectionString"); var postgressConnectionString = Configuration.GetValue<string>("DatabaseSettings:PostgressConnectionString"); var redisConnectionString = Configuration.GetValue<string>("CacheSettings:RedisCache");

Файл по умолчанию, созданный при добавлении поддержки оркестровки, содержит ссылку на службу только для Dockerfile проекта API, как показано ниже.

version: '3.4' services: stackup_docker_db_demo: image: ${DOCKER_REGISTRY-}stackupdockerdbdemo build: context: . dockerfile: stackup_docker_db_demo/Dockerfile

Давайте теперь добавим ссылки на службы для PostgreSQL, Redis-alpine и MongoDB, чтобы эти контейнеры также запускались после сборки и запуска проекта.

stackupMongo: image: mongo container_name: stackupMongo restart: always ports: - "27017:27017" stackupRedis: image: redis:alpine container_name: stackupRedis restart: always ports: - "6379:6379" stackupPostgres: image: postgres container_name: stackupPostgres restart: always ports: - "5432:5432" environment: - POSTGRES_USER=admin - POSTGRES_PASSWORD=admin - POSTGRES_DB=stackup_postgress volumes: - stackup_postgres_data:/var/lib/postgresql/data/ pgadmin: container_name: pgadmin4_container image: dpage/pgadmin4:6.17 restart: always environment: PGADMIN_DEFAULT_EMAIL: admin@admin.com PGADMIN_DEFAULT_PASSWORD: password PGADMIN_LISTEN_PORT: 80 ports: - "8009:80" volumes: - pgadmin-data:/var/lib/pgadmin volumes: stackup_postgres_data: pgadmin-data:

Я также добавил службу для pgAdmin, чтобы мы получили пользовательский интерфейс для нашей базы данных PostgreSQL, через который мы можем подключаться и создавать базу данных и таблицы для нашей демонстрации вручную.

Теперь нам нужно обновить службу проекта WebAPI с помощью среды разработки и переменных среды с теми же ключами, которые использовались ранее в appsettings.json, чтобы их можно было переопределить во время выполнения.

services: stackup_docker_db_demo: image: ${DOCKER_REGISTRY-}stackupdockerdbdemo build: context: . dockerfile: stackup_docker_db_demo/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Development - "DatabaseSettings __MongoConnectionString=mongodb://stackupMongo:27017" - "DatabaseSettings__ PostgressConnectionString=host=stackupPostgres;Port=5432;Database=Stackup;User Id=admin;Password=admin;" - "CacheSettings__RedisCache=stackupRedis:6379"

Для вложенных свойств мы должны использовать двойное подчеркивание «__» так что компилятор может быть проанализирован и сопоставлен с правильным ключом.

Примечание. Нам нужно использовать имя службы, предоставленное для ресурсов, в качестве имени сервера в строках подключения, например стекМонго , стекPostgres и стекRedis в примере выше.

Теперь у нас есть готовое скелетное приложение, и мы можем запустить его, чтобы увидеть наши переменные в файле запуска с заполненной соединительной строкой.

Основное требование теперь выполнено, мы можем прочитать значения, определенные в файле docker-compose.yml, через appsettings.json в наш код C#. Давайте теперь исправим небольшой код, чтобы убедиться, что мы можем выполнять все операции CRUD с этими докеризованными ресурсами.

Настройка базы данных и таблицы в PostgreSQL

PgAdmin работает на локальном хосте: 8009, как определено в нашей конфигурации. Мы войдем в него с помощью учетных данных admin@admin.com и пароля, а затем создадим базу данных с именем Stackup и таблицу с именем blogpost.

Адрес имени хоста будет таким же, как имя службы и имя пользователя/пароль, оба имеют права администратора.

CREATE TABLE BlogPost ( id VARCHAR (100) PRIMARY KEY, BlogName VARCHAR ( 500 ) NOT NULL, BlogTitle VARCHAR ( 500 ) NOT NULL, BlogDescription VARCHAR ( 1000 ) NOT NULL, Tags VARCHAR ( 500 )
);

Запустите приведенный выше скрипт после создания базы данных, чтобы подготовить таблицу, в которую мы будем записывать данные из нашего Асп.Нет Основной проект WebAPI.

Во-первых, установите следующие пакеты Nuget, которые нам понадобятся для нашей реализации.

У нас будет две модели, две из которых создают простую запись в блоге, а другая — для создания ответа на запись в блоге, полученную из всех трех наших хранилищ.

using MongoDB.Driver;
using System.Collections.Generic; namespace stackup_docker_db_demo.Model
{ public class BlogPost { public string Id { get; set; } public string BlogName { get; set; } public string BlogTitle { get; set; } public string BlogDescription { get; set;} public string Tags { get; set; } } public class BlogPostResponse { public IEnumerable<BlogPost> MongoBlogPost { get; set; } public BlogPost RedisBlogPost { get; set; } public List<BlogPost> PostgressBlogPost { get; set;}
    }
}

Нам понадобятся три провайдера, по одному для PostgreSQL, Redis и MongoDB. Для этой демонстрации я непосредственно создаю классы, которые взаимодействуют с базовой базой данных, но в идеале мы должны следовать некоторому шаблону.

Поставщик MongoDB

using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using stackup_docker_db_demo.Model;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks; namespace stackup_docker_db_demo.DBProviders
{ public class MongoProvider { public IMongoClient _mongoClient; public IMongoDatabase _mongoDatabase; public IMongoCollection<BlogPost> BlogPosts { get; } public MongoProvider(IConfiguration config, IMongoClient _client) { _mongoClient = _client; _mongoDatabase = _mongoClient.GetDatabase("stackupMongodb"); BlogPosts = _mongoDatabase.GetCollection<BlogPost>("BlogPosts"); } public async Task CreatePost(BlogPost post) { await BlogPosts.InsertOneAsync(post); } public async Task<IEnumerable<BlogPost>> GetPost() { return await BlogPosts.Find(x => true).ToListAsync();
        }
    }
}

Поставщик Redis

using Microsoft.Extensions.Caching.Distributed;
using MongoDB.Bson.IO;
using System.Threading.Tasks;
using System;
using stackup_docker_db_demo.Model;
using Newtonsoft.Json;
using JsonConvert = Newtonsoft.Json.JsonConvert; namespace stackup_docker_db_demo.DBProviders
{ public class RedisProvider { private readonly IDistributedCache _redisCache; public RedisProvider(IDistributedCache cache) { _redisCache = cache; } public async Task<BlogPost> GetKeyFromRedis() { string postJson = await _redisCache.GetStringAsync("default"); if (string.IsNullOrEmpty(postJson)) return null; var post = Newtonsoft.Json.JsonConvert.DeserializeObject<BlogPost>(postJson); return post; } public async Task<BlogPost> UpdateKeyValue(BlogPost post) { string postJson = JsonConvert.SerializeObject(post); await _redisCache.SetStringAsync("default", postJson); return await GetKeyFromRedis();
        }

    }
}

Поставщик PostgreSQL

using Dapper;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Npgsql;
using stackup_docker_db_demo.Model;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks; namespace stackup_docker_db_demo.DBProviders
{ public class PostgressProvider { IConfiguration _configuration; string _connectionString; public PostgressProvider(IConfiguration config) { _configuration = config; _connectionString = _configuration.GetValue<string>("DatabaseSettings:PostgressConnectionString"); } public async Task<bool> CreateBlogPost(BlogPost post) { using var _connection = new NpgsqlConnection(_connectionString); string query = ""; ; var fullPath = $"stackup_docker_db_demo.SqlQueries.CreateBlogPost.sql"; var assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream(fullPath)) using (StreamReader reader = new StreamReader(stream)) { query = reader.ReadToEnd(); } var inserted = await _connection.ExecuteAsync(query, new { Id = post.Id, BlogName = post.BlogName, BlogDescription = post.BlogDescription, BlogTitle = post.BlogTitle, Tags = post.Tags }); return inserted == 0 ? false : true; } public async Task<List<BlogPost>> GetBlogPost() { using var _connection = new NpgsqlConnection(_connectionString); string query = "select * from blogpost"; var blog = await _connection.QueryAsync<BlogPost>(query); return blog == null ? null : blog.ToList();

        }

    }
}

Здесь я напрямую использовал структуру dapper для подключения к базе данных и файлу сценария. CreateBlogPost.sql находится внутри папки с именем SqlQueries в основном каталоге проекта.

INSERT INTO BlogPost (Id, BlogName, BlogTitle, BlogDescription, Tags) Values (@Id, @BlogName, @BlogTitle, @BlogDescription, @Tags);

Файл запуска также необходимо обновить для внедрения зависимостей и настройки клиентов Mongo и Redis, к которым обращаются их соответствующие поставщики.

services.AddControllers(); var mongoConnectionString = Configuration.GetValue<string>("DatabaseSettings:MongoConnectionString"); var postgressConnectionString = Configuration.GetValue<string>("DatabaseSettings:PostgressConnectionString"); var redisConnectionString = Configuration.GetValue<string>("CacheSettings:RedisCache"); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1" }); }); services.AddSingleton<IMongoClient>(x => { return new MongoClient(mongoConnectionString);
            });
            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = redisConnectionString;
            });
            services.AddSingleton<RedisProvider>();
            services.AddSingleton<MongoProvider>();
            services.AddSingleton<PostgressProvider>();

У нас есть простой BlogPostController с двумя конечными точками, первая создает сообщение в блоге в каждом из наших трех докеризированных ресурсов. Во-вторых, это конечная точка GET, которая возвращает объект ответа, извлекающий созданный пост в блоге сразу из всех трех докеризированных ресурсов.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using stackup_docker_db_demo.DBProviders;
using stackup_docker_db_demo.Model;
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks; namespace stackup_docker_db_demo.Controllers
{ [] [] public class BlogPostController : ControllerBase { RedisProvider _redis; PostgressProvider _postgress; MongoProvider _mongo; public BlogPostController(RedisProvider redis, PostgressProvider postgress, MongoProvider mongo) { _redis = redis; _postgress = postgress; _mongo = mongo; } [] public async Task<IActionResult> CreateDiscountCoupon(BlogPost post) { post.Id = Guid.NewGuid().ToString(); post.BlogTitle = "Connecting to Dockerized Redis from Asp.net Core"; await _redis.UpdateKeyValue(post); post.BlogTitle = "Connecting to Dockerized postgres from Asp.net Core"; await _postgress.CreateBlogPost(post); post.BlogTitle = "Connecting to Dockerized mongodb from Asp.net Core"; await _mongo.CreatePost(post); return Ok(); } [] public async Task<IActionResult> GetBlogPost() { var response = new BlogPostResponse(); response.PostgressBlogPost = await _postgress.GetBlogPost(); response.MongoBlogPost = await _mongo.GetPost(); response.RedisBlogPost = await _redis.GetKeyFromRedis(); return Ok(response);
        }
    }
}

Вот и все, наш код готов к добавлению и извлечению данных из наших баз данных и служб кэширования. Давайте запустим наш проект и сначала создадим новую запись в блоге через конечную точку POST.

{ "blogName": "StackUp", "blogTitle": "Connecting to Dockerized databases from Asp.net Core", "blogDescription": "Tutorial on how to connect to Dockerized Postgress, MongoDB and Redis from an asp.net core application", "tags": "Programming, Asp.Net Core, Docker"
}

После создания мы можем использовать конечную точку GET, которая суммирует сообщение в блоге в новый объект и возвращает ответ, как показано ниже.

Мы выполнили шаги для подключения к базам данных и службам кэширования, работающим в док-контейнерах, из Asp.NET основной проект.

Используя что-то подобное, мы можем получить доступ и работать с различными образами докеров, доступными для интеграции. Спасибо, что потратили свое время и прочитали этот пост, и я надеюсь, что это помогло вам в любом случае.

Исходный код демонстрационного приложения, описанного здесь, доступен по адресу Ссылка на GitHub.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *