- use test container for default database mssql

- use test container for default broker rabbitmq
- change structure persist-message-db
This commit is contained in:
meysamhadeli 2022-12-04 00:54:19 +03:30
parent b4d475aebc
commit 27c63a4efa
58 changed files with 809 additions and 601 deletions

View File

@ -1,9 +1,9 @@
version: "3.3" version: "3.3"
services: services:
####################################################### #######################################################
# Gateway # Gateway
####################################################### #######################################################
gateway: gateway:
image: gateway image: gateway
build: build:
@ -19,17 +19,15 @@ services:
- db - db
- rabbitmq - rabbitmq
- jaeger - jaeger
- eventstore.db
- elasticsearch - elasticsearch
- kibana - kibana
# - mongo
volumes: volumes:
- '${USERPROFILE}\.aspnet\https:/https/' - '${USERPROFILE}\.aspnet\https:/https/'
environment: environment:
- 'ASPNETCORE_URLS=https://+;http://+' - 'ASPNETCORE_URLS=https://+;http://+'
- ASPNETCORE_HTTPS_PORT=5001 - ASPNETCORE_HTTPS_PORT=5001
- ASPNETCORE_Kestrel__Certificates__Default__Password=password - ASPNETCORE_Kestrel__Certificates__Default__Password=password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
networks: networks:
- booking - booking
@ -52,17 +50,16 @@ services:
- db - db
- rabbitmq - rabbitmq
- jaeger - jaeger
- eventstore.db
- elasticsearch - elasticsearch
- kibana - kibana
# - mongo - mongo
volumes: volumes:
- '${USERPROFILE}\.aspnet\https:/https/' - '${USERPROFILE}\.aspnet\https:/https/'
environment: environment:
- 'ASPNETCORE_URLS=https://+;http://+' - 'ASPNETCORE_URLS=https://+;http://+'
- ASPNETCORE_HTTPS_PORT=5003 - ASPNETCORE_HTTPS_PORT=5003
- ASPNETCORE_Kestrel__Certificates__Default__Password=password - ASPNETCORE_Kestrel__Certificates__Default__Password=password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
networks: networks:
- booking - booking
@ -85,17 +82,15 @@ services:
- db - db
- rabbitmq - rabbitmq
- jaeger - jaeger
- eventstore.db
- elasticsearch - elasticsearch
- kibana - kibana
# - mongo
volumes: volumes:
- '${USERPROFILE}\.aspnet\https:/https/' - '${USERPROFILE}\.aspnet\https:/https/'
environment: environment:
- 'ASPNETCORE_URLS=https://+;http://+' - 'ASPNETCORE_URLS=https://+;http://+'
- ASPNETCORE_HTTPS_PORT=5005 - ASPNETCORE_HTTPS_PORT=5005
- ASPNETCORE_Kestrel__Certificates__Default__Password=password - ASPNETCORE_Kestrel__Certificates__Default__Password=password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
networks: networks:
- booking - booking
@ -118,17 +113,16 @@ services:
- db - db
- rabbitmq - rabbitmq
- jaeger - jaeger
- eventstore.db
- elasticsearch - elasticsearch
- kibana - kibana
# - mongo - mongo
volumes: volumes:
- '${USERPROFILE}\.aspnet\https:/https/' - '${USERPROFILE}\.aspnet\https:/https/'
environment: environment:
- 'ASPNETCORE_URLS=https://+;http://+' - 'ASPNETCORE_URLS=https://+;http://+'
- ASPNETCORE_HTTPS_PORT=5012 - ASPNETCORE_HTTPS_PORT=5012
- ASPNETCORE_Kestrel__Certificates__Default__Password=password - ASPNETCORE_Kestrel__Certificates__Default__Password=password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
networks: networks:
- booking - booking
@ -154,18 +148,49 @@ services:
- eventstore.db - eventstore.db
- elasticsearch - elasticsearch
- kibana - kibana
# - mongo - mongo
volumes: volumes:
- '${USERPROFILE}\.aspnet\https:/https/' - '${USERPROFILE}\.aspnet\https:/https/'
environment: environment:
- 'ASPNETCORE_URLS=https://+;http://+' - 'ASPNETCORE_URLS=https://+;http://+'
- ASPNETCORE_HTTPS_PORT=5010 - ASPNETCORE_HTTPS_PORT=5010
- ASPNETCORE_Kestrel__Certificates__Default__Password=password - ASPNETCORE_Kestrel__Certificates__Default__Password=password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
networks: networks:
- booking - booking
#######################################################
# SqlServer
#######################################################
db:
container_name: sqldb
image: mcr.microsoft.com/mssql/server:2022-latest
restart: unless-stopped
ports:
- "1433:1433"
environment:
SA_PASSWORD: "@Aa123456"
ACCEPT_EULA: "Y"
networks:
- booking
# ######################################################
# # Postgres
# ######################################################
# postgres:
# image: postgres:latest
# container_name: postgres
# restart: on-failure
# ports:
# - '5432:5432'
# environment:
# - POSTGRES_USER=postgres
# - POSTGRES_PASSWORD=postgres
# networks:
# - booking
####################################################### #######################################################
# Rabbitmq # Rabbitmq
@ -181,22 +206,6 @@ services:
- booking - booking
#######################################################
# SqlServer
#######################################################
db:
container_name: sqldb
image: mcr.microsoft.com/mssql/server:2017-latest
restart: unless-stopped
ports:
- "1433:1433"
environment:
SA_PASSWORD: "@Aa123456"
ACCEPT_EULA: "Y"
networks:
- booking
####################################################### #######################################################
# Jaeger # Jaeger
####################################################### #######################################################
@ -220,22 +229,22 @@ services:
# EventStoreDB # EventStoreDB
####################################################### #######################################################
eventstore.db: eventstore.db:
image: eventstore/eventstore:21.2.0-buster-slim image: eventstore/eventstore:21.2.0-buster-slim
restart: on-failure restart: on-failure
environment: environment:
- EVENTSTORE_CLUSTER_SIZE=1 - EVENTSTORE_CLUSTER_SIZE=1
- EVENTSTORE_RUN_PROJECTIONS=All - EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=true - EVENTSTORE_START_STANDARD_PROJECTIONS=true
- EVENTSTORE_EXT_TCP_PORT=1010 - EVENTSTORE_EXT_TCP_PORT=1010
- EVENTSTORE_EXT_HTTP_PORT=2113 - EVENTSTORE_EXT_HTTP_PORT=2113
- EVENTSTORE_INSECURE=true - EVENTSTORE_INSECURE=true
- EVENTSTORE_ENABLE_EXTERNAL_TCP=true - EVENTSTORE_ENABLE_EXTERNAL_TCP=true
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true - EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true
ports: ports:
- '1010:1113' - '1010:1113'
- '2113:2113' - '2113:2113'
networks: networks:
- booking - booking
####################################################### #######################################################
@ -260,36 +269,36 @@ services:
# Elastic Search # Elastic Search
####################################################### #######################################################
elasticsearch: elasticsearch:
container_name: elasticsearch container_name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.9.2 image: docker.elastic.co/elasticsearch/elasticsearch:7.9.2
restart: unless-stopped restart: unless-stopped
ports: ports:
- 9200:9200 - 9200:9200
volumes: volumes:
- elasticsearch-data:/usr/share/elasticsearch/data - elasticsearch-data:/usr/share/elasticsearch/data
environment: environment:
- xpack.monitoring.enabled=true - xpack.monitoring.enabled=true
- xpack.watcher.enabled=false - xpack.watcher.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.type=single-node - discovery.type=single-node
networks: networks:
- booking - booking
####################################################### #######################################################
# Kibana # Kibana
####################################################### #######################################################
kibana: kibana:
container_name: kibana container_name: kibana
image: docker.elastic.co/kibana/kibana:7.9.2 image: docker.elastic.co/kibana/kibana:7.9.2
restart: unless-stopped restart: unless-stopped
ports: ports:
- 5601:5601 - 5601:5601
depends_on: depends_on:
- elasticsearch - elasticsearch
environment: environment:
- ELASTICSEARCH_URL=http://localhost:9200 - ELASTICSEARCH_URL=http://localhost:9200
networks: networks:
- booking - booking

View File

@ -20,7 +20,7 @@ services:
####################################################### #######################################################
db: db:
container_name: sqldb container_name: sqldb
image: mcr.microsoft.com/mssql/server:2017-latest image: mcr.microsoft.com/mssql/server:2022-latest
restart: unless-stopped restart: unless-stopped
ports: ports:
- "1433:1433" - "1433:1433"
@ -31,6 +31,21 @@ services:
- booking - booking
# #######################################################
# # Postgres
# ######################################################
# postgres:
# image: postgres:latest
# container_name: postgres
# restart: on-failure
# ports:
# - '5432:5432'
# environment:
# - POSTGRES_USER=postgres
# - POSTGRES_PASSWORD=postgres
# networks:
# - booking
####################################################### #######################################################
# Jaeger # Jaeger
####################################################### #######################################################
@ -125,6 +140,18 @@ services:
networks: networks:
- booking - booking
#######################################################
# Redis
#######################################################
redis:
image: redis
container_name: redis
restart: unless-stopped
networks:
- booking
ports:
- 6379:6379
networks: networks:
@ -141,3 +168,4 @@ volumes:

View File

@ -27,6 +27,10 @@
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="Grpc.Core.Testing" Version="2.46.5" /> <PackageReference Include="Grpc.Core.Testing" Version="2.46.5" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Mongo2Go" Version="3.1.3" /> <PackageReference Include="Mongo2Go" Version="3.1.3" />
@ -123,7 +127,7 @@
<PackageReference Include="Bogus" Version="34.0.2" /> <PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="FluentAssertions" Version="6.8.0" /> <PackageReference Include="FluentAssertions" Version="6.8.0" />
<PackageReference Include="MediatR" Version="9.0.0" /> <PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="Respawn" Version="4.0.0" /> <PackageReference Include="Respawn" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.0" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />

View File

@ -2,8 +2,8 @@ using System.Collections.Immutable;
using System.Data; using System.Data;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
@ -11,6 +11,8 @@ namespace BuildingBlocks.EFCore;
public abstract class AppDbContextBase : DbContext, IDbContext public abstract class AppDbContextBase : DbContext, IDbContext
{ {
public const string DefaultSchema = "dbo";
private readonly ICurrentUserProvider _currentUserProvider; private readonly ICurrentUserProvider _currentUserProvider;
private IDbContextTransaction _currentTransaction; private IDbContextTransaction _currentTransaction;

View File

@ -1,6 +1,6 @@
namespace BuildingBlocks.EFCore; namespace BuildingBlocks.EFCore;
public class ConnectionStrings public class DatabaseOptions
{ {
public string DefaultConnection { get; set; } public string DefaultConnection { get; set; }
} }

View File

@ -1,6 +1,7 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.PersistMessageProcessor.Data; using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Web;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -18,14 +19,17 @@ public static class Extensions
IConfiguration configuration) IConfiguration configuration)
where TContext : DbContext, IDbContext where TContext : DbContext, IDbContext
{ {
services.AddOptions<ConnectionStrings>() services.AddOptions<DatabaseOptions>()
.Bind(configuration.GetSection(nameof(ConnectionStrings))) .Bind(configuration.GetSection(nameof(DatabaseOptions)))
.ValidateDataAnnotations(); .ValidateDataAnnotations();
services.AddDbContext<TContext>(options => services.AddDbContext<TContext>((sp, options) =>
options.UseSqlServer( {
configuration.GetConnectionString("DefaultConnection"), var databaseOptions = services.GetOptions<DatabaseOptions>(nameof(DatabaseOptions));
x => x.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name)));
options.UseSqlServer(databaseOptions?.DefaultConnection,
dbOptions => dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name));
});
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>()); services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
@ -67,10 +71,8 @@ public static class Extensions
{ {
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var persistMessageContext = scope.ServiceProvider.GetRequiredService<PersistMessageDbContext>();
await persistMessageContext.Database.MigrateAsync();
var context = scope.ServiceProvider.GetRequiredService<TContext>(); var context = scope.ServiceProvider.GetRequiredService<TContext>();
await context.Database.MigrateAsync(); await context.Database.MigrateAsync();
} }

View File

@ -1,12 +1,13 @@
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.EFCore; namespace BuildingBlocks.EFCore;
public interface IDbContext public interface IDbContext
{ {
DbSet<TEntity> Set<TEntity>() DbSet<TEntity> Set<TEntity>() where TEntity : class;
where TEntity : class; DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
IReadOnlyList<IDomainEvent> GetDomainEvents(); IReadOnlyList<IDomainEvent> GetDomainEvents();
Task BeginTransactionAsync(CancellationToken cancellationToken = default); Task BeginTransactionAsync(CancellationToken cancellationToken = default);

View File

@ -3,7 +3,6 @@ using BuildingBlocks.Logging;
using BuildingBlocks.MassTransit; using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo; using BuildingBlocks.Mongo;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using DotNetCore.CAP.MongoDB;
using HealthChecks.UI.Client; using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Diagnostics.HealthChecks;
@ -17,11 +16,11 @@ public static class Extensions
{ {
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services) public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services)
{ {
var appOptions = services.GetOptions<AppOptions>("AppOptions"); var appOptions = services.GetOptions<AppOptions>(nameof(AppOptions));
var sqlOptions = services.GetOptions<ConnectionStrings>("ConnectionStrings"); var sqlOptions = services.GetOptions<DatabaseOptions>(nameof(DatabaseOptions));
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>("RabbitMq"); var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
var mongoOptions = services.GetOptions<MongoOptions>("MongoOptions"); var mongoOptions = services.GetOptions<MongoOptions>(nameof(MongoOptions));
var logOptions = services.GetOptions<LogOptions>("LogOptions"); var logOptions = services.GetOptions<LogOptions>(nameof(LogOptions));
var healthChecksBuilder = services.AddHealthChecks() var healthChecksBuilder = services.AddHealthChecks()
.AddRabbitMQ(rabbitConnectionString: $"amqp://{rabbitMqOptions.UserName}:{rabbitMqOptions.Password}@{rabbitMqOptions.HostName}") .AddRabbitMQ(rabbitConnectionString: $"amqp://{rabbitMqOptions.UserName}:{rabbitMqOptions.Password}@{rabbitMqOptions.HostName}")

View File

@ -46,7 +46,7 @@ public static class Extensions
configure.UsingRabbitMq((context, configurator) => configure.UsingRabbitMq((context, configurator) =>
{ {
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>("RabbitMq"); var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
var host = IsRunningInContainer ? "rabbitmq" : rabbitMqOptions.HostName; var host = IsRunningInContainer ? "rabbitmq" : rabbitMqOptions.HostName;
configurator.Host(host, rabbitMqOptions?.Port ?? 5672, "/", h => configurator.Host(host, rabbitMqOptions?.Port ?? 5672, "/", h =>

View File

@ -1,16 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace BuildingBlocks.PersistMessageProcessor.Data;
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<PersistMessageDbContext>
{
public PersistMessageDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<PersistMessageDbContext>();
builder.UseSqlServer(
"Data Source=.\\sqlexpress;Initial Catalog=PersistMessageDB;Persist Security Info=False;Integrated Security=SSPI");
return new PersistMessageDbContext(builder.Options);
}
}

View File

@ -1,69 +0,0 @@
// <auto-generated />
using System;
using Booking.Data;
using BuildingBlocks.PersistMessageProcessor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Booking.Data.Migrations
{
[DbContext(typeof(PersistMessageDbContext))]
[Migration("20220728155556_initial")]
partial class initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ApplicationName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Booking.Data.Migrations
{
public partial class initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "dbo");
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
}
}
}

View File

@ -1,63 +0,0 @@
// <auto-generated />
using System;
using Booking.Data;
using BuildingBlocks.PersistMessageProcessor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Booking.Data.Migrations
{
[DbContext(typeof(PersistMessageDbContext))]
partial class PersistMessageDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,23 +0,0 @@
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor.Data.Configurations;
using BuildingBlocks.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor.Data;
public class PersistMessageDbContext : AppDbContextBase, IPersistMessageDbContext
{
public const string DefaultSchema = "dbo";
public PersistMessageDbContext(DbContextOptions<PersistMessageDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new PersistMessageConfiguration());
base.OnModelCreating(builder);
}
}

View File

@ -1,2 +0,0 @@
dotnet ef migrations add initial --context PersistMessageDbContext -o "Data\Migrations"
dotnet ef database update --context PersistMessageDbContext

View File

@ -1,28 +1,11 @@
using BuildingBlocks.Core; using Microsoft.Extensions.DependencyInjection;
using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.Web;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.PersistMessageProcessor; namespace BuildingBlocks.PersistMessageProcessor;
public static class Extensions public static class Extensions
{ {
public static IServiceCollection AddPersistMessage(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services)
{ {
services.AddOptions<PersistMessageOptions>()
.Bind(configuration.GetSection(nameof(PersistMessageOptions)))
.ValidateDataAnnotations();
var persistMessageOptions = services.GetOptions<PersistMessageOptions>("PersistMessageOptions");
services.AddDbContext<PersistMessageDbContext>(options =>
options.UseSqlServer(persistMessageOptions.ConnectionString,
x => x.MigrationsAssembly(typeof(PersistMessageDbContext).Assembly.GetName().Name)));
services.AddScoped<IPersistMessageDbContext>(provider => provider.GetService<PersistMessageDbContext>());
services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>(); services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>();
services.AddHostedService<PersistMessageBackgroundService>(); services.AddHostedService<PersistMessageBackgroundService>();

View File

@ -1,9 +0,0 @@
using BuildingBlocks.EFCore;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor;
public interface IPersistMessageDbContext : IDbContext
{
DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
}

View File

@ -2,6 +2,7 @@
using System.Text.Json; using System.Text.Json;
using Ardalis.GuardClauses; using Ardalis.GuardClauses;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.EFCore;
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using MassTransit; using MassTransit;
@ -15,18 +16,18 @@ public class PersistMessageProcessor : IPersistMessageProcessor
{ {
private readonly ILogger<PersistMessageProcessor> _logger; private readonly ILogger<PersistMessageProcessor> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly IPersistMessageDbContext _persistMessageDbContext; private readonly IDbContext _dbContext;
private readonly IPublishEndpoint _publishEndpoint; private readonly IPublishEndpoint _publishEndpoint;
public PersistMessageProcessor( public PersistMessageProcessor(
ILogger<PersistMessageProcessor> logger, ILogger<PersistMessageProcessor> logger,
IMediator mediator, IMediator mediator,
IPersistMessageDbContext persistMessageDbContext, IDbContext dbContext,
IPublishEndpoint publishEndpoint) IPublishEndpoint publishEndpoint)
{ {
_logger = logger; _logger = logger;
_mediator = mediator; _mediator = mediator;
_persistMessageDbContext = persistMessageDbContext; _dbContext = dbContext;
_publishEndpoint = publishEndpoint; _publishEndpoint = publishEndpoint;
} }
@ -54,13 +55,13 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate, public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return (await _persistMessageDbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken)) return (await _dbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken))
.AsReadOnly(); .AsReadOnly();
} }
public Task<PersistMessage> ExistMessageAsync(long messageId, CancellationToken cancellationToken = default) public Task<PersistMessage> ExistMessageAsync(long messageId, CancellationToken cancellationToken = default)
{ {
return _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(x => return _dbContext.PersistMessages.FirstOrDefaultAsync(x =>
x.Id == messageId && x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox && x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.Processed, x.MessageStatus == MessageStatus.Processed,
@ -73,7 +74,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var message = var message =
await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync( await _dbContext.PersistMessages.FirstOrDefaultAsync(
x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken); x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken);
if (message is null) if (message is null)
@ -110,7 +111,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessAllAsync(CancellationToken cancellationToken = default) public async Task ProcessAllAsync(CancellationToken cancellationToken = default)
{ {
var messages = await _persistMessageDbContext.PersistMessages var messages = await _dbContext.PersistMessages
.Where(x => x.MessageStatus != MessageStatus.Processed) .Where(x => x.MessageStatus != MessageStatus.Processed)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@ -119,7 +120,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessInboxAsync(long messageId, CancellationToken cancellationToken = default) public async Task ProcessInboxAsync(long messageId, CancellationToken cancellationToken = default)
{ {
var message = await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync( var message = await _dbContext.PersistMessages.FirstOrDefaultAsync(
x => x.Id == messageId && x => x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox && x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.InProgress, x.MessageStatus == MessageStatus.InProgress,
@ -190,7 +191,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
else else
id = SnowFlakIdGenerator.NewId(); id = SnowFlakIdGenerator.NewId();
await _persistMessageDbContext.PersistMessages.AddAsync( await _dbContext.PersistMessages.AddAsync(
new PersistMessage( new PersistMessage(
id, id,
messageEnvelope.Message.GetType().ToString(), messageEnvelope.Message.GetType().ToString(),
@ -198,7 +199,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
deliveryType), deliveryType),
cancellationToken); cancellationToken);
await _persistMessageDbContext.SaveChangesAsync(cancellationToken); await _dbContext.SaveChangesAsync(cancellationToken);
_logger.LogInformation( _logger.LogInformation(
"Message with id: {MessageID} and delivery type: {DeliveryType} saved in persistence message store.", "Message with id: {MessageID} and delivery type: {DeliveryType} saved in persistence message store.",
@ -212,8 +213,8 @@ public class PersistMessageProcessor : IPersistMessageProcessor
{ {
message.ChangeState(MessageStatus.Processed); message.ChangeState(MessageStatus.Processed);
_persistMessageDbContext.PersistMessages.Update(message); _dbContext.PersistMessages.Update(message);
await _persistMessageDbContext.SaveChangesAsync(cancellationToken); await _dbContext.SaveChangesAsync(cancellationToken);
} }
} }

View File

@ -2,7 +2,6 @@
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo; using BuildingBlocks.Mongo;
using BuildingBlocks.PersistMessageProcessor; using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Web; using BuildingBlocks.Web;
@ -14,6 +13,7 @@ using MediatR;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -21,33 +21,31 @@ using Microsoft.Extensions.Options;
using Mongo2Go; using Mongo2Go;
using NSubstitute; using NSubstitute;
using Respawn; using Respawn;
using Respawn.Graph;
using Serilog; using Serilog;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace BuildingBlocks.TestBase; namespace BuildingBlocks.TestBase;
public class IntegrationTestFixture<TEntryPoint> : IDisposable public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class where TEntryPoint : class
{ {
private readonly WebApplicationFactory<TEntryPoint> _factory; private readonly WebApplicationFactory<TEntryPoint> _factory;
private int Timeout => 60; // Second private int Timeout => 60; // Second
private ITestHarness TestHarness => ServiceProvider.GetTestHarness();
public HttpClient HttpClient => _factory.CreateClient(); public MsSqlTestcontainer MsSqlTestContainer;
public RabbitMqTestcontainer RabbitMqTestContainer;
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
public HttpClient HttpClient => _factory?.CreateClient();
public GrpcChannel Channel => public GrpcChannel Channel =>
GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient}); GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
public Action<IServiceCollection> TestRegistrationServices { get; set; } public Action<IServiceCollection> TestRegistrationServices { get; set; }
public IServiceProvider ServiceProvider => _factory.Services; public IServiceProvider ServiceProvider => _factory?.Services;
public IConfiguration Configuration => _factory.Services.GetRequiredService<IConfiguration>(); public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
public MsSqlTestcontainer SqlTestContainer;
public MsSqlTestcontainer SqlPersistTestContainer;
public MongoDbTestcontainer MongoTestContainer;
public RabbitMqTestcontainer RabbitMqTestContainer;
public IntegrationTestFixture() public IntegrationTestFixture()
{ {
@ -60,12 +58,21 @@ public class IntegrationTestFixture<TEntryPoint> : IDisposable
TestRegistrationServices?.Invoke(services); TestRegistrationServices?.Invoke(services);
services.ReplaceSingleton(AddHttpContextAccessorMock); services.ReplaceSingleton(AddHttpContextAccessorMock);
}); });
builder.ConfigureAppConfiguration(AddCustomAppSettings);
}); });
} }
public void Dispose()
public async Task InitializeAsync()
{ {
_factory.Dispose(); await StartTestContainerAsync();
}
public async Task DisposeAsync()
{
await StopTestContainerAsync();
_factory?.DisposeAsync();
} }
public virtual void RegisterServices(Action<IServiceCollection> services) public virtual void RegisterServices(Action<IServiceCollection> services)
@ -190,11 +197,39 @@ public class IntegrationTestFixture<TEntryPoint> : IDisposable
}); });
} }
private async Task StartTestContainerAsync()
{
MsSqlTestContainer = TestContainers.MsSqlTestContainer;
RabbitMqTestContainer = TestContainers.RabbitMqTestContainer;
await MsSqlTestContainer.StartAsync();
await RabbitMqTestContainer.StartAsync();
}
private async Task StopTestContainerAsync()
{
await MsSqlTestContainer.StopAsync();
await RabbitMqTestContainer.StopAsync();
}
private void AddCustomAppSettings(IConfigurationBuilder configuration)
{
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
{
new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username),
new("RabbitMqOptions:Password", RabbitMqTestContainer.Password),
new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString())
});
}
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
{ {
var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>(); var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>();
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider}; httpContextAccessorMock.HttpContext = new DefaultHttpContext { RequestServices = scope.ServiceProvider };
httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012); httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012);
httpContextAccessorMock.HttpContext.Request.Scheme = "http"; httpContextAccessorMock.HttpContext.Request.Scheme = "http";
@ -329,32 +364,9 @@ public class IntegrationTestFixture<TEntryPoint, TWContext, TRContext> : Integra
public class IntegrationTestFixtureCore<TEntryPoint> : IAsyncLifetime public class IntegrationTestFixtureCore<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class where TEntryPoint : class
{ {
private Checkpoint _checkpointDefaultDB; private Respawner _reSpawnerDefaultDb;
private Checkpoint _checkpointPersistMessageDB;
private MongoDbRunner _mongoRunner; private MongoDbRunner _mongoRunner;
private string DefaultConnectionString
{
get => Fixture.ServiceProvider.GetRequiredService<IOptions<ConnectionStrings>>()?.Value.DefaultConnection;
set => Fixture.ServiceProvider.GetRequiredService<IOptions<ConnectionStrings>>().Value.DefaultConnection =
value;
}
private string PersistConnectionString
{
get => Fixture.ServiceProvider.GetRequiredService<IOptions<PersistMessageOptions>>()?.Value.ConnectionString;
set => Fixture.ServiceProvider.GetRequiredService<IOptions<PersistMessageOptions>>().Value.ConnectionString =
value;
}
private string MongoConnectionString
{
get => Fixture.ServiceProvider.GetRequiredService<IOptions<MongoOptions>>()?.Value?.ConnectionString;
set => Fixture.ServiceProvider.GetRequiredService<IOptions<MongoOptions>>().Value.ConnectionString = value;
}
private RabbitMqOptions RabbitMqOptions =>
Fixture.ServiceProvider.GetRequiredService<IOptions<RabbitMqOptions>>()?.Value;
public IntegrationTestFixtureCore(IntegrationTestFixture<TEntryPoint> integrationTestFixture) public IntegrationTestFixtureCore(IntegrationTestFixture<TEntryPoint> integrationTestFixture)
{ {
@ -364,71 +376,43 @@ public class IntegrationTestFixtureCore<TEntryPoint> : IAsyncLifetime
public IntegrationTestFixture<TEntryPoint> Fixture { get; } public IntegrationTestFixture<TEntryPoint> Fixture { get; }
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
_checkpointDefaultDB = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; var databaseOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<DatabaseOptions>>()?.Value;
_checkpointPersistMessageDB = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; var mongoOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<MongoOptions>>()?.Value;
if (!string.IsNullOrEmpty(DefaultConnectionString)) if (!string.IsNullOrEmpty(databaseOptions?.DefaultConnection))
await _checkpointDefaultDB.Reset(DefaultConnectionString); {
var dbConnection = new SqlConnection(databaseOptions.DefaultConnection);
await dbConnection.OpenAsync();
if (!string.IsNullOrEmpty(PersistConnectionString)) _reSpawnerDefaultDb = await Respawner.CreateAsync(dbConnection,
await _checkpointPersistMessageDB.Reset(PersistConnectionString); new RespawnerOptions
{
TablesToIgnore = new Table[] { "__EFMigrationsHistory" },
});
await _reSpawnerDefaultDb.ResetAsync(dbConnection);
}
_mongoRunner = MongoDbRunner.Start(); _mongoRunner = MongoDbRunner.Start();
if (MongoConnectionString != null) if (!string.IsNullOrEmpty(mongoOptions?.ConnectionString))
MongoConnectionString = _mongoRunner.ConnectionString; mongoOptions.ConnectionString = _mongoRunner.ConnectionString;
//await StartTestContainerAsync();
await SeedDataAsync(); await SeedDataAsync();
} }
public async Task DisposeAsync() public async Task DisposeAsync()
{ {
if (!string.IsNullOrEmpty(PersistConnectionString)) _mongoRunner.Dispose();
_mongoRunner.Dispose();
//await StopTestContainerAsync();
} }
protected virtual void RegisterTestsServices(IServiceCollection services) protected virtual void RegisterTestsServices(IServiceCollection services)
{ {
} }
private async Task StartTestContainerAsync()
{
// <<For using test-container base>>
Fixture.SqlTestContainer = TestContainers.SqlTestContainer;
Fixture.SqlPersistTestContainer = TestContainers.SqlPersistTestContainer;
Fixture.MongoTestContainer = TestContainers.MongoTestContainer;
Fixture.RabbitMqTestContainer = TestContainers.RabbitMqTestContainer;
await Fixture.SqlTestContainer.StartAsync();
await Fixture.SqlPersistTestContainer.StartAsync();
await Fixture.MongoTestContainer.StartAsync();
await Fixture.RabbitMqTestContainer.StartAsync();
DefaultConnectionString = Fixture.SqlTestContainer?.ConnectionString;
PersistConnectionString = Fixture.SqlPersistTestContainer?.ConnectionString;
MongoConnectionString = Fixture.MongoTestContainer?.ConnectionString;
RabbitMqOptions.Password = Fixture.RabbitMqTestContainer.Password;
RabbitMqOptions.UserName = Fixture.RabbitMqTestContainer.Username;
RabbitMqOptions.HostName = Fixture.RabbitMqTestContainer.Hostname;
RabbitMqOptions.Port = (ushort)Fixture.RabbitMqTestContainer.Port;
}
private async Task StopTestContainerAsync()
{
// <<For using test-container base>>
await Fixture.SqlTestContainer.StopAsync();
await Fixture.SqlPersistTestContainer.StopAsync();
await Fixture.MongoTestContainer.StopAsync();
await Fixture.RabbitMqTestContainer.StopAsync();
}
private async Task SeedDataAsync() private async Task SeedDataAsync()
{ {
using var scope = Fixture.ServiceProvider.CreateScope(); using var scope = Fixture.ServiceProvider.CreateScope();

View File

@ -6,24 +6,30 @@ namespace BuildingBlocks.TestBase;
public static class TestContainers public static class TestContainers
{ {
public static MsSqlTestcontainer SqlTestContainer => new TestcontainersBuilder<MsSqlTestcontainer>() public static PostgreSqlTestcontainer PostgresTestContainer => new TestcontainersBuilder<PostgreSqlTestcontainer>()
.WithDatabase( .WithDatabase(
new MsSqlTestcontainerConfiguration new PostgreSqlTestcontainerConfiguration
{ {
Database = Guid.NewGuid().ToString("D"), Database = Guid.NewGuid().ToString("D"),
Password = Guid.NewGuid().ToString("D") Password = Guid.NewGuid().ToString("D"),
Username = Guid.NewGuid().ToString("D")
}) })
.WithImage("mcr.microsoft.com/mssql/server:2017-latest") .WithImage("postgres:latest")
.Build(); .Build();
public static MsSqlTestcontainer SqlPersistTestContainer => new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new MsSqlTestcontainerConfiguration // issue ref: https://github.com/testcontainers/testcontainers-dotnet/discussions/533
public static MsSqlTestcontainer MsSqlTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new MsSqlTestcontainerConfiguration()
{ {
Database = Guid.NewGuid().ToString("D"), Password = Guid.NewGuid().ToString("D") Password = Guid.NewGuid().ToString("D")
}) })
.WithImage("mcr.microsoft.com/mssql/server:2017-latest") .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.WithExposedPort(1433)
.WithPortBinding(1433, true) // Add this line for issue in hangup MsSqlTestContainer in docker desktop
.Build(); .Build();
public static MongoDbTestcontainer MongoTestContainer => new TestcontainersBuilder<MongoDbTestcontainer>() public static MongoDbTestcontainer MongoTestContainer => new TestcontainersBuilder<MongoDbTestcontainer>()
.WithDatabase(new MongoDbTestcontainerConfiguration() .WithDatabase(new MongoDbTestcontainerConfiguration()
{ {
@ -34,12 +40,9 @@ public static class TestContainers
.WithImage("mongo") .WithImage("mongo")
.Build(); .Build();
public static RabbitMqTestcontainer RabbitMqTestContainer => new TestcontainersBuilder<RabbitMqTestcontainer>() public static RabbitMqTestcontainer RabbitMqTestContainer => new TestcontainersBuilder<RabbitMqTestcontainer>()
.WithMessageBroker(new RabbitMqTestcontainerConfiguration() .WithMessageBroker(new RabbitMqTestcontainerConfiguration() { Password = "guest", Username = "guest" })
{
Password = "guest",
Username = "guest"
})
.WithImage("rabbitmq:3-management") .WithImage("rabbitmq:3-management")
.Build(); .Build();
} }

View File

@ -6,7 +6,7 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "rabbitmq", "HostName": "rabbitmq",
"ExchangeName": "booking", "ExchangeName": "booking",
"UserName": "guest", "UserName": "guest",

View File

@ -19,7 +19,7 @@
"Authority": "https://localhost:5005", "Authority": "https://localhost:5005",
"Audience": "booking-api" "Audience": "booking-api"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "booking", "ExchangeName": "booking",
"UserName": "guest", "UserName": "guest",
@ -40,7 +40,7 @@
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true, "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "ConnectionString": "Server=localhost;Port=5432;Database=persist_message_db;User Id=postgres;Password=postgres;Include Error Detail=true"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,5 +1,5 @@
{ {
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "booking", "ExchangeName": "booking",
"UserName": "guest", "UserName": "guest",
@ -17,7 +17,7 @@
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 1,
"Enabled": true, "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageTestDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "ConnectionString": "Server=localhost;Port=5432;Database=persist_message_db_test;User Id=postgres;Password=postgres;Include Error Detail=true"
}, },
"MongoOptions": { "MongoOptions": {
"ConnectionString": "mongodb://localhost:27017", "ConnectionString": "mongodb://localhost:27017",

View File

@ -43,7 +43,7 @@ public static class InfrastructureExtensions
options.SuppressModelStateInvalidFilter = true; options.SuppressModelStateInvalidFilter = true;
}); });
var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions"); var appOptions = builder.Services.GetOptions<AppOptions>(nameof(AppOptions));
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name)); Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
@ -58,7 +58,7 @@ public static class InfrastructureExtensions
})); }));
}); });
builder.Services.AddPersistMessage(configuration); builder.Services.AddPersistMessageProcessor();
builder.Services.AddMongoDbContext<BookingReadDbContext>(configuration); builder.Services.AddMongoDbContext<BookingReadDbContext>(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
@ -90,7 +90,7 @@ public static class InfrastructureExtensions
public static WebApplication UseInfrastructure(this WebApplication app) public static WebApplication UseInfrastructure(this WebApplication app)
{ {
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
app.UseProblemDetails(); app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();

View File

@ -1,18 +1,15 @@
using System.Collections.Generic; using System.Linq;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Booking.Api; using Booking.Api;
using Booking.Data; using Booking.Data;
using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.PersistMessageProcessor.Data; using BuildingBlocks.EFCore;
using BuildingBlocks.TestBase; using BuildingBlocks.TestBase;
using Flight; using Flight;
using FluentAssertions; using FluentAssertions;
using Grpc.Core; using Grpc.Core;
using Grpc.Core.Testing; using Grpc.Core.Testing;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using MassTransit;
using MassTransit.Testing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using NSubstitute; using NSubstitute;
@ -22,10 +19,10 @@ using GetByIdRequest = Flight.GetByIdRequest;
namespace Integration.Test.Booking.Features; namespace Integration.Test.Booking.Features;
public class CreateBookingTests : IntegrationTestBase<Program, PersistMessageDbContext, BookingReadDbContext> public class CreateBookingTests : IntegrationTestBase<Program, AppDbContextBase, BookingReadDbContext>
{ {
public CreateBookingTests( public CreateBookingTests(
IntegrationTestFixture<Program, PersistMessageDbContext, BookingReadDbContext> integrationTestFixture) : base( IntegrationTestFixture<Program, AppDbContextBase, BookingReadDbContext> integrationTestFixture) : base(
integrationTestFixture) integrationTestFixture)
{ {
} }

View File

@ -15,7 +15,7 @@
"interval": "day" "interval": "day"
} }
}, },
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"MongoOptions": { "MongoOptions": {
@ -26,7 +26,7 @@
"Authority": "https://localhost:5005", "Authority": "https://localhost:5005",
"Audience": "flight-api" "Audience": "flight-api"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "flight", "ExchangeName": "flight",
"UserName": "guest", "UserName": "guest",
@ -35,8 +35,7 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,8 +1,8 @@
{ {
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "flight", "ExchangeName": "flight",
"UserName": "guest", "UserName": "guest",
@ -19,7 +19,6 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 1,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageTestDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -1,3 +1,4 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
@ -8,7 +9,7 @@ public class AircraftConfiguration : IEntityTypeConfiguration<Aircraft>
{ {
public void Configure(EntityTypeBuilder<Aircraft> builder) public void Configure(EntityTypeBuilder<Aircraft> builder)
{ {
builder.ToTable("Aircraft", FlightDbContext.DefaultSchema); builder.ToTable("Aircraft", AppDbContextBase.DefaultSchema);
builder.HasKey(r => r.Id); builder.HasKey(r => r.Id);
builder.Property(r => r.Id).ValueGeneratedNever(); builder.Property(r => r.Id).ValueGeneratedNever();
} }

View File

@ -1,3 +1,4 @@
using BuildingBlocks.EFCore;
using Flight.Airports.Models; using Flight.Airports.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
@ -8,7 +9,7 @@ public class AirportConfiguration: IEntityTypeConfiguration<Airport>
{ {
public void Configure(EntityTypeBuilder<Airport> builder) public void Configure(EntityTypeBuilder<Airport> builder)
{ {
builder.ToTable("Airport", FlightDbContext.DefaultSchema); builder.ToTable("Airport", AppDbContextBase.DefaultSchema);
builder.HasKey(r => r.Id); builder.HasKey(r => r.Id);
builder.Property(r => r.Id).ValueGeneratedNever(); builder.Property(r => r.Id).ValueGeneratedNever();

View File

@ -1,3 +1,4 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Flight.Airports.Models; using Flight.Airports.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -9,7 +10,7 @@ public class FlightConfiguration : IEntityTypeConfiguration<Flights.Models.Fligh
{ {
public void Configure(EntityTypeBuilder<Flights.Models.Flight> builder) public void Configure(EntityTypeBuilder<Flights.Models.Flight> builder)
{ {
builder.ToTable("Flight", FlightDbContext.DefaultSchema); builder.ToTable("Flight", AppDbContextBase.DefaultSchema);
builder.HasKey(r => r.Id); builder.HasKey(r => r.Id);
builder.Property(r => r.Id).ValueGeneratedNever(); builder.Property(r => r.Id).ValueGeneratedNever();

View File

@ -0,0 +1,45 @@
using System;
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Flight.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{
public void Configure(EntityTypeBuilder<PersistMessage> builder)
{
builder.ToTable("PersistMessage", AppDbContextBase.DefaultSchema);
builder.HasKey(x => x.Id);
builder.Property(r => r.Id).ValueGeneratedNever();
builder.Property(x => x.Id)
.IsRequired();
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.MessageStatus)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v))
.IsRequired()
.IsUnicode(false);
}
}

View File

@ -1,3 +1,4 @@
using BuildingBlocks.EFCore;
using Flight.Seats.Models; using Flight.Seats.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
@ -8,7 +9,7 @@ public class SeatConfiguration : IEntityTypeConfiguration<Seat>
{ {
public void Configure(EntityTypeBuilder<Seat> builder) public void Configure(EntityTypeBuilder<Seat> builder)
{ {
builder.ToTable("Seat", FlightDbContext.DefaultSchema); builder.ToTable("Seat", AppDbContextBase.DefaultSchema);
builder.HasKey(r => r.Id); builder.HasKey(r => r.Id);
builder.Property(r => r.Id).ValueGeneratedNever(); builder.Property(r => r.Id).ValueGeneratedNever();

View File

@ -9,8 +9,7 @@ namespace Flight.Data
{ {
var builder = new DbContextOptionsBuilder<FlightDbContext>(); var builder = new DbContextOptionsBuilder<FlightDbContext>();
builder.UseSqlServer( builder.UseSqlServer("Data Source=.\\sqlexpress;Initial Catalog=FlightDB;Persist Security Info=False;Integrated Security=SSPI;TrustServerCertificate=True");
"Data Source=.\\sqlexpress;Initial Catalog=FlightDB;Persist Security Info=False;Integrated Security=SSPI");
return new FlightDbContext(builder.Options, null); return new FlightDbContext(builder.Options, null);
} }
} }

View File

@ -1,22 +1,18 @@
using System.Reflection;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Flight.Airports.Models; using Flight.Airports.Models;
using Flight.Seats.Models; using Flight.Seats.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Flight.Data; namespace Flight.Data;
public sealed class FlightDbContext : AppDbContextBase public sealed class FlightDbContext : AppDbContextBase
{ {
public const string DefaultSchema = "dbo";
public FlightDbContext(DbContextOptions<FlightDbContext> options, ICurrentUserProvider currentUserProvider) : base( public FlightDbContext(DbContextOptions<FlightDbContext> options, ICurrentUserProvider currentUserProvider) : base(
options, currentUserProvider) options, currentUserProvider)
{ {
} }
public DbSet<Flights.Models.Flight> Flights => Set<Flights.Models.Flight>(); public DbSet<Flights.Models.Flight> Flights => Set<Flights.Models.Flight>();
public DbSet<Airport> Airports => Set<Airport>(); public DbSet<Airport> Airports => Set<Airport>();
public DbSet<Aircraft> Aircraft => Set<Aircraft>(); public DbSet<Aircraft> Aircraft => Set<Aircraft>();
@ -24,8 +20,8 @@ public sealed class FlightDbContext : AppDbContextBase
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder);
builder.FilterSoftDeletedProperties(); builder.FilterSoftDeletedProperties();
builder.ApplyConfigurationsFromAssembly(typeof(FlightRoot).Assembly); builder.ApplyConfigurationsFromAssembly(typeof(FlightRoot).Assembly);
base.OnModelCreating(builder);
} }
} }

View File

@ -12,17 +12,52 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Flight.Data.Migrations namespace Flight.Data.Migrations
{ {
[DbContext(typeof(FlightDbContext))] [DbContext(typeof(FlightDbContext))]
[Migration("20220728175834_Init")] [Migration("20221203201848_Initial")]
partial class Init partial class Initial
{ {
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b => modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b =>
{ {

View File

@ -5,8 +5,10 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Flight.Data.Migrations namespace Flight.Data.Migrations
{ {
public partial class Init : Migration /// <inheritdoc />
public partial class Initial : Migration
{ {
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.EnsureSchema( migrationBuilder.EnsureSchema(
@ -54,6 +56,24 @@ namespace Flight.Data.Migrations
table.PrimaryKey("PK_Airport", x => x.Id); table.PrimaryKey("PK_Airport", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Flight", name: "Flight",
schema: "dbo", schema: "dbo",
@ -144,8 +164,13 @@ namespace Flight.Data.Migrations
column: "FlightId"); column: "FlightId");
} }
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Seat", name: "Seat",
schema: "dbo"); schema: "dbo");

View File

@ -17,10 +17,44 @@ namespace Flight.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b => modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b =>
{ {

View File

@ -52,7 +52,7 @@ public static class InfrastructureExtensions
builder.Services.AddCustomMediatR(); builder.Services.AddCustomMediatR();
builder.Services.AddCustomProblemDetails(); builder.Services.AddCustomProblemDetails();
var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions"); var appOptions = builder.Services.GetOptions<AppOptions>(nameof(AppOptions));
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name)); Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.Services.AddRateLimiter(options => builder.Services.AddRateLimiter(options =>
@ -66,11 +66,11 @@ public static class InfrastructureExtensions
})); }));
}); });
builder.Services.AddPersistMessageProcessor();
builder.Services.AddCustomDbContext<FlightDbContext>(configuration); builder.Services.AddCustomDbContext<FlightDbContext>(configuration);
builder.Services.AddScoped<IDataSeeder, FlightDataSeeder>(); builder.Services.AddScoped<IDataSeeder, FlightDataSeeder>();
builder.Services.AddMongoDbContext<FlightReadDbContext>(configuration); builder.Services.AddMongoDbContext<FlightReadDbContext>(configuration);
builder.Services.AddPersistMessage(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddJwt(); builder.Services.AddJwt();
@ -99,7 +99,7 @@ public static class InfrastructureExtensions
public static WebApplication UseInfrastructure(this WebApplication app) public static WebApplication UseInfrastructure(this WebApplication app)
{ {
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
app.UseProblemDetails(); app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();

View File

@ -1,9 +1,9 @@
{ {
"App": "Identity-Service", "App": "Identity-Service",
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=db;Database=IdentityDB;User ID=sa;Password=@Aa123456" "DefaultConnection": "Server=db;Database=IdentityDB;User ID=sa;Password=@Aa123456"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "rabbitmq", "HostName": "rabbitmq",
"ExchangeName": "identity", "ExchangeName": "identity",
"UserName": "guest", "UserName": "guest",

View File

@ -2,10 +2,10 @@
"AppOptions": { "AppOptions": {
"Name": "Identity-Service" "Name": "Identity-Service"
}, },
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=IdentityDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=IdentityDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "identity", "ExchangeName": "identity",
"UserName": "guest", "UserName": "guest",
@ -31,8 +31,7 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,8 +1,8 @@
{ {
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=IdentityDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=IdentityDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "identity", "ExchangeName": "identity",
"UserName": "guest", "UserName": "guest",
@ -19,7 +19,6 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 1,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageTestDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -0,0 +1,44 @@
using System;
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Identity.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{
public void Configure(EntityTypeBuilder<PersistMessage> builder)
{
builder.ToTable("PersistMessage", AppDbContextBase.DefaultSchema);
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.IsRequired();
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.MessageStatus)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v))
.IsRequired()
.IsUnicode(false);
}
}

View File

@ -9,7 +9,7 @@ public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<IdentityCo
{ {
var builder = new DbContextOptionsBuilder<IdentityContext>(); var builder = new DbContextOptionsBuilder<IdentityContext>();
builder.UseSqlServer("Server=.\\sqlexpress;Database=IdentityDB;Trusted_Connection=True;MultipleActiveResultSets=true"); builder.UseSqlServer("Server=.\\sqlexpress;Database=IdentityDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True");
return new IdentityContext(builder.Options, null); return new IdentityContext(builder.Options, null);
} }
} }

View File

@ -8,11 +8,13 @@ using System.Threading.Tasks;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using Humanizer;
using Identity.Identity.Models; using Identity.Identity.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
namespace Identity.Data; namespace Identity.Data;
@ -21,8 +23,6 @@ public sealed class IdentityContext : IdentityDbContext<ApplicationUser, Identit
IdentityUserClaim<long>, IdentityUserClaim<long>,
IdentityUserRole<long>, IdentityUserLogin<long>, IdentityRoleClaim<long>, IdentityUserToken<long>>, IDbContext IdentityUserRole<long>, IdentityUserLogin<long>, IdentityRoleClaim<long>, IdentityUserToken<long>>, IDbContext
{ {
public const string DefaultSchema = "dbo";
private IDbContextTransaction _currentTransaction; private IDbContextTransaction _currentTransaction;
public IdentityContext(DbContextOptions<IdentityContext> options, IHttpContextAccessor httpContextAccessor) : public IdentityContext(DbContextOptions<IdentityContext> options, IHttpContextAccessor httpContextAccessor) :
@ -35,6 +35,13 @@ public sealed class IdentityContext : IdentityDbContext<ApplicationUser, Identit
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
base.OnModelCreating(builder); base.OnModelCreating(builder);
// https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/
foreach (var entity in builder.Model.GetEntityTypes())
{
// Replace schema
entity.SetSchema(AppDbContextBase.DefaultSchema);
}
builder.FilterSoftDeletedProperties(); builder.FilterSoftDeletedProperties();
} }

View File

@ -12,17 +12,55 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Identity.Data.Migrations namespace Identity.Data.Migrations
{ {
[DbContext(typeof(IdentityContext))] [DbContext(typeof(IdentityContext))]
[Migration("20220728175937_initial")] [Migration("20221203211306_initial")]
partial class initial partial class initial
{ {
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b => modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b =>
{ {
@ -30,7 +68,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("bigint"); .HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<int>("AccessFailedCount") b.Property<int>("AccessFailedCount")
.HasColumnType("int"); .HasColumnType("int");
@ -98,7 +136,7 @@ namespace Identity.Data.Migrations
.HasDatabaseName("UserNameIndex") .HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL"); .HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null); b.ToTable("AspNetUsers", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<long>", b =>
@ -107,7 +145,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("bigint"); .HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken() .IsConcurrencyToken()
@ -128,7 +166,7 @@ namespace Identity.Data.Migrations
.HasDatabaseName("RoleNameIndex") .HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null); b.ToTable("AspNetRoles", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
@ -137,7 +175,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType") b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
@ -152,7 +190,7 @@ namespace Identity.Data.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null); b.ToTable("AspNetRoleClaims", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
@ -161,7 +199,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType") b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
@ -176,7 +214,7 @@ namespace Identity.Data.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null); b.ToTable("AspNetUserClaims", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
@ -197,7 +235,7 @@ namespace Identity.Data.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null); b.ToTable("AspNetUserLogins", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
@ -212,7 +250,7 @@ namespace Identity.Data.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null); b.ToTable("AspNetUserRoles", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
@ -231,7 +269,7 @@ namespace Identity.Data.Migrations
b.HasKey("UserId", "LoginProvider", "Name"); b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>

View File

@ -5,12 +5,18 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Identity.Data.Migrations namespace Identity.Data.Migrations
{ {
/// <inheritdoc />
public partial class initial : Migration public partial class initial : Migration
{ {
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.EnsureSchema(
name: "dbo");
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetRoles", name: "AspNetRoles",
schema: "dbo",
columns: table => new columns: table => new
{ {
Id = table.Column<long>(type: "bigint", nullable: false) Id = table.Column<long>(type: "bigint", nullable: false)
@ -26,6 +32,7 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUsers", name: "AspNetUsers",
schema: "dbo",
columns: table => new columns: table => new
{ {
Id = table.Column<long>(type: "bigint", nullable: false) Id = table.Column<long>(type: "bigint", nullable: false)
@ -53,8 +60,28 @@ namespace Identity.Data.Migrations
table.PrimaryKey("PK_AspNetUsers", x => x.Id); table.PrimaryKey("PK_AspNetUsers", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetRoleClaims", name: "AspNetRoleClaims",
schema: "dbo",
columns: table => new columns: table => new
{ {
Id = table.Column<int>(type: "int", nullable: false) Id = table.Column<int>(type: "int", nullable: false)
@ -69,6 +96,7 @@ namespace Identity.Data.Migrations
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId, column: x => x.RoleId,
principalSchema: "dbo",
principalTable: "AspNetRoles", principalTable: "AspNetRoles",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
@ -76,6 +104,7 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUserClaims", name: "AspNetUserClaims",
schema: "dbo",
columns: table => new columns: table => new
{ {
Id = table.Column<int>(type: "int", nullable: false) Id = table.Column<int>(type: "int", nullable: false)
@ -90,6 +119,7 @@ namespace Identity.Data.Migrations
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId", name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId, column: x => x.UserId,
principalSchema: "dbo",
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
@ -97,6 +127,7 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUserLogins", name: "AspNetUserLogins",
schema: "dbo",
columns: table => new columns: table => new
{ {
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false), LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
@ -110,6 +141,7 @@ namespace Identity.Data.Migrations
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId", name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId, column: x => x.UserId,
principalSchema: "dbo",
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
@ -117,6 +149,7 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUserRoles", name: "AspNetUserRoles",
schema: "dbo",
columns: table => new columns: table => new
{ {
UserId = table.Column<long>(type: "bigint", nullable: false), UserId = table.Column<long>(type: "bigint", nullable: false),
@ -128,12 +161,14 @@ namespace Identity.Data.Migrations
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId", name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId, column: x => x.RoleId,
principalSchema: "dbo",
principalTable: "AspNetRoles", principalTable: "AspNetRoles",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId", name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId, column: x => x.UserId,
principalSchema: "dbo",
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
@ -141,6 +176,7 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUserTokens", name: "AspNetUserTokens",
schema: "dbo",
columns: table => new columns: table => new
{ {
UserId = table.Column<long>(type: "bigint", nullable: false), UserId = table.Column<long>(type: "bigint", nullable: false),
@ -154,6 +190,7 @@ namespace Identity.Data.Migrations
table.ForeignKey( table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId", name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId, column: x => x.UserId,
principalSchema: "dbo",
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
@ -161,11 +198,13 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId", name: "IX_AspNetRoleClaims_RoleId",
schema: "dbo",
table: "AspNetRoleClaims", table: "AspNetRoleClaims",
column: "RoleId"); column: "RoleId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "RoleNameIndex", name: "RoleNameIndex",
schema: "dbo",
table: "AspNetRoles", table: "AspNetRoles",
column: "NormalizedName", column: "NormalizedName",
unique: true, unique: true,
@ -173,54 +212,71 @@ namespace Identity.Data.Migrations
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId", name: "IX_AspNetUserClaims_UserId",
schema: "dbo",
table: "AspNetUserClaims", table: "AspNetUserClaims",
column: "UserId"); column: "UserId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId", name: "IX_AspNetUserLogins_UserId",
schema: "dbo",
table: "AspNetUserLogins", table: "AspNetUserLogins",
column: "UserId"); column: "UserId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId", name: "IX_AspNetUserRoles_RoleId",
schema: "dbo",
table: "AspNetUserRoles", table: "AspNetUserRoles",
column: "RoleId"); column: "RoleId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "EmailIndex", name: "EmailIndex",
schema: "dbo",
table: "AspNetUsers", table: "AspNetUsers",
column: "NormalizedEmail"); column: "NormalizedEmail");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "UserNameIndex", name: "UserNameIndex",
schema: "dbo",
table: "AspNetUsers", table: "AspNetUsers",
column: "NormalizedUserName", column: "NormalizedUserName",
unique: true, unique: true,
filter: "[NormalizedUserName] IS NOT NULL"); filter: "[NormalizedUserName] IS NOT NULL");
} }
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetRoleClaims"); name: "AspNetRoleClaims",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUserClaims"); name: "AspNetUserClaims",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUserLogins"); name: "AspNetUserLogins",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUserRoles"); name: "AspNetUserRoles",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUserTokens"); name: "AspNetUserTokens",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetRoles"); name: "PersistMessage",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUsers"); name: "AspNetRoles",
schema: "dbo");
migrationBuilder.DropTable(
name: "AspNetUsers",
schema: "dbo");
} }
} }
} }

View File

@ -17,10 +17,47 @@ namespace Identity.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b => modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b =>
{ {
@ -28,7 +65,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("bigint"); .HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<int>("AccessFailedCount") b.Property<int>("AccessFailedCount")
.HasColumnType("int"); .HasColumnType("int");
@ -96,7 +133,7 @@ namespace Identity.Data.Migrations
.HasDatabaseName("UserNameIndex") .HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL"); .HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null); b.ToTable("AspNetUsers", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<long>", b =>
@ -105,7 +142,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("bigint"); .HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken() .IsConcurrencyToken()
@ -126,7 +163,7 @@ namespace Identity.Data.Migrations
.HasDatabaseName("RoleNameIndex") .HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null); b.ToTable("AspNetRoles", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
@ -135,7 +172,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType") b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
@ -150,7 +187,7 @@ namespace Identity.Data.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null); b.ToTable("AspNetRoleClaims", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
@ -159,7 +196,7 @@ namespace Identity.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType") b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
@ -174,7 +211,7 @@ namespace Identity.Data.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null); b.ToTable("AspNetUserClaims", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
@ -195,7 +232,7 @@ namespace Identity.Data.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null); b.ToTable("AspNetUserLogins", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
@ -210,7 +247,7 @@ namespace Identity.Data.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null); b.ToTable("AspNetUserRoles", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
@ -229,7 +266,7 @@ namespace Identity.Data.Migrations
b.HasKey("UserId", "LoginProvider", "Name"); b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", "dbo");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>

View File

@ -41,7 +41,7 @@ public static class InfrastructureExtensions
options.SuppressModelStateInvalidFilter = true; options.SuppressModelStateInvalidFilter = true;
}); });
var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions"); var appOptions = builder.Services.GetOptions<AppOptions>(nameof(AppOptions));
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name)); Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.Services.AddRateLimiter(options => builder.Services.AddRateLimiter(options =>
@ -56,7 +56,7 @@ public static class InfrastructureExtensions
}); });
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddPersistMessage(configuration); builder.Services.AddPersistMessageProcessor();
builder.Services.AddCustomDbContext<IdentityContext>(configuration); builder.Services.AddCustomDbContext<IdentityContext>(configuration);
builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>(); builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>();
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
@ -82,7 +82,7 @@ public static class InfrastructureExtensions
public static WebApplication UseInfrastructure(this WebApplication app) public static WebApplication UseInfrastructure(this WebApplication app)
{ {
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
app.UseProblemDetails(); app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();

View File

@ -1,13 +1,13 @@
{ {
"App": "Passenger-Service", "App": "Passenger-Service",
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=db;Database=PassengerDB;User ID=sa;Password=@Aa123456" "DefaultConnection": "Server=db;Database=PassengerDB;User ID=sa;Password=@Aa123456"
}, },
"Jwt": { "Jwt": {
"Authority": "https://localhost:5005", "Authority": "https://localhost:5005",
"Audience": "passenger-api" "Audience": "passenger-api"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "rabbitmq", "HostName": "rabbitmq",
"ExchangeName": "passenger", "ExchangeName": "passenger",
"UserName": "guest", "UserName": "guest",

View File

@ -2,7 +2,7 @@
"AppOptions": { "AppOptions": {
"Name": "Passenger-Service" "Name": "Passenger-Service"
}, },
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=PassengerDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=PassengerDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"MongoOptions": { "MongoOptions": {
@ -13,7 +13,7 @@
"Authority": "https://localhost:5005", "Authority": "https://localhost:5005",
"Audience": "passenger-api" "Audience": "passenger-api"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "passenger", "ExchangeName": "passenger",
"UserName": "guest", "UserName": "guest",
@ -35,8 +35,7 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,8 +1,8 @@
{ {
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=.\\sqlexpress;Database=PassengerDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" "DefaultConnection": "Server=.\\sqlexpress;Database=PassengerDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"RabbitMq": { "RabbitMqOptions": {
"HostName": "localhost", "HostName": "localhost",
"ExchangeName": "passenger", "ExchangeName": "passenger",
"UserName": "guest", "UserName": "guest",
@ -19,7 +19,6 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 1,
"Enabled": true, "Enabled": true
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageTestDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -1,3 +1,4 @@
using BuildingBlocks.EFCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
@ -7,7 +8,7 @@ public class PassengerConfiguration: IEntityTypeConfiguration<Passengers.Models.
{ {
public void Configure(EntityTypeBuilder<Passengers.Models.Passenger> builder) public void Configure(EntityTypeBuilder<Passengers.Models.Passenger> builder)
{ {
builder.ToTable("Passenger", "dbo"); builder.ToTable("Passenger", AppDbContextBase.DefaultSchema);
builder.HasKey(r => r.Id); builder.HasKey(r => r.Id);
builder.Property(r => r.Id).ValueGeneratedNever(); builder.Property(r => r.Id).ValueGeneratedNever();

View File

@ -1,13 +1,15 @@
using Microsoft.EntityFrameworkCore; using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace BuildingBlocks.PersistMessageProcessor.Data.Configurations; namespace Passenger.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage> public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{ {
public void Configure(EntityTypeBuilder<PersistMessage> builder) public void Configure(EntityTypeBuilder<PersistMessage> builder)
{ {
builder.ToTable("PersistMessage", PersistMessageDbContext.DefaultSchema); builder.ToTable("PersistMessage", AppDbContextBase.DefaultSchema);
builder.HasKey(x => x.Id); builder.HasKey(x => x.Id);

View File

@ -10,7 +10,7 @@ public class DesignTimeDbContextFactory: IDesignTimeDbContextFactory<PassengerDb
var builder = new DbContextOptionsBuilder<PassengerDbContext>(); var builder = new DbContextOptionsBuilder<PassengerDbContext>();
builder.UseSqlServer( builder.UseSqlServer(
"Data Source=.\\sqlexpress;Initial Catalog=PassengerDB;Persist Security Info=False;Integrated Security=SSPI"); "Data Source=.\\sqlexpress;Initial Catalog=PassengerDB;Persist Security Info=False;Integrated Security=SSPI;TrustServerCertificate=True");
return new PassengerDbContext(builder.Options, null); return new PassengerDbContext(builder.Options, null);
} }
} }

View File

@ -12,17 +12,55 @@ using Passenger.Data;
namespace Passenger.Data.Migrations namespace Passenger.Data.Migrations
{ {
[DbContext(typeof(PassengerDbContext))] [DbContext(typeof(PassengerDbContext))]
[Migration("20220728180020_initial")] [Migration("20221203211633_initial")]
partial class initial partial class initial
{ {
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b => modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b =>
{ {

View File

@ -5,8 +5,10 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Passenger.Data.Migrations namespace Passenger.Data.Migrations
{ {
/// <inheritdoc />
public partial class initial : Migration public partial class initial : Migration
{ {
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.EnsureSchema( migrationBuilder.EnsureSchema(
@ -33,13 +35,37 @@ namespace Passenger.Data.Migrations
{ {
table.PrimaryKey("PK_Passenger", x => x.Id); table.PrimaryKey("PK_Passenger", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
} }
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Passenger", name: "Passenger",
schema: "dbo"); schema: "dbo");
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
} }
} }
} }

View File

@ -17,10 +17,47 @@ namespace Passenger.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b => modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b =>
{ {

View File

@ -45,7 +45,7 @@ public static class InfrastructureExtensions
options.SuppressModelStateInvalidFilter = true; options.SuppressModelStateInvalidFilter = true;
}); });
var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions"); var appOptions = builder.Services.GetOptions<AppOptions>(nameof(AppOptions));
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name)); Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.Services.AddRateLimiter(options => builder.Services.AddRateLimiter(options =>
@ -59,9 +59,9 @@ public static class InfrastructureExtensions
})); }));
}); });
builder.Services.AddPersistMessageProcessor();
builder.Services.AddCustomDbContext<PassengerDbContext>(configuration); builder.Services.AddCustomDbContext<PassengerDbContext>(configuration);
builder.Services.AddMongoDbContext<PassengerReadDbContext>(configuration); builder.Services.AddMongoDbContext<PassengerReadDbContext>(configuration);
builder.Services.AddPersistMessage(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddJwt(); builder.Services.AddJwt();
@ -89,7 +89,7 @@ public static class InfrastructureExtensions
public static WebApplication UseInfrastructure(this WebApplication app) public static WebApplication UseInfrastructure(this WebApplication app)
{ {
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
app.UseProblemDetails(); app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();