feat: update dotnet to version 9

This commit is contained in:
Meysam Hadeli 2024-12-18 23:28:18 +03:30
parent 42c300fd96
commit 4785a680da
57 changed files with 762 additions and 707 deletions

View File

@ -10,11 +10,11 @@
"rollForward": false
},
"dotnet-ef": {
"version": "8.0.8",
"version": "9.0.0",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}
}

View File

@ -241,6 +241,11 @@ dotnet_diagnostic.IDE0055.severity = suggestion
# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name'
dotnet_diagnostic.CS1574.severity = error
# IDE0160, IDE0161: Report violations when block-scoped namespaces are used
dotnet_diagnostic.IDE0160.severity = none
dotnet_diagnostic.IDE0161.severity = none
##################################################################################
# https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html
# https://jetbrains.com.xy2401.com/help/resharper/Reference__Code_Inspections_CSHARP.html

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@ -10,22 +10,22 @@
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Version="1.1.118">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.163">
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.182">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.5">
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.5">
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.5">
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.11.20">
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.12.19">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="8.0.0">
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="9.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="all" Version="0.15.0">

View File

@ -55,6 +55,7 @@
- :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`.
- :sparkle: Using `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR`.
- :sparkle: Using `Minimal API` for all endpoints.
- :sparkle: Using `AspNetCore OpenApi` for `generating` built-in support `OpenAPI documentation` in ASP.NET Core.
- :sparkle: Using `Health Check` for `reporting` the `health` of app infrastructure components.
- :sparkle: Using `Docker-Compose` and `Kubernetes` for our deployment mechanism.
- :sparkle: Using `Kibana` on top of `Serilog` for `logging`.
@ -85,24 +86,26 @@ High-level plan is represented in the table
## Technologies - Libraries
- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations
- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core.
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core.
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
- ✔️ **[`AspNetCore OpenApi`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?view=aspnetcore-9.0&tabs=visual-studio#configure-openapi-document-generation)** - Provides built-in support for OpenAPI document generation in ASP.NET Core.
- ✔️ **[`Masstransit`](https://github.com/MassTransit/MassTransit)** - Distributed Application Framework for .NET.
- ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET.
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules
- ✔️ **[`Swagger & Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules.
- ✔️ **[`Scalar`](https://github.com/scalar/scalar/tree/main/packages/scalar.aspnetcore)** - Scalar provides an easy way to render beautiful API references based on OpenAPI/Swagger documents.
- ✔️ **[`Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core.
- ✔️ **[`Serilog`](https://github.com/serilog/serilog)** - Simple .NET logging with fully-structured events
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
- ✔️ **[`Scrutor`](https://github.com/khellang/Scrutor)** - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
- ✔️ **[`Opentelemetry-dotnet`](https://github.com/open-telemetry/opentelemetry-dotnet)** - The OpenTelemetry .NET Client
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core.
- ✔️ **[`EasyCaching`](https://github.com/dotnetcore/EasyCaching)** - Open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier.
- ✔️ **[`Mapster`](https://github.com/MapsterMapper/Mapster)** - Convention-based object-object mapper in .NET.
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core.
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs.
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET.
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier.
- ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET.
- ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing.
- ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB.

View File

@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.401",
"version": "9.0.100",
"rollForward": "latestFeature"
}
}

View File

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="4.6.0" />
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" />
@ -19,41 +20,42 @@
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="Figgle" Version="0.5.1" />
<PackageReference Include="FluentValidation" Version="11.10.0" />
<PackageReference Include="FluentValidation" Version="11.11.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
<PackageReference Include="Npgsql" Version="8.0.4" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="9.0.0" />
<PackageReference Include="Npgsql" Version="9.0.1" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.10.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.9.0-beta.1" />
<PackageReference Include="Polly" Version="8.4.1" />
<PackageReference Include="Polly" Version="8.5.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="IdGen" Version="3.0.7" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Scalar.AspNetCore" Version="1.2.64" />
<PackageReference Include="Scrutor" Version="5.0.2" />
<PackageReference Include="Sentry.Serilog" Version="4.13.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
@ -63,48 +65,53 @@
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.5" />
<PackageReference Include="Sieve" Version="2.5.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
<PackageReference Include="MassTransit" Version="8.2.5" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.5" />
<PackageReference Include="Duende.IdentityServer" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.1.0" />
<PackageReference Include="MassTransit" Version="8.3.3" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.3.3" />
<PackageReference Include="Duende.IdentityServer" Version="7.0.8" />
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.8" />
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.8" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
<PackageReference Include="Testcontainers" Version="3.10.0" />
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.10.0" />
<PackageReference Include="Testcontainers.MongoDb" Version="3.10.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.10.0" />
<PackageReference Include="Testcontainers.RabbitMq" Version="3.10.0" />
<PackageReference Include="Testcontainers" Version="4.0.0" />
<PackageReference Include="Testcontainers.EventStoreDb" Version="4.0.0" />
<PackageReference Include="Testcontainers.MongoDb" Version="4.0.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.0.0" />
<PackageReference Include="Testcontainers.RabbitMq" Version="4.0.0" />
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
<PackageReference Include="prometheus-net" Version="8.2.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
<PackageReference Include="OpenTelemetry" Version="1.10.0" />
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.6.0-rc.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.10.0" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.7" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
<PackageReference Include="AutoBogus" Version="2.13.1" />
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Respawn" Version="6.2.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.65.0" />
<PackageReference Include="Google.Protobuf" Version="3.29.1" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.67.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -9,30 +9,17 @@ using MessageEnvelope = BuildingBlocks.Core.Event.MessageEnvelope;
namespace BuildingBlocks.Core;
public sealed class EventDispatcher : IEventDispatcher
public sealed class EventDispatcher(
IServiceScopeFactory serviceScopeFactory,
IEventMapper eventMapper,
ILogger<EventDispatcher> logger,
IPersistMessageProcessor persistMessageProcessor,
IHttpContextAccessor httpContextAccessor
)
: IEventDispatcher
{
private readonly IEventMapper _eventMapper;
private readonly ILogger<EventDispatcher> _logger;
private readonly IPersistMessageProcessor _persistMessageProcessor;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IServiceScopeFactory _serviceScopeFactory;
public EventDispatcher(IServiceScopeFactory serviceScopeFactory,
IEventMapper eventMapper,
ILogger<EventDispatcher> logger,
IPersistMessageProcessor persistMessageProcessor,
IHttpContextAccessor httpContextAccessor)
{
_serviceScopeFactory = serviceScopeFactory;
_eventMapper = eventMapper;
_logger = logger;
_persistMessageProcessor = persistMessageProcessor;
_httpContextAccessor = httpContextAccessor;
}
public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
where T : IEvent
{
if (events.Count > 0)
@ -45,7 +32,7 @@ public sealed class EventDispatcher : IEventDispatcher
{
foreach (var integrationEvent in integrationEvents)
{
await _persistMessageProcessor.PublishMessageAsync(
await persistMessageProcessor.PublishMessageAsync(
new MessageEnvelope(integrationEvent, SetHeaders()),
cancellationToken);
}
@ -74,7 +61,7 @@ public sealed class EventDispatcher : IEventDispatcher
foreach (var internalMessage in internalMessages)
{
await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
await persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
}
}
}
@ -89,20 +76,20 @@ public sealed class EventDispatcher : IEventDispatcher
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
IReadOnlyList<IDomainEvent> events)
{
_logger.LogTrace("Processing integration events start...");
logger.LogTrace("Processing integration events start...");
var wrappedIntegrationEvents = GetWrappedIntegrationEvents(events.ToList())?.ToList();
if (wrappedIntegrationEvents?.Count > 0)
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(wrappedIntegrationEvents);
var integrationEvents = new List<IIntegrationEvent>();
using var scope = _serviceScopeFactory.CreateScope();
using var scope = serviceScopeFactory.CreateScope();
foreach (var @event in events)
{
var eventType = @event.GetType();
_logger.LogTrace($"Handling domain event: {eventType.Name}");
logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = _eventMapper.MapToIntegrationEvent(@event);
var integrationEvent = eventMapper.MapToIntegrationEvent(@event);
if (integrationEvent is null)
continue;
@ -110,7 +97,7 @@ public sealed class EventDispatcher : IEventDispatcher
integrationEvents.Add(integrationEvent);
}
_logger.LogTrace("Processing integration events done...");
logger.LogTrace("Processing integration events done...");
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
}
@ -119,16 +106,16 @@ public sealed class EventDispatcher : IEventDispatcher
private Task<IReadOnlyList<IInternalCommand>> MapDomainEventToInternalCommandAsync(
IReadOnlyList<IDomainEvent> events)
{
_logger.LogTrace("Processing internal message start...");
logger.LogTrace("Processing internal message start...");
var internalCommands = new List<IInternalCommand>();
using var scope = _serviceScopeFactory.CreateScope();
using var scope = serviceScopeFactory.CreateScope();
foreach (var @event in events)
{
var eventType = @event.GetType();
_logger.LogTrace($"Handling domain event: {eventType.Name}");
logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = _eventMapper.MapToInternalCommand(@event);
var integrationEvent = eventMapper.MapToInternalCommand(@event);
if (integrationEvent is null)
continue;
@ -136,7 +123,7 @@ public sealed class EventDispatcher : IEventDispatcher
internalCommands.Add(integrationEvent);
}
_logger.LogTrace("Processing internal message done...");
logger.LogTrace("Processing internal message done...");
return Task.FromResult<IReadOnlyList<IInternalCommand>>(internalCommands);
}
@ -159,9 +146,9 @@ public sealed class EventDispatcher : IEventDispatcher
private IDictionary<string, object> SetHeaders()
{
var headers = new Dictionary<string, object>();
headers.Add("CorrelationId", _httpContextAccessor?.HttpContext?.GetCorrelationId());
headers.Add("UserId", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
headers.Add("UserName", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
headers.Add("CorrelationId", httpContextAccessor?.HttpContext?.GetCorrelationId());
headers.Add("UserId", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
headers.Add("UserName", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
return headers;
}

View File

@ -1,15 +1,14 @@
namespace BuildingBlocks.EFCore;
using System.Collections.Immutable;
using Core.Event;
using Core.Model;
using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model;
using BuildingBlocks.Web;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
using Web;
using Exception = System.Exception;
using IsolationLevel = System.Data.IsolationLevel;
namespace BuildingBlocks.EFCore;
public abstract class AppDbContextBase : DbContext, IDbContext
{
private readonly ICurrentUserProvider? _currentUserProvider;
@ -174,9 +173,9 @@ public abstract class AppDbContextBase : DbContext, IDbContext
}
}
}
catch (Exception ex)
catch (System.Exception ex)
{
throw new Exception("try for find IAggregate", ex);
throw new System.Exception("try for find IAggregate", ex);
}
}
}

View File

@ -1,5 +1,3 @@
using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;

View File

@ -2,49 +2,38 @@ using System.Text.Json;
using BuildingBlocks.Core;
using MediatR;
using Microsoft.Extensions.Logging;
using System.Transactions;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Polly;
namespace BuildingBlocks.EFCore;
using System.Transactions;
using PersistMessageProcessor;
using Polly;
public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull, IRequest<TResponse>
where TResponse : notnull
public class EfTxBehavior<TRequest, TResponse>(
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
IDbContext dbContextBase,
IPersistMessageDbContext persistMessageDbContext,
IEventDispatcher eventDispatcher
)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull, IRequest<TResponse>
where TResponse : notnull
{
private readonly ILogger<EfTxBehavior<TRequest, TResponse>> _logger;
private readonly IDbContext _dbContextBase;
private readonly IPersistMessageDbContext _persistMessageDbContext;
private readonly IEventDispatcher _eventDispatcher;
public EfTxBehavior(
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
IDbContext dbContextBase,
IPersistMessageDbContext persistMessageDbContext,
IEventDispatcher eventDispatcher)
{
_logger = logger;
_dbContextBase = dbContextBase;
_persistMessageDbContext = persistMessageDbContext;
_eventDispatcher = eventDispatcher;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
CancellationToken cancellationToken)
{
_logger.LogInformation(
logger.LogInformation(
"{Prefix} Handled command {MediatrRequest}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
_logger.LogDebug(
logger.LogDebug(
"{Prefix} Handled command {MediatrRequest} with content {RequestContent}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName,
JsonSerializer.Serialize(request));
_logger.LogInformation(
logger.LogInformation(
"{Prefix} Open the transaction for {MediatrRequest}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
@ -56,32 +45,32 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
var response = await next();
_logger.LogInformation(
logger.LogInformation(
"{Prefix} Executed the {MediatrRequest} request",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
while (true)
{
var domainEvents = _dbContextBase.GetDomainEvents();
var domainEvents = dbContextBase.GetDomainEvents();
if (domainEvents is null || !domainEvents.Any())
{
return response;
}
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
await eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
// Save data to database with some retry policy in distributed transaction
await _dbContextBase.RetryOnFailure(async () =>
await dbContextBase.RetryOnFailure(async () =>
{
await _dbContextBase.SaveChangesAsync(cancellationToken);
await dbContextBase.SaveChangesAsync(cancellationToken);
});
// Save data to database with some retry policy in distributed transaction
await _persistMessageDbContext.RetryOnFailure(async () =>
await persistMessageDbContext.RetryOnFailure(async () =>
{
await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
await persistMessageDbContext.SaveChangesAsync(cancellationToken);
});
scope.Complete();

View File

@ -1,19 +1,20 @@
using System.Linq.Expressions;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.Model;
using BuildingBlocks.Web;
using Humanizer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BuildingBlocks.EFCore;
using Ardalis.GuardClauses;
using Humanizer;
using Microsoft.EntityFrameworkCore.Metadata;
public static class Extensions
{
public static IServiceCollection AddCustomDbContext<TContext>(this IServiceCollection services)
@ -36,32 +37,30 @@ public static class Extensions
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
// Suppress warnings for pending model changes
options.ConfigureWarnings(
w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
});
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
services.AddScoped<ISeedManager, SeedManager>();
services.AddScoped<IDbContext>(sp => sp.GetRequiredService<TContext>());
return services;
}
public static IApplicationBuilder UseMigration<TContext>(
this IApplicationBuilder app,
IWebHostEnvironment env
)
public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app)
where TContext : DbContext, IDbContext
{
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
MigrateAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
if (!env.IsEnvironment("test"))
{
SeedDataAsync(app.ApplicationServices).GetAwaiter().GetResult();
}
SeedAsync(app.ApplicationServices).GetAwaiter().GetResult();
return app;
}
// ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
{
@ -114,23 +113,30 @@ public static class Extensions
}
}
private static async Task MigrateDatabaseAsync<TContext>(IServiceProvider serviceProvider)
private static async Task MigrateAsync<TContext>(IServiceProvider serviceProvider)
where TContext : DbContext, IDbContext
{
using var scope = serviceProvider.CreateScope();
await using var scope = serviceProvider.CreateAsyncScope();
var context = scope.ServiceProvider.GetRequiredService<TContext>();
await context.Database.MigrateAsync();
}
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TContext>>();
private static async Task SeedDataAsync(IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
foreach (var seeder in seeders)
if (pendingMigrations.Any())
{
await seeder.SeedAllAsync();
logger.LogInformation("Applying {Count} pending migrations...", pendingMigrations.Count());
await context.Database.MigrateAsync();
logger.LogInformation("Migrations applied successfully.");
}
}
private static async Task SeedAsync(IServiceProvider serviceProvider)
{
await using var scope = serviceProvider.CreateAsyncScope();
var seedersManager = scope.ServiceProvider.GetRequiredService<ISeedManager>();
await seedersManager.ExecuteAsync();
}
}

View File

@ -4,4 +4,8 @@ namespace BuildingBlocks.EFCore
{
Task SeedAllAsync();
}
public interface ITestDataSeeder : IDataSeeder
{
}
}

View File

@ -0,0 +1,6 @@
namespace BuildingBlocks.EFCore;
public interface ISeedManager
{
Task ExecuteAsync();
}

View File

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BuildingBlocks.EFCore;
public class SeedManager(
IServiceProvider serviceProvider
)
: ISeedManager
{
public async Task ExecuteAsync()
{
await using var scope = serviceProvider.CreateAsyncScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<SeedManager>>();
var env = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var dataSeeders = scope.ServiceProvider.GetServices<IDataSeeder>();
if (env.IsEnvironment("test"))
{
foreach (var seeder in dataSeeders.Where(x => x is ITestDataSeeder))
{
logger.LogInformation("Test Seed {SeederName} is started.", seeder.GetType().Name);
await seeder.SeedAllAsync();
logger.LogInformation("Test Seed {SeederName} is completed.", seeder.GetType().Name);
}
}
else
{
foreach (var seeder in dataSeeders.Where(x => x is not ITestDataSeeder))
{
logger.LogInformation("Seed {SeederName} is started.", seeder.GetType().Name);
await seeder.SeedAllAsync();
logger.LogInformation("Seed {SeederName} is completed.", seeder.GetType().Name);
}
}
}
}

View File

@ -1,7 +1,5 @@
using System.Reflection;
using BuildingBlocks.Core.Event;
using BuildingBlocks.Web;
using Humanizer;
using MassTransit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

View File

@ -1,7 +1,9 @@
using System.Globalization;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
namespace BuildingBlocks.Mongo;
@ -14,6 +16,12 @@ public class MongoDbContext : IMongoDbContext
public IMongoDatabase Database { get; }
public IMongoClient MongoClient { get; }
protected readonly IList<Func<Task>> _commands;
private static readonly bool _isSerializerRegisterd;
static MongoDbContext()
{
BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
}
public MongoDbContext(IOptions<MongoOptions> options)
{

View File

@ -1,6 +1,7 @@
using System.Linq.Expressions;
using BuildingBlocks.Core.Model;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace BuildingBlocks.Mongo;

View File

@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Scalar.AspNetCore;
namespace BuildingBlocks.OpenApi
{
public static class Extensions
{
// ref: https://github.com/dotnet/eShop/blob/main/src/eShop.ServiceDefaults/OpenApi.Extensions.cs
public static IServiceCollection AddAspnetOpenApi(this IServiceCollection services)
{
string[] versions = ["v1"];
foreach (var description in versions)
{
services.AddOpenApi(
description,
options =>
{
options.AddDocumentTransformer<SecuritySchemeDocumentTransformer>();
});
}
return services;
}
public static IApplicationBuilder UseAspnetOpenApi(this WebApplication app)
{
app.MapOpenApi();
app.UseSwaggerUI(
options =>
{
var descriptions = app.DescribeApiVersions();
// build a swagger endpoint for each discovered API version
foreach (var description in descriptions)
{
var openApiUrl = $"/openapi/{description.GroupName}.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(openApiUrl, name);
}
});
// Add scalar ui
app.MapScalarApiReference(
redocOptions =>
{
redocOptions.WithOpenApiRoutePattern("/openapi/{documentName}.json");
});
return app;
}
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
public class SecuritySchemeDocumentTransformer : IOpenApiDocumentTransformer
{
public Task TransformAsync(
OpenApiDocument document,
OpenApiDocumentTransformerContext context,
CancellationToken cancellationToken
)
{
document.Components ??= new();
// Bearer token scheme
document.Components.SecuritySchemes.Add(
"Bearer",
new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description =
"Enter 'Bearer' [space] and your token in the text input below.\n\nExample: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'",
}
);
// API Key scheme
document.Components.SecuritySchemes.Add(
"ApiKey",
new OpenApiSecurityScheme
{
Name = "X-API-KEY",
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Description =
"Enter your API key in the text input below.\n\nExample: '12345-abcdef'",
}
);
return Task.CompletedTask;
}
}

View File

@ -1,33 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace BuildingBlocks.PersistMessageProcessor.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{
public void Configure(EntityTypeBuilder<PersistMessage> builder)
{
builder.ToTable(nameof(PersistMessage));
builder.HasKey(x => x.Id);
builder.Property(r => r.Id)
.IsRequired().ValueGeneratedNever();
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
builder.Property(r => r.Version).IsConcurrencyToken();
builder.Property(x => x.DeliveryType)
.HasDefaultValue(MessageDeliveryType.Outbox)
.HasConversion(
x => x.ToString(),
x => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), x));
builder.Property(x => x.MessageStatus)
.HasDefaultValue(MessageStatus.InProgress)
.HasConversion(
v => v.ToString(),
v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v));
}
}

View File

@ -1,51 +1,57 @@
using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.PersistMessageProcessor;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
public static class Extensions
{
public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services,
IWebHostEnvironment env)
public static IServiceCollection AddPersistMessageProcessor(
this IServiceCollection services,
IWebHostEnvironment env
)
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
services.AddValidateOptions<PersistMessageOptions>();
services.AddDbContext<PersistMessageDbContext>((sp, options) =>
{
var persistMessageOptions = sp.GetRequiredService<PersistMessageOptions>();
services.AddDbContext<PersistMessageDbContext>(
(sp, options) =>
{
var persistMessageOptions = sp.GetRequiredService<PersistMessageOptions>();
options.UseNpgsql(persistMessageOptions.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(PersistMessageDbContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
});
options.UseNpgsql(
persistMessageOptions.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(
typeof(PersistMessageDbContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
services.AddScoped<IPersistMessageDbContext>(provider =>
{
var persistMessageDbContext = provider.GetRequiredService<PersistMessageDbContext>();
// Todo: follow up the issues of .net 9 to use better approach taht will provide by .net!
options.ConfigureWarnings(
w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
});
persistMessageDbContext.Database.EnsureCreated();
persistMessageDbContext.CreatePersistMessageTable();
services.AddScoped<IPersistMessageDbContext>(
provider =>
{
var persistMessageDbContext =
provider.GetRequiredService<PersistMessageDbContext>();
return persistMessageDbContext;
});
persistMessageDbContext.Database.EnsureCreated();
persistMessageDbContext.CreatePersistMessageTableIfNotExists();
return persistMessageDbContext;
});
services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>();
if (env.EnvironmentName != "test")
{
services.AddHostedService<PersistMessageBackgroundService>();
}
services.AddHostedService<PersistMessageBackgroundService>();
return services;
}

View File

@ -4,7 +4,7 @@ namespace BuildingBlocks.PersistMessageProcessor;
public interface IPersistMessageDbContext
{
DbSet<PersistMessage> PersistMessages { get; }
DbSet<PersistMessage> PersistMessage { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default);
}

View File

@ -3,7 +3,8 @@ namespace BuildingBlocks.PersistMessageProcessor;
[Flags]
public enum MessageDeliveryType
{
Unknown = 0,
Outbox = 1,
Inbox = 2,
Internal = 4
Internal = 3
}

View File

@ -2,6 +2,7 @@ namespace BuildingBlocks.PersistMessageProcessor;
public enum MessageStatus
{
Unknown = 0,
InProgress = 1,
Processed = 2
}

View File

@ -1,6 +1,6 @@
using BuildingBlocks.Core.Model;
namespace BuildingBlocks.PersistMessageProcessor;
using Core.Model;
public class PersistMessage : IVersion
{

View File

@ -5,27 +5,20 @@ using Microsoft.Extensions.Options;
namespace BuildingBlocks.PersistMessageProcessor;
public class PersistMessageBackgroundService : BackgroundService
public class PersistMessageBackgroundService(
ILogger<PersistMessageBackgroundService> logger,
IServiceProvider serviceProvider,
IOptions<PersistMessageOptions> options
)
: BackgroundService
{
private readonly ILogger<PersistMessageBackgroundService> _logger;
private readonly IServiceProvider _serviceProvider;
private PersistMessageOptions _options;
private PersistMessageOptions _options = options.Value;
private Task? _executingTask;
public PersistMessageBackgroundService(
ILogger<PersistMessageBackgroundService> logger,
IServiceProvider serviceProvider,
IOptions<PersistMessageOptions> options)
{
_logger = logger;
_serviceProvider = serviceProvider;
_options = options.Value;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PersistMessage Background Service Start");
logger.LogInformation("PersistMessage Background Service Start");
_executingTask = ProcessAsync(stoppingToken);
@ -34,7 +27,7 @@ public class PersistMessageBackgroundService : BackgroundService
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("PersistMessage Background Service Stop");
logger.LogInformation("PersistMessage Background Service Stop");
return base.StopAsync(cancellationToken);
}
@ -43,15 +36,15 @@ public class PersistMessageBackgroundService : BackgroundService
{
while (!stoppingToken.IsCancellationRequested)
{
await using (var scope = _serviceProvider.CreateAsyncScope())
await using (var scope = serviceProvider.CreateAsyncScope())
{
var service = scope.ServiceProvider.GetRequiredService<IPersistMessageProcessor>();
await service.ProcessAllAsync(stoppingToken);
}
var delay = _options.Interval is { }
? TimeSpan.FromSeconds((int)_options.Interval)
: TimeSpan.FromSeconds(30);
? TimeSpan.FromSeconds((int)_options.Interval)
: TimeSpan.FromSeconds(30);
await Task.Delay(delay, stoppingToken);
}

View File

@ -1,14 +1,11 @@
using BuildingBlocks.Core.Model;
using BuildingBlocks.EFCore;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor.Data;
using Configurations;
using Core.Model;
using Microsoft.Extensions.Logging;
using Exception = System.Exception;
using IsolationLevel = System.Data.IsolationLevel;
namespace BuildingBlocks.PersistMessageProcessor;
public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
{
private readonly ILogger<PersistMessageDbContext>? _logger;
@ -20,11 +17,10 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
_logger = logger;
}
public DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
public DbSet<PersistMessage> PersistMessage => Set<PersistMessage>();
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new PersistMessageConfiguration());
base.OnModelCreating(builder);
builder.ToSnakeCaseTables();
}
@ -79,13 +75,8 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
}
}
public void CreatePersistMessageTable()
public void CreatePersistMessageTableIfNotExists()
{
if (Database.GetPendingMigrations().Any())
{
throw new InvalidOperationException("Cannot create table if there are pending migrations.");
}
string createTableSql = @"
create table if not exists persist_message (
id uuid not null,
@ -120,9 +111,9 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
}
}
}
catch (Exception ex)
catch (System.Exception ex)
{
throw new Exception("try for find IVersion", ex);
throw new System.Exception("try for find IVersion", ex);
}
}
}

View File

@ -54,13 +54,13 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate,
CancellationToken cancellationToken = default)
{
return (await _persistMessageDbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken))
return (await _persistMessageDbContext.PersistMessage.Where(predicate).ToListAsync(cancellationToken))
.AsReadOnly();
}
public Task<PersistMessage> ExistMessageAsync(Guid messageId, CancellationToken cancellationToken = default)
{
return _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(x =>
return _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync(x =>
x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.Processed,
@ -73,7 +73,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
CancellationToken cancellationToken = default)
{
var message =
await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(
await _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync(
x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken);
if (message is null)
@ -109,7 +109,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessAllAsync(CancellationToken cancellationToken = default)
{
var messages = await _persistMessageDbContext.PersistMessages
var messages = await _persistMessageDbContext.PersistMessage
.Where(x => x.MessageStatus != MessageStatus.Processed)
.ToListAsync(cancellationToken);
@ -121,7 +121,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessInboxAsync(Guid messageId, CancellationToken cancellationToken = default)
{
var message = await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(
var message = await _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync(
x => x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.InProgress,
@ -193,7 +193,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
else
id = NewId.NextGuid();
await _persistMessageDbContext.PersistMessages.AddAsync(
await _persistMessageDbContext.PersistMessage.AddAsync(
new PersistMessage(
id,
messageEnvelope.Message.GetType().ToString(),
@ -215,7 +215,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
{
message.ChangeState(MessageStatus.Processed);
_persistMessageDbContext.PersistMessages.Update(message);
_persistMessageDbContext.PersistMessage.Update(message);
await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
}

View File

@ -1,90 +0,0 @@
using System.Text;
using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace BuildingBlocks.Swagger;
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider provider;
private readonly SwaggerOptions? _options;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
/// </summary>
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptions<SwaggerOptions> options)
{
this.provider = provider;
_options = options.Value;
}
/// <inheritdoc />
public void Configure(SwaggerGenOptions options)
{
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
}
private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var text = new StringBuilder("An example application with OpenAPI, Swashbuckle, and API versioning.");
var info = new OpenApiInfo
{
Version = description.ApiVersion.ToString(),
Title = _options?.Title ?? "APIs",
Description = "An application with Swagger, Swashbuckle, and API versioning.",
Contact = new OpenApiContact { Name = "", Email = "" },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
};
if (description.IsDeprecated)
{
text.Append("This API version has been deprecated.");
}
if (description.SunsetPolicy is SunsetPolicy policy)
{
if (policy.Date is DateTimeOffset when)
{
text.Append(" The API will be sunset on ")
.Append(when.Date.ToShortDateString())
.Append('.');
}
if (policy.HasLinks)
{
text.AppendLine();
for (var i = 0; i < policy.Links.Count; i++)
{
var link = policy.Links[i];
if (link.Type == "text/html")
{
text.AppendLine();
if (link.Title.HasValue)
{
text.Append(link.Title.Value).Append(": ");
}
text.Append(link.LinkTarget.OriginalString);
}
}
}
}
info.Description = text.ToString();
return info;
}
}

View File

@ -1,107 +0,0 @@
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
namespace BuildingBlocks.Swagger;
public static class ServiceCollectionExtensions
{
public const string HeaderName = "X-Api-Key";
public const string HeaderVersion = "api-version";
// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md
// https://github.com/dotnet/aspnet-api-versioning/tree/88323136a97a59fcee24517a514c1a445530c7e2/examples/AspNetCore/WebApi/MinimalOpenApiExample
public static IServiceCollection AddCustomSwagger(this IServiceCollection services,
IConfiguration configuration,
params Assembly[] assemblies)
{
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddOptions<SwaggerOptions>().Bind(configuration.GetSection(nameof(SwaggerOptions)))
.ValidateDataAnnotations();
services.AddSwaggerGen(
options =>
{
options.OperationFilter<SwaggerDefaultValues>();
foreach (var assembly in assemblies)
{
var xmlFile = XmlCommentsFilePath(assembly);
if (File.Exists(xmlFile))
options.IncludeXmlComments(xmlFile);
}
options.AddEnumsWithValuesFixFilters();
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
BearerFormat = "JWT",
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
////https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/467
// options.OperationFilter<TagByApiExplorerSettingsOperationFilter>();
// options.OperationFilter<TagBySwaggerOperationFilter>();
// Enables Swagger annotations (SwaggerOperationAttribute, SwaggerParameterAttribute etc.)
// options.EnableAnnotations();
});
// services.Configure<SwaggerGeneratorOptions>(o => o.InferSecuritySchemes = true);
static string XmlCommentsFilePath(Assembly assembly)
{
var basePath = Path.GetDirectoryName(assembly.Location);
var fileName = assembly.GetName().Name + ".xml";
return Path.Combine(basePath, fileName);
}
return services;
}
public static IApplicationBuilder UseCustomSwagger(this WebApplication app)
{
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
var descriptions = app.DescribeApiVersions();
// build a swagger endpoint for each discovered API version
foreach (var description in descriptions)
{
var url = $"/swagger/{description.GroupName}/swagger.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
});
return app;
}
}

View File

@ -1,70 +0,0 @@
using System.Linq;
using Humanizer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace BuildingBlocks.Swagger
{
public class SwaggerDefaultValues : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var apiDescription = context.ApiDescription;
operation.Deprecated |= apiDescription.IsDeprecated();
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077
foreach (var responseType in context.ApiDescription.SupportedResponseTypes)
{
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387
var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString();
var response = operation.Responses[responseKey];
foreach (var contentType in response.Content.Keys)
{
if (responseType.ApiResponseFormats.All(x => x.MediaType != contentType))
{
response.Content.Remove(contentType);
}
}
}
if (operation.Parameters == null)
{
return;
}
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
foreach (var parameter in operation.Parameters)
{
var description = apiDescription.ParameterDescriptions.FirstOrDefault(p => p.Name == parameter.Name);
if (description is null)
{
return;
}
if (parameter.Description == null)
{
parameter.Description = description.ModelMetadata?.Description;
}
parameter.Name = description.Name.Camelize();
if (parameter.Schema.Default == null && description.DefaultValue != null)
{
// REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330
var json = JsonConvert.SerializeObject(description.DefaultValue, description.ModelMetadata
.ModelType, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json);
}
parameter.Required |= description.IsRequired;
}
}
}
}

View File

@ -1,9 +0,0 @@
namespace BuildingBlocks.Swagger
{
public class SwaggerOptions
{
public string Title { get; set; }
public string Name { get; set; }
public string Version { get; set; }
}
}

View File

@ -1,3 +1,4 @@
using System.Globalization;
using System.Net;
using System.Security.Claims;
using Ardalis.GuardClauses;
@ -9,7 +10,6 @@ using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Web;
using EasyNetQ.Management.Client;
using Grpc.Net.Client;
using MassTransit;
using MassTransit.Testing;
using MediatR;
using Microsoft.AspNetCore.Hosting;
@ -18,10 +18,17 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using MongoDB.Driver;
using Npgsql;
using NSubstitute;
using Respawn;
using Serilog;
using Testcontainers.EventStoreDb;
using Testcontainers.MongoDb;
using Testcontainers.PostgreSql;
using Testcontainers.RabbitMq;
using WebMotions.Fake.Authentication.JwtBearer;
using Xunit;
using Xunit.Abstractions;
@ -29,12 +36,6 @@ using ILogger = Serilog.ILogger;
namespace BuildingBlocks.TestBase;
using System.Globalization;
using Npgsql;
using Testcontainers.EventStoreDb;
using Testcontainers.MongoDb;
using Testcontainers.PostgreSql;
using Testcontainers.RabbitMq;
public class TestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class
@ -50,8 +51,7 @@ where TEntryPoint : class
public EventStoreDbContainer EventStoreDbTestContainer;
public CancellationTokenSource CancellationTokenSource;
public PersistMessageBackgroundService PersistMessageBackgroundService =>
ServiceProvider.GetRequiredService<PersistMessageBackgroundService>();
public PersistMessageBackgroundService PersistMessageBackgroundService => ServiceProvider.GetRequiredService<PersistMessageBackgroundService>();
public HttpClient HttpClient
{
@ -95,9 +95,17 @@ where TEntryPoint : class
{
TestRegistrationServices?.Invoke(services);
services.ReplaceSingleton(AddHttpContextAccessorMock);
services.RemoveAll<IHostedService>();
services.AddSingleton<PersistMessageBackgroundService>();
// Register all ITestDataSeeder implementations dynamically
services.Scan(scan => scan
.FromApplicationDependencies() // Scan the current app and its dependencies
.AddClasses(classes => classes.AssignableTo<ITestDataSeeder>()) // Find classes that implement ITestDataSeeder
.AsImplementedInterfaces()
.WithScopedLifetime());
// add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor
// https://github.com/webmotions/fake-authentication-jwtbearer
// https://github.com/webmotions/fake-authentication-jwtbearer/issues/14
@ -200,14 +208,8 @@ where TEntryPoint : class
var result = await WaitUntilConditionMet(
async () =>
{
var published =
await TestHarness.Published.Any<TMessage>(cancellationToken);
var faulty =
await TestHarness.Published.Any<Fault<TMessage>>(
cancellationToken);
return published && faulty == false;
var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
return published;
});
return result;
@ -224,10 +226,7 @@ where TEntryPoint : class
var consumed =
await TestHarness.Consumed.Any<TMessage>(cancellationToken);
var faulty =
await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken);
return consumed && faulty == false;
return consumed;
});
return result;
@ -611,8 +610,6 @@ where TEntryPoint : class
_reSpawnerDefaultDb = await Respawner.CreateAsync(
DefaultDbConnection,
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
await SeedDataAsync();
}
}
@ -680,18 +677,6 @@ where TEntryPoint : class
protected virtual void RegisterTestsServices(IServiceCollection services)
{
}
private async Task SeedDataAsync()
{
using var scope = Fixture.ServiceProvider.CreateScope();
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
foreach (var seeder in seeders)
{
await seeder.SeedAllAsync();
}
}
}
public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEntryPoint>

View File

@ -5,7 +5,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.Tools" Version="2.66.0">
<PackageReference Include="Grpc.Tools" Version="2.68.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -8,9 +8,10 @@ using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.OpenApi;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.ProblemDetails;
using BuildingBlocks.Web;
using Figgle;
using FluentValidation;
@ -19,13 +20,10 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Prometheus;
using Serilog;
namespace Booking.Extensions.Infrastructure;
using BuildingBlocks.ProblemDetails;
public static class InfrastructureExtensions
{
public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder)
@ -67,7 +65,7 @@ public static class InfrastructureExtensions
builder.AddCustomSerilog(env);
builder.Services.AddJwt();
builder.Services.AddHttpContextAccessor();
builder.Services.AddCustomSwagger(configuration, typeof(BookingRoot).Assembly);
builder.Services.AddAspnetOpenApi();
builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR();
builder.Services.AddValidatorsFromAssembly(typeof(BookingRoot).Assembly);
@ -106,7 +104,7 @@ public static class InfrastructureExtensions
if (env.IsDevelopment())
{
app.UseCustomSwagger();
app.UseAspnetOpenApi();
}
return app;

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -17,7 +17,7 @@ namespace Flight.Data.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.2")
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);

View File

@ -1,8 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models;
using Flight.Airports.Models;
using Flight.Flights.Models;
using Flight.Seats.Models;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
@ -11,23 +10,12 @@ using MongoDB.Driver.Linq;
namespace Flight.Data.Seed;
using Flights.Models;
public class FlightDataSeeder : IDataSeeder
public class FlightDataSeeder(
FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper
) : IDataSeeder
{
private readonly FlightDbContext _flightDbContext;
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public FlightDataSeeder(FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightDbContext = flightDbContext;
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task SeedAllAsync()
{
await SeedAirportAsync();
@ -38,28 +26,28 @@ public class FlightDataSeeder : IDataSeeder
private async Task SeedAirportAsync()
{
if (!await _flightDbContext.Airports.AnyAsync())
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports))
{
await _flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await _flightDbContext.SaveChangesAsync();
await flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await flightDbContext.SaveChangesAsync();
if (!await _flightReadDbContext.Airport.AsQueryable().AnyAsync())
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable()))
{
await _flightReadDbContext.Airport.InsertManyAsync(_mapper.Map<List<AirportReadModel>>(InitialData.Airports));
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(InitialData.Airports));
}
}
}
private async Task SeedAircraftAsync()
{
if (!await _flightDbContext.Aircraft.AnyAsync())
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft))
{
await _flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await _flightDbContext.SaveChangesAsync();
await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await flightDbContext.SaveChangesAsync();
if (!await _flightReadDbContext.Aircraft.AsQueryable().AnyAsync())
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable()))
{
await _flightReadDbContext.Aircraft.InsertManyAsync(_mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
}
}
}
@ -67,28 +55,28 @@ public class FlightDataSeeder : IDataSeeder
private async Task SeedSeatAsync()
{
if (!await _flightDbContext.Seats.AnyAsync())
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats))
{
await _flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await _flightDbContext.SaveChangesAsync();
await flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await flightDbContext.SaveChangesAsync();
if (!await _flightReadDbContext.Seat.AsQueryable().AnyAsync())
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable()))
{
await _flightReadDbContext.Seat.InsertManyAsync(_mapper.Map<List<SeatReadModel>>(InitialData.Seats));
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(InitialData.Seats));
}
}
}
private async Task SeedFlightAsync()
{
if (!await _flightDbContext.Flights.AnyAsync())
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights))
{
await _flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await _flightDbContext.SaveChangesAsync();
await flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await flightDbContext.SaveChangesAsync();
if (!await _flightReadDbContext.Flight.AsQueryable().AnyAsync())
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable()))
{
await _flightReadDbContext.Flight.InsertManyAsync(_mapper.Map<List<FlightReadModel>>(InitialData.Flights));
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
}
}
}

View File

@ -1,4 +1,3 @@
using System;
using System.Threading.RateLimiting;
using BuildingBlocks.Core;
using BuildingBlocks.EFCore;
@ -9,9 +8,10 @@ using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.OpenApi;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.ProblemDetails;
using BuildingBlocks.Web;
using Figgle;
using Flight.Data;
@ -27,7 +27,6 @@ using Serilog;
namespace Flight.Extensions.Infrastructure;
using BuildingBlocks.ProblemDetails;
public static class InfrastructureExtensions
{
@ -38,6 +37,7 @@ public static class InfrastructureExtensions
builder.Services.AddScoped<ICurrentUserProvider, CurrentUserProvider>();
builder.Services.AddScoped<IEventMapper, EventMapper>();
builder.Services.AddScoped<IEventDispatcher, EventDispatcher>();
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
@ -72,7 +72,7 @@ public static class InfrastructureExtensions
builder.Services.AddEndpointsApiExplorer();
builder.AddCustomSerilog(env);
builder.Services.AddJwt();
builder.Services.AddCustomSwagger(configuration, typeof(FlightRoot).Assembly);
builder.Services.AddAspnetOpenApi();
builder.Services.AddCustomVersioning();
builder.Services.AddValidatorsFromAssembly(typeof(FlightRoot).Assembly);
builder.Services.AddCustomMapster(typeof(FlightRoot).Assembly);
@ -105,7 +105,7 @@ public static class InfrastructureExtensions
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
});
app.UseCorrelationId();
app.UseMigration<FlightDbContext>(env);
app.UseMigration<FlightDbContext>();
app.UseCustomHealthCheck();
app.MapGrpcService<FlightGrpcServices>();
app.UseRateLimiter();
@ -113,7 +113,7 @@ public static class InfrastructureExtensions
if (env.IsDevelopment())
{
app.UseCustomSwagger();
app.UseAspnetOpenApi();
}
return app;

View File

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.65.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
<PackageReference Include="Grpc.Tools" Version="2.66.0">
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
<PackageReference Include="Grpc.Tools" Version="2.68.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,38 +1,44 @@
namespace Flight.Flights.Features.DeletingFlight.V1;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using BuildingBlocks.Core.Event;
using BuildingBlocks.Web;
using Data;
using Duende.IdentityServer.EntityFramework.Entities;
using Exceptions;
using Flight.Data;
using Flight.Flights.Exceptions;
using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver.Linq;
using ValueObjects;
namespace Flight.Flights.Features.DeletingFlight.V1;
public record DeleteFlight(Guid Id) : ICommand<DeleteFlightResult>, IInternalCommand;
public record DeleteFlightResult(Guid Id);
public record FlightDeletedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
public record FlightDeletedDomainEvent(
Guid Id,
string FlightNumber,
Guid AircraftId,
DateTime DepartureDate,
Guid DepartureAirportId,
DateTime ArriveDate,
Guid ArriveAirportId,
decimal DurationMinutes,
DateTime FlightDate,
Enums.FlightStatus Status,
decimal Price,
bool IsDeleted
) : IDomainEvent;
public class DeleteFlightEndpoint : IMinimalEndpoint
{
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
builder.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}",
builder.MapDelete(
$"{EndpointConfig.BaseApiPath}/flight/{{id}}",
async (Guid id, IMediator mediator, CancellationToken cancellationToken) =>
{
await mediator.Send(new DeleteFlight(id), cancellationToken);
@ -70,7 +76,10 @@ internal class DeleteFlightHandler : ICommandHandler<DeleteFlight, DeleteFlightR
_flightDbContext = flightDbContext;
}
public async Task<DeleteFlightResult> Handle(DeleteFlight request, CancellationToken cancellationToken)
public async Task<DeleteFlightResult> Handle(
DeleteFlight request,
CancellationToken cancellationToken
)
{
Guard.Against.Null(request, nameof(request));
@ -81,9 +90,18 @@ internal class DeleteFlightHandler : ICommandHandler<DeleteFlight, DeleteFlightR
throw new FlightNotFountException();
}
flight.Delete(flight.Id, flight.FlightNumber, flight.AircraftId, flight.DepartureAirportId,
flight.DepartureDate, flight.ArriveDate, flight.ArriveAirportId, flight.DurationMinutes,
flight.FlightDate, flight.Status, flight.Price);
flight.Delete(
flight.Id,
flight.FlightNumber,
flight.AircraftId,
flight.DepartureAirportId,
flight.DepartureDate,
flight.ArriveDate,
flight.ArriveAirportId,
flight.DurationMinutes,
flight.FlightDate,
flight.Status,
flight.Price);
var deleteFlight = _flightDbContext.Flights.Update(flight).Entity;

View File

@ -1,3 +1,5 @@
using MongoDB.Driver.Linq;
namespace Flight.Flights.Features.GettingAvailableFlights.V1;
using System;

View File

@ -76,9 +76,9 @@ internal class GetFlightByIdHandler : IQueryHandler<GetFlightById, GetFlightById
{
Guard.Against.Null(request, nameof(request));
var flight =
await _flightReadDbContext.Flight.AsQueryable().SingleOrDefaultAsync(x => x.FlightId == request.Id &&
!x.IsDeleted, cancellationToken);
var flight = await _flightReadDbContext.Flight.AsQueryable().SingleOrDefaultAsync(
x => x.FlightId == request.Id &&
!x.IsDeleted, cancellationToken);
if (flight is null)
{

View File

@ -1,3 +1,5 @@
using MongoDB.Driver.Linq;
namespace Flight.Seats.Features.GettingAvailableSeats.V1;
using System;

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -0,0 +1,85 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models;
using Flight.Airports.Models;
using Flight.Data;
using Flight.Data.Seed;
using Flight.Flights.Models;
using Flight.Seats.Models;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace EndToEnd.Test;
public class FlightTestDataSeeder(
FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper
) : ITestDataSeeder
{
public async Task SeedAllAsync()
{
await SeedAirportAsync();
await SeedAircraftAsync();
await SeedFlightAsync();
await SeedSeatAsync();
}
private async Task SeedAirportAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports))
{
await flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable()))
{
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(InitialData.Airports));
}
}
}
private async Task SeedAircraftAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft))
{
await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable()))
{
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
}
}
}
private async Task SeedSeatAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats))
{
await flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable()))
{
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(InitialData.Seats));
}
}
}
private async Task SeedFlightAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights))
{
await flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable()))
{
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
}
}
}
}

View File

@ -1,4 +1,3 @@
using System.Threading.Tasks;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Api;

View File

@ -0,0 +1,85 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models;
using Flight.Airports.Models;
using Flight.Data;
using Flight.Data.Seed;
using Flight.Flights.Models;
using Flight.Seats.Models;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Integration.Test;
public class FlightTestDataSeeder(
FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper
) : ITestDataSeeder
{
public async Task SeedAllAsync()
{
await SeedAirportAsync();
await SeedAircraftAsync();
await SeedFlightAsync();
await SeedSeatAsync();
}
private async Task SeedAirportAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports))
{
await flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable()))
{
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(InitialData.Airports));
}
}
}
private async Task SeedAircraftAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft))
{
await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable()))
{
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
}
}
}
private async Task SeedSeatAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats))
{
await flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable()))
{
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(InitialData.Seats));
}
}
}
private async Task SeedFlightAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights))
{
await flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable()))
{
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
}
}
}
}

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -1,4 +1,3 @@
using System;
using System.Threading.RateLimiting;
using BuildingBlocks.Core;
using BuildingBlocks.EFCore;
@ -6,27 +5,26 @@ using BuildingBlocks.HealthCheck;
using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.OpenApi;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.ProblemDetails;
using BuildingBlocks.Web;
using Figgle;
using FluentValidation;
using Identity.Configurations;
using Identity.Data;
using Identity.Data.Seed;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Prometheus;
using Serilog;
namespace Identity.Extensions.Infrastructure;
using BuildingBlocks.ProblemDetails;
using Configurations;
using Microsoft.AspNetCore.HttpOverrides;
public static class InfrastructureExtensions
{
@ -67,7 +65,7 @@ public static class InfrastructureExtensions
builder.Services.AddCustomDbContext<IdentityContext>();
builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>();
builder.AddCustomSerilog(env);
builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly);
builder.Services.AddAspnetOpenApi();
builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR();
builder.Services.AddValidatorsFromAssembly(typeof(IdentityRoot).Assembly);
@ -104,8 +102,8 @@ public static class InfrastructureExtensions
{
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
});
app.UseMigration<IdentityContext>(env);
app.UseCorrelationId();
app.UseMigration<IdentityContext>();
app.UseCustomHealthCheck();
app.UseIdentityServer();
@ -113,7 +111,7 @@ public static class InfrastructureExtensions
if (env.IsDevelopment())
{
app.UseCustomSwagger();
app.UseAspnetOpenApi();
}
return app;

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -0,0 +1,63 @@
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.Core;
using BuildingBlocks.EFCore;
using Identity.Data.Seed;
using Identity.Identity.Constants;
using Identity.Identity.Models;
using Microsoft.AspNetCore.Identity;
namespace Integration.Test;
public class IdentityDataSeeder(
UserManager<User> userManager,
RoleManager<Role> roleManager,
IEventDispatcher eventDispatcher
)
: IDataSeeder
{
public async Task SeedAllAsync()
{
await SeedRoles();
await SeedUsers();
}
private async Task SeedRoles()
{
if (await roleManager.RoleExistsAsync(Constants.Role.Admin) == false)
{
await roleManager.CreateAsync(new Role { Name = Constants.Role.Admin });
}
if (await roleManager.RoleExistsAsync(Constants.Role.User) == false)
{
await roleManager.CreateAsync(new Role { Name = Constants.Role.User });
}
}
private async Task SeedUsers()
{
if (await userManager.FindByNameAsync("samh") == null)
{
var result = await userManager.CreateAsync(InitialData.Users.First(), "Admin@123456");
if (result.Succeeded)
{
await userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin);
await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.First().Id, InitialData.Users.First().FirstName + " " + InitialData.Users.First().LastName, InitialData.Users.First().PassPortNumber));
}
}
if (await userManager.FindByNameAsync("meysamh2") == null)
{
var result = await userManager.CreateAsync(InitialData.Users.Last(), "User@123456");
if (result.Succeeded)
{
await userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User);
await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.Last().Id, InitialData.Users.Last().FirstName + " " + InitialData.Users.Last().LastName, InitialData.Users.Last().PassPortNumber));
}
}
}
}

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -8,9 +8,10 @@ using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.OpenApi;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.ProblemDetails;
using BuildingBlocks.Web;
using Figgle;
using FluentValidation;
@ -25,7 +26,6 @@ using Serilog;
namespace Passenger.Extensions.Infrastructure;
using BuildingBlocks.ProblemDetails;
public static class InfrastructureExtensions
{
@ -67,7 +67,7 @@ public static class InfrastructureExtensions
builder.AddCustomSerilog(env);
builder.Services.AddJwt();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddCustomSwagger(configuration, typeof(PassengerRoot).Assembly);
builder.Services.AddAspnetOpenApi();
builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR();
builder.Services.AddValidatorsFromAssembly(typeof(PassengerRoot).Assembly);
@ -98,15 +98,15 @@ public static class InfrastructureExtensions
{
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
});
app.UseMigration<PassengerDbContext>(env);
app.UseCorrelationId();
app.UseMigration<PassengerDbContext>();
app.UseCustomHealthCheck();
app.MapGrpcService<PassengerGrpcServices>();
app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
if (env.IsDevelopment())
{
app.UseCustomSwagger();
app.UseAspnetOpenApi();
}
return app;

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.65.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -7,8 +7,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>