No mundo do desenvolvimento web moderno, a flexibilidade e a escalabilidade são cruciais. Uma das maneiras de alcançar essa flexibilidade é através do suporte a múltiplos bancos de dados em sua aplicação. Isso permite que você escolha o banco de dados mais adequado para cada situação, otimizando o desempenho e reduzindo custos. Neste artigo, vamos explorar como adicionar suporte ao SQL Server em uma aplicação .NET existente que já utiliza o MySQL, usando o Entity Framework Core (EF Core) e Docker. Este guia prático é baseado em uma série de lições que visam construir uma aplicação .NET pronta para produção, desde o código até a nuvem.
Configurando o SQL Server com Docker
O Docker simplifica o processo de configuração de ambientes de desenvolvimento e produção. Para começar a usar o SQL Server, precisamos primeiro baixar a imagem do Docker Hub. Utilize o seguinte comando no seu terminal:
docker pull mcr.microsoft.com/mssql/server:2022-latest
Este comando baixa a versão mais recente do SQL Server 2022. Após o download, podemos executar o container com o seguinte comando:
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Admin123!" -p 1433:1433 --name ecommerce-sqlserver -d mcr.microsoft.com/mssql/server:2022-latest
Importante: Certifique-se de usar uma senha forte para a conta 'sa' (System Administrator). No exemplo acima, utilizamos 'Admin123!', mas em um ambiente de produção, você deve usar uma senha significativamente mais robusta. Este comando mapeia a porta 1433 do container para a porta 1433 do host, permitindo que você se conecte ao SQL Server a partir do seu ambiente de desenvolvimento.
Para verificar se o SQL Server está rodando corretamente, você pode usar ferramentas como o Azure Data Studio para se conectar ao servidor. Use as seguintes credenciais:
- Usuário padrão: sa
- Senha (do exemplo): Admin123!
Atualizando as Connection Strings no appsettings.json
Para que a aplicação .NET possa se conectar tanto ao MySQL quanto ao SQL Server, precisamos adicionar as respectivas connection strings no arquivo appsettings.json. Este arquivo contém as configurações da aplicação, incluindo as informações de conexão com os bancos de dados.
{
"DatabaseProvider": "SqlServer", // "MySql" or "SqlServer"
"ConnectionStrings": {
"MySQL": "Server=localhost;Port=3306;Database=ECommerceDb;User=root;Password=Admin123!;",
"SqlServer": "Server=localhost,1433;Database=ECommerceDb;User Id=sa;Password=Admin123!;TrustServerCertificate=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Observe a chave DatabaseProvider. Ela permite que a aplicação determine, em tempo de execução, qual banco de dados utilizar. Altere o valor para "SqlServer" ou "MySql" conforme necessário. As connection strings contêm as informações de conexão para cada banco de dados, incluindo o servidor, porta, nome do banco de dados, usuário e senha.
Instalando o Pacote do SQL Server para EF Core
Para interagir com o SQL Server através do Entity Framework Core, precisamos instalar o pacote correspondente. Execute o seguinte comando no terminal, dentro do diretório do projeto ECommerce.Infrastructure:
dotnet add ECommerce.Infrastructure package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.21
Este comando adiciona o pacote Microsoft.EntityFrameworkCore.SqlServer ao projeto, permitindo que o EF Core se comunique com o SQL Server. A versão especificada (8.0.21) é um exemplo; verifique a versão mais recente disponível no NuGet.
Criando os DbContexts para MySQL e SQL Server
Agora, precisamos criar classes DbContext específicas para cada banco de dados. Estas classes herdarão de uma classe base AppDbContext, que conterá a configuração comum para ambos os bancos de dados.
Criando MySqlDbContext
Crie um arquivo chamado MySqlDbContext.cs no diretório ECommerce.Infrastructure/Data com o seguinte conteúdo:
using Microsoft.EntityFrameworkCore;
namespace ECommerce.Infrastructure.Data;
public class MySqlDbContext : AppDbContext
{
public MySqlDbContext(DbContextOptions<MySqlDbContext> options)
: base(options)
{
}
}
Criando SqlServerDbContext
Crie um arquivo chamado SqlServerDbContext.cs no diretório ECommerce.Infrastructure/Data com o seguinte conteúdo:
using Microsoft.EntityFrameworkCore;
namespace ECommerce.Infrastructure.Data;
public class SqlServerDbContext : AppDbContext
{
public SqlServerDbContext(DbContextOptions<SqlServerDbContext> options)
: base(options)
{
}
}
Essas classes DbContext são responsáveis por representar a conexão com o banco de dados e permitir que o EF Core execute operações de CRUD (Create, Read, Update, Delete) nas tabelas do banco de dados.
Criando Data Factories para Migrações
As Data Factories são utilizadas para criar instâncias dos DbContexts durante o processo de migração. Elas são necessárias para que o EF Core possa criar e atualizar o esquema do banco de dados.
Renomeando e Atualizando a MySqlDbContextFactory
Renomeie o arquivo DesignTimeDbContextFactory.cs para MySqlDbContextFactory.cs e atualize seu conteúdo para:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace ECommerce.Infrastructure.Data;
public class MySqlDbContextFactory : IDesignTimeDbContextFactory<MySqlDbContext>
{
public MySqlDbContext CreateDbContext(string[] args)
{
// Locate the API project's appsettings.json
var basePath = Path.Combine(Directory.GetCurrentDirectory(), "../ECommerce.API");
var configuration = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
var connectionString = configuration.GetConnectionString("MySQL")
?? throw new InvalidOperationException("MySQL connection string not found in appsettings.json");
var optionsBuilder = new DbContextOptionsBuilder<MySqlDbContext>();
// Use a fixed version instead of ServerVersion.AutoDetect to avoid connection attempts during design-time
optionsBuilder.UseMySql(
connectionString,
new MySqlServerVersion(new Version(8, 0, 36))
);
return new MySqlDbContext(optionsBuilder.Options);
}
}
Criando a SqlServerDbContextFactory
Crie um arquivo chamado SqlServerDbContextFactory.cs no diretório ECommerce.Infrastructure/Data com o seguinte conteúdo:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace ECommerce.Infrastructure.Data;
public class SqlServerDbContextFactory : IDesignTimeDbContextFactory<SqlServerDbContext>
{
public SqlServerDbContext CreateDbContext(string[] args)
{
var basePath = Path.Combine(Directory.GetCurrentDirectory(), "../ECommerce.API");
var configuration = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
var connectionString = configuration.GetConnectionString("SqlServer")
?? throw new InvalidOperationException("SqlServer connection string not found in appsettings.json");
var optionsBuilder = new DbContextOptionsBuilder<SqlServerDbContext>();
optionsBuilder.UseSqlServer(connectionString);
return new SqlServerDbContext(optionsBuilder.Options);
}
}
Implementando a Injeção de Dependência Multi-Provider
Para facilitar a alternância entre os bancos de dados, vamos criar uma extensão de injeção de dependência que registra o AppDbContext com o provider correto com base na configuração.
Crie um arquivo chamado DependencyInjection.cs no diretório ECommerce.Infrastructure com o seguinte conteúdo:
using ECommerce.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ECommerce.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration configuration)
{
var provider = configuration["DatabaseProvider"] ?? "MySQL";
if (string.Equals(provider, "SqlServer", StringComparison.OrdinalIgnoreCase))
{
var conn = configuration.GetConnectionString("SqlServer");
services.AddDbContext<AppDbContext, SqlServerDbContext>(options =>
options.UseSqlServer(conn));
}
else if (string.Equals(provider, "MySQL", StringComparison.OrdinalIgnoreCase))
{
var conn = configuration.GetConnectionString("MySQL");
services.AddDbContext<AppDbContext, MySqlDbContext>(options =>
options.UseMySql(conn, ServerVersion.AutoDetect(conn)));
}
else
{
throw new InvalidOperationException($"Unsupported provider: {provider}");
}
return services;
}
}
Esta extensão lê a configuração DatabaseProvider do arquivo appsettings.json e registra o AppDbContext com o provider correspondente (MySQL ou SQL Server). Se a configuração não for encontrada, o MySQL será utilizado como padrão.
Atualizando o Arquivo Program.cs
No arquivo Program.cs, precisamos adicionar a chamada para a extensão de injeção de dependência que acabamos de criar.
using ECommerce.Application.Services.Implementations;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Repositories;
using ECommerce.Infrastructure.Data;
using ECommerce.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using ECommerce.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
// ------------------------------------------------------
// Add Controllers
// ------------------------------------------------------
builder.Services.AddControllers();
// add infrastructure (DbContext + provider selection)
builder.Services.AddInfrastructure(builder.Configuration);
// ------------------------------------------------------
// Repository Registrations
// ------------------------------------------------------
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IOrderItemRepository, OrderItemRepository>();
// ------------------------------------------------------
// Service Registrations (Application Layer)
// ------------------------------------------------------
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IOrderItemService, OrderItemService>();
// ------------------------------------------------------
// Swagger
// ------------------------------------------------------
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// ------------------------------------------------------
// Middleware Pipeline
// ------------------------------------------------------
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
A linha builder.Services.AddInfrastructure(builder.Configuration); registra o AppDbContext com o provider correto, permitindo que a aplicação utilize o banco de dados configurado no arquivo appsettings.json.
Criando as Migrações para SQL Server
Antes de criar as migrações, mova as migrações existentes do MySQL para uma pasta separada:
mkdir Migrations\MySQL
move Migrations\*.cs Migrations\MySQL\
Agora, crie as migrações para o SQL Server:
dotnet ef migrations add InitSqlServer -p ECommerce.Infrastructure -s ECommerce.API --context SqlServerDbContext --output-dir "Migrations/SqlServer"
Este comando cria as migrações no diretório Migrations/SqlServer, utilizando o SqlServerDbContext. As migrações contêm as instruções SQL para criar o esquema do banco de dados no SQL Server.
Testando com o Swagger
Agora você pode executar a aplicação e testar a conexão com o SQL Server através do Swagger. Certifique-se de que a configuração DatabaseProvider no arquivo appsettings.json esteja definida como "SqlServer".
Conclusão
Neste artigo, exploramos como adicionar suporte ao SQL Server em uma aplicação .NET existente que já utiliza o MySQL, usando o Entity Framework Core e Docker. A capacidade de alternar entre diferentes bancos de dados em tempo de execução oferece flexibilidade e escalabilidade, permitindo que você escolha o banco de dados mais adequado para cada situação. No futuro, podemos esperar que mais aplicações adotem essa abordagem multi-banco de dados, aproveitando os benefícios de cada tecnologia e otimizando o desempenho e os custos.