├── .dockerignore
├── .github
└── workflows
│ ├── SonarCloud.yml
│ ├── docs.yml
│ ├── publish-AsyncMonolith.Ef-nuget-package.yaml
│ ├── publish-AsyncMonolith.MsSql-nuget-package.yaml
│ ├── publish-AsyncMonolith.MySql-nuget-package.yaml
│ ├── publish-AsyncMonolith.PostgreSql-nuget-package.yaml
│ ├── publish-AsyncMonolith.TestHelpers-nuget-package.yaml
│ └── test.yml
├── .gitignore
├── .idea
└── .idea.AsyncMonolith
│ └── .idea
│ ├── .gitignore
│ ├── encodings.xml
│ ├── indexLayout.xml
│ └── vcs.xml
├── AsyncMonolith.Ef
├── AsyncMonolith.Ef.csproj
├── EfConsumerMessageFetcher.cs
├── EfProducerService.cs
├── EfScheduledMessageFetcher.cs
└── StartupExtensions.cs
├── AsyncMonolith.MariaDb
├── AsyncMonolith.MariaDb.csproj
├── MariaDbConsumerMessageFetcher.cs
├── MariaDbProducerService.cs
├── MariaDbScheduledMessageFetcher.cs
└── StartupExtensions.cs
├── AsyncMonolith.MsSql
├── AsyncMonolith.MsSql.csproj
├── MsSqlConsumerMessageFetcher.cs
├── MsSqlProducerService.cs
├── MsSqlScheduledMessageFetcher.cs
└── StartupExtensions.cs
├── AsyncMonolith.MySql
├── AsyncMonolith.MySql.csproj
├── MySqlConsumerMessageFetcher.cs
├── MySqlProducerService.cs
├── MySqlScheduledMessageFetcher.cs
└── StartupExtensions.cs
├── AsyncMonolith.PostgreSql
├── AsyncMonolith.PostgreSql.csproj
├── PostgreSqlConsumerMessageFetcher.cs
├── PostgreSqlProducerService.cs
├── PostgreSqlScheduledMessageFetcher.cs
└── StartupExtensions.cs
├── AsyncMonolith.TestHelpers
├── AsyncMonolith.TestHelpers.csproj
├── ConsumerMessageTestHelpers.cs
├── ConsumerTestBase.cs
├── FakeIdGenerator.cs
├── FakeProducerService.cs
├── FakeScheduleService.cs
├── SetupTestHelpers.cs
└── TestConsumerMessageProcessor.cs
├── AsyncMonolith.Tests
├── AsyncMonolith.Tests.csproj
├── ConsumerMessageFetcherTests.cs
├── ConsumerMessageProcessorTests.cs
├── ConsumerRegistryTests.cs
├── Infra
│ ├── DbTestsBase.cs
│ ├── DbType.cs
│ ├── EfTestDbContainer.cs
│ ├── ExceptionConsumer.cs
│ ├── ExceptionConsumer2Attempts.cs
│ ├── ExceptionConsumer2AttemptsMessage.cs
│ ├── ExceptionConsumerMessage.cs
│ ├── MariaDbTestDbContainer.cs
│ ├── MsSqlTestDbContainer.cs
│ ├── MultiConsumer1.cs
│ ├── MultiConsumer2.cs
│ ├── MultiConsumerMessage.cs
│ ├── MySqlTestDbContainer.cs
│ ├── PostgreSqlTestDbContainer.cs
│ ├── SingleConsumer.cs
│ ├── SingleConsumerMessage.cs
│ ├── TestConsumerInvocations.cs
│ ├── TestDbContainerBase.cs
│ ├── TestDbContext.cs
│ ├── TestServiceHelpers.cs
│ ├── TimeoutConsumer.cs
│ └── TimeoutConsumerMessage.cs
├── ProducerServiceDbTests.cs
├── ProducerServiceTests.cs
├── ScheduledMessageFetcherTests.cs
├── ScheduledMessageProcessorTests.cs
└── ScheduledMessageServiceTests.cs
├── AsyncMonolith.sln
├── AsyncMonolith.sln.DotSettings
├── AsyncMonolith
├── AsyncMonolith.csproj
├── Consumers
│ ├── BaseConsumer.cs
│ ├── ConsumerAttemptsAttribute.cs
│ ├── ConsumerMessage.cs
│ ├── ConsumerMessageProcessor.cs
│ ├── ConsumerMessageProcessorFactory.cs
│ ├── ConsumerRegistry.cs
│ ├── ConsumerTimeoutAttribute.cs
│ ├── IConsumer.cs
│ ├── IConsumerMessageFetcher.cs
│ ├── IConsumerPayload.cs
│ └── PoisonedMessage.cs
├── Producers
│ └── IProducerService.cs
├── Scheduling
│ ├── IScheduleService.cs
│ ├── IScheduledMessageFetcher.cs
│ ├── ScheduleService.cs
│ ├── ScheduledMessage.cs
│ ├── ScheduledMessageProcessor.cs
│ └── ScheduledMessageProcessorFactory.cs
├── Utilities
│ ├── AsyncMonolithIdGenerator.cs
│ ├── AsyncMonolithInstrumentation.cs
│ ├── AsyncMonolithSettings.cs
│ └── StartupExtensions.cs
└── logo.png
├── Demo
├── ApplicationDbContext.cs
├── Counter
│ ├── TotalValueConsumer.cs
│ ├── TotalValueService.cs
│ ├── ValueController.cs
│ ├── ValuePersisted.cs
│ ├── ValueSubmitted.cs
│ └── ValueSubmittedConsumer.cs
├── Demo.csproj
├── Demo.http
├── Dockerfile
├── Migrations
│ ├── 20240615074531_InitialMigration.Designer.cs
│ ├── 20240615074531_InitialMigration.cs
│ ├── 20240618182258_TraceId.Designer.cs
│ ├── 20240618182258_TraceId.cs
│ └── ApplicationDbContextModelSnapshot.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Spam
│ ├── SpamController.cs
│ ├── SpamMessage.cs
│ ├── SpamMessageConsumer.cs
│ ├── SpamResultService.cs
│ └── SubmittedValue.cs
├── appsettings.Development.json
└── appsettings.json
├── Diagrams
├── AsyncMonolith.drawio
└── AsyncMonolith.svg
├── LICENSE
├── README.md
├── Schemas
├── asyncmonolith_mariadb.sql
├── asyncmonolith_mssql.sql
├── asyncmonolith_mysql.sql
└── asyncmonolith_postgresql.sql
├── default.DotSettings
├── docker-compose.dcproj
├── docker-compose.override.yml
├── docker-compose.yml
├── docs
├── assets
│ ├── internals.svg
│ └── logo.png
├── contributing.md
├── demo.md
├── guides
│ ├── changing-messages.md
│ ├── consuming-messages.md
│ ├── opentelemetry.md
│ ├── producing-messages.md
│ └── scheduling-messages.md
├── index.md
├── internals.md
├── posts
│ ├── idempotency.md
│ ├── mediator.md
│ └── transactional-outbox.md
├── quickstart.md
├── releases.md
├── support.md
├── tests.md
└── warnings.md
├── launchSettings.json
└── mkdocs.yml
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
26 | !**/.gitignore
27 | !.git/HEAD
28 | !.git/config
29 | !.git/packed-refs
30 | !.git/refs/heads/**
--------------------------------------------------------------------------------
/.github/workflows/SonarCloud.yml:
--------------------------------------------------------------------------------
1 | name: SonarCloud
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, synchronize, reopened]
9 | jobs:
10 | build:
11 | defaults:
12 | run:
13 | working-directory: AsyncMonolith
14 | name: Build and analyze
15 | runs-on: windows-latest
16 | steps:
17 | - name: Set up JDK 17
18 | uses: actions/setup-java@v3
19 | with:
20 | java-version: 17
21 | distribution: 'zulu' # Alternative distribution options are available.
22 | - uses: actions/checkout@v3
23 | with:
24 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
25 | - name: Cache SonarCloud packages
26 | uses: actions/cache@v3
27 | with:
28 | path: ~\sonar\cache
29 | key: ${{ runner.os }}-sonar
30 | restore-keys: ${{ runner.os }}-sonar
31 | - name: Cache SonarCloud scanner
32 | id: cache-sonar-scanner
33 | uses: actions/cache@v3
34 | with:
35 | path: .\.sonar\scanner
36 | key: ${{ runner.os }}-sonar-scanner
37 | restore-keys: ${{ runner.os }}-sonar-scanner
38 | - name: Install SonarCloud scanner
39 | if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
40 | shell: powershell
41 | run: |
42 | New-Item -Path .\.sonar\scanner -ItemType Directory
43 | dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
44 | - name: Build and analyze
45 | env:
46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
47 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
48 | shell: powershell
49 | run: |
50 | .\.sonar\scanner\dotnet-sonarscanner begin /k:"timmoth_asyncmonolith" /o:"timmoth" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
51 | dotnet build
52 | .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
53 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - main
7 | permissions:
8 | contents: write
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 | - name: Configure Git Credentials
15 | run: |
16 | git config user.name github-actions[bot]
17 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com
18 | - uses: actions/setup-python@v5
19 | with:
20 | python-version: 3.x
21 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
22 | - uses: actions/cache@v4
23 | with:
24 | key: mkdocs-material-${{ env.cache_id }}
25 | path: .cache
26 | restore-keys: |
27 | mkdocs-material-
28 | - run: pip install mkdocs-material
29 | - run: mkdocs gh-deploy --force
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish-AsyncMonolith.Ef-nuget-package.yaml:
--------------------------------------------------------------------------------
1 | name: Publish AsyncMonolith.Ef nuget package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: AsyncMonolith.Ef
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Build and Package
22 | run: |
23 | dotnet restore
24 | dotnet build -c Release
25 | dotnet pack --configuration Release --output nupkg
26 |
27 | - name: Publish to NuGet
28 | run: |
29 | dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish-AsyncMonolith.MsSql-nuget-package.yaml:
--------------------------------------------------------------------------------
1 | name: Publish AsyncMonolith.MsSql nuget package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: AsyncMonolith.MsSql
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Build and Package
22 | run: |
23 | dotnet restore
24 | dotnet build -c Release
25 | dotnet pack --configuration Release --output nupkg
26 |
27 | - name: Publish to NuGet
28 | run: |
29 | dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish-AsyncMonolith.MySql-nuget-package.yaml:
--------------------------------------------------------------------------------
1 | name: Publish AsyncMonolith.MySql nuget package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: AsyncMonolith.MySql
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Build and Package
22 | run: |
23 | dotnet restore
24 | dotnet build -c Release
25 | dotnet pack --configuration Release --output nupkg
26 |
27 | - name: Publish to NuGet
28 | run: |
29 | dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish-AsyncMonolith.PostgreSql-nuget-package.yaml:
--------------------------------------------------------------------------------
1 | name: Publish AsyncMonolith.PostgreSql nuget package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: AsyncMonolith.PostgreSql
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Build and Package
22 | run: |
23 | dotnet restore
24 | dotnet build -c Release
25 | dotnet pack --configuration Release --output nupkg
26 |
27 | - name: Publish to NuGet
28 | run: |
29 | dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish-AsyncMonolith.TestHelpers-nuget-package.yaml:
--------------------------------------------------------------------------------
1 | name: Publish AsyncMonolith.TestHelpers nuget package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: AsyncMonolith.TestHelpers
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Build and Package
22 | run: |
23 | dotnet restore
24 | dotnet build -c Release
25 | dotnet pack --configuration Release --output nupkg
26 |
27 | - name: Publish to NuGet
28 | run: |
29 | dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v3
12 |
13 | - name: Setup .NET
14 | uses: actions/setup-dotnet@v3
15 | with:
16 | dotnet-version: 8.0.x
17 |
18 | - name: Test
19 | run: dotnet test AsyncMonolith.Tests --verbosity normal
--------------------------------------------------------------------------------
/.idea/.idea.AsyncMonolith/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /modules.xml
6 | /contentModel.xml
7 | /projectSettingsUpdater.xml
8 | /.idea.AsyncMonolith.iml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 |
--------------------------------------------------------------------------------
/.idea/.idea.AsyncMonolith/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.AsyncMonolith/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.AsyncMonolith/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AsyncMonolith.Ef/AsyncMonolith.Ef.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.Ef
11 | 8.0.7
12 | Tim Jones
13 | Aptacode
14 | Entity Framework interface for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.Ef
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | true
40 | AsyncMonolith.dll
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/AsyncMonolith.Ef/EfConsumerMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 |
6 | namespace AsyncMonolith.Ef;
7 |
8 | ///
9 | /// Fetches consumer messages using Entity Framework.
10 | ///
11 | public sealed class EfConsumerMessageFetcher : IConsumerMessageFetcher
12 | {
13 | private readonly IOptions _options;
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// The options for AsyncMonolithSettings.
19 | public EfConsumerMessageFetcher(IOptions options)
20 | {
21 | _options = options;
22 | }
23 |
24 | ///
25 | /// Fetches consumer messages from the database.
26 | ///
27 | /// The DbSet of consumer messages.
28 | /// The current time.
29 | /// The cancellation token.
30 | /// A task that represents the asynchronous operation. The task result contains a list of consumer messages.
31 | public Task> Fetch(DbSet consumerSet, long currentTime,
32 | CancellationToken cancellationToken = default)
33 | {
34 | return consumerSet
35 | .Where(m => m.AvailableAfter <= currentTime)
36 | .OrderBy(m => m.CreatedAt)
37 | .Take(_options.Value.ProcessorBatchSize)
38 | .ToListAsync(cancellationToken);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/AsyncMonolith.Ef/EfScheduledMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Scheduling;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 |
6 | namespace AsyncMonolith.Ef;
7 |
8 | ///
9 | /// Fetches scheduled messages from the database using Entity Framework.
10 | ///
11 | public sealed class EfScheduledMessageFetcher : IScheduledMessageFetcher
12 | {
13 | private readonly IOptions _options;
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// The options for AsyncMonolith.
19 | public EfScheduledMessageFetcher(IOptions options)
20 | {
21 | _options = options;
22 | }
23 |
24 | ///
25 | /// Fetches scheduled messages from the database.
26 | ///
27 | /// The DbSet of scheduled messages.
28 | /// The current time.
29 | /// The cancellation token.
30 | /// A task representing the asynchronous operation, containing the list of fetched scheduled messages.
31 | public Task> Fetch(DbSet set, long currentTime,
32 | CancellationToken cancellationToken = default)
33 | {
34 | return set.Where(m => m.AvailableAfter <= currentTime)
35 | .OrderBy(m => m.AvailableAfter)
36 | .Take(_options.Value.ProcessorBatchSize)
37 | .ToListAsync(cancellationToken);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AsyncMonolith.Ef/StartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Producers;
4 | using AsyncMonolith.Scheduling;
5 | using AsyncMonolith.Utilities;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace AsyncMonolith.Ef;
10 |
11 | ///
12 | /// Extension methods for configuring EF AsyncMonolith in the IServiceCollection.
13 | ///
14 | public static class StartupExtensions
15 | {
16 | ///
17 | /// Adds EF AsyncMonolith to the IServiceCollection.
18 | ///
19 | /// The DbContext type.
20 | /// The IServiceCollection to add the services to.
21 | /// The action used to configure the settings.
22 | /// A reference to this instance after the operation has completed.
23 | /// Thrown when the ConsumerMessageProcessorCount or ScheduledMessageProcessorCount is greater than 1.
24 | public static IServiceCollection AddEfAsyncMonolith(
25 | this IServiceCollection services,
26 | Action settings) where T : DbContext =>
27 | AddEfAsyncMonolith(services, settings, AsyncMonolithSettings.Default);
28 |
29 | ///
30 | /// Adds EF AsyncMonolith to the IServiceCollection.
31 | ///
32 | /// The DbContext type.
33 | /// The IServiceCollection to add the services to.
34 | /// The assembly containing the DbContext.
35 | /// The optional AsyncMonolithSettings.
36 | /// A reference to this instance after the operation has completed.
37 | /// Thrown when the ConsumerMessageProcessorCount or ScheduledMessageProcessorCount is greater than 1.
38 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
39 | public static IServiceCollection AddEfAsyncMonolith(
40 | this IServiceCollection services,
41 | Assembly assembly,
42 | AsyncMonolithSettings? settings = null) where T : DbContext =>
43 | AddEfAsyncMonolith(
44 | services,
45 | configuration => configuration.RegisterTypesFromAssembly(assembly),
46 | settings ?? AsyncMonolithSettings.Default);
47 |
48 | private static IServiceCollection AddEfAsyncMonolith(
49 | this IServiceCollection services,
50 | Action configuration,
51 | AsyncMonolithSettings settings) where T : DbContext
52 | {
53 | configuration(settings);
54 |
55 | if (settings.ConsumerMessageProcessorCount > 1)
56 | {
57 | throw new ArgumentException(
58 | "AsyncMonolithSettings.ConsumerMessageProcessorCount can only be set to 1 when using 'DbType.Ef'.");
59 | }
60 |
61 | if (settings.ScheduledMessageProcessorCount > 1)
62 | {
63 | throw new ArgumentException(
64 | "AsyncMonolithSettings.ScheduledMessageProcessorCount can only be set to 1 when using 'DbType.Ef'.");
65 | }
66 |
67 | services.InternalAddAsyncMonolith(settings);
68 | services.AddScoped>();
69 | services.AddSingleton();
70 | services.AddSingleton();
71 | return services;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/AsyncMonolith.MariaDb/AsyncMonolith.MariaDb.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.MariaDb
11 | 8.0.7
12 | Tim Jones
13 | Aptacode
14 | MariaDb interface for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.MariaDb
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | true
39 | AsyncMonolith.dll
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AsyncMonolith.MariaDb/MariaDbConsumerMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using MySqlConnector;
6 |
7 | namespace AsyncMonolith.MariaDb;
8 |
9 | ///
10 | /// Represents a message fetcher for consumer messages in MariaDb.
11 | ///
12 | public sealed class MariaDbConsumerMessageFetcher : IConsumerMessageFetcher
13 | {
14 | private const string MariaDb = @"
15 | SELECT *
16 | FROM consumer_messages
17 | WHERE available_after <= @currentTime
18 | ORDER BY created_at
19 | LIMIT @batchSize
20 | FOR UPDATE SKIP LOCKED";
21 |
22 | private readonly IOptions _options;
23 |
24 | ///
25 | /// Initializes a new instance of the class.
26 | ///
27 | /// The options for AsyncMonolith settings.
28 | public MariaDbConsumerMessageFetcher(IOptions options)
29 | {
30 | _options = options;
31 | }
32 |
33 | ///
34 | /// Fetches consumer messages from the database.
35 | ///
36 | /// The DbSet of consumer messages.
37 | /// The current time.
38 | /// The cancellation token.
39 | /// A task that represents the asynchronous operation. The task result contains a list of consumer messages.
40 | public Task> Fetch(DbSet consumerSet, long currentTime,
41 | CancellationToken cancellationToken = default)
42 | {
43 | return consumerSet
44 | .FromSqlRaw(MariaDb, new MySqlParameter("@currentTime", currentTime),
45 | new MySqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
46 | .ToListAsync(cancellationToken);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/AsyncMonolith.MariaDb/MariaDbScheduledMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Scheduling;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using MySqlConnector;
6 |
7 | namespace AsyncMonolith.MariaDb;
8 |
9 | ///
10 | /// Fetches scheduled messages from MariaDb.
11 | ///
12 | public sealed class MariaDbScheduledMessageFetcher : IScheduledMessageFetcher
13 | {
14 | private const string MariaDb = @"
15 | SELECT *
16 | FROM scheduled_messages
17 | WHERE available_after <= @currentTime
18 | LIMIT @batchSize
19 | FOR UPDATE SKIP LOCKED";
20 |
21 | private readonly IOptions _options;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The options for AsyncMonolith.
27 | public MariaDbScheduledMessageFetcher(IOptions options)
28 | {
29 | _options = options;
30 | }
31 |
32 | ///
33 | /// Fetches scheduled messages from the database.
34 | ///
35 | /// The of scheduled messages.
36 | /// The current time.
37 | /// The cancellation token.
38 | /// A task that represents the asynchronous operation. The task result contains a list of fetched scheduled messages.
39 | public Task> Fetch(DbSet set, long currentTime,
40 | CancellationToken cancellationToken = default)
41 | {
42 | return set
43 | .FromSqlRaw(MariaDb, new MySqlParameter("@currentTime", currentTime),
44 | new MySqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
45 | .ToListAsync(cancellationToken);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AsyncMonolith.MariaDb/StartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Producers;
4 | using AsyncMonolith.Scheduling;
5 | using AsyncMonolith.Utilities;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace AsyncMonolith.MariaDb;
10 | ///
11 | /// AsyncMonolith MariaDb startup extensions
12 | ///
13 | public static class StartupExtensions
14 | {
15 | ///
16 | /// Adds MariaDb implementation of AsyncMonolith to the IServiceCollection.
17 | ///
18 | /// The DbContext type.
19 | /// The IServiceCollection to add the services to.
20 | /// The action used to configure the settings.
21 | /// A reference to this instance after the operation has completed.
22 | public static IServiceCollection AddMariaDbAsyncMonolith(
23 | this IServiceCollection services,
24 | Action settings) where T : DbContext =>
25 | AddMariaDbAsyncMonolith(services, settings, AsyncMonolithSettings.Default);
26 |
27 | ///
28 | /// Adds MariaDb implementation of AsyncMonolith to the IServiceCollection.
29 | ///
30 | /// The DbContext type.
31 | /// The IServiceCollection to add the services to.
32 | /// The assembly containing the DbContext.
33 | /// Optional AsyncMonolith settings.
34 | /// A reference to this instance after the operation has completed.
35 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
36 | public static IServiceCollection AddMariaDbAsyncMonolith(
37 | this IServiceCollection services,
38 | Assembly assembly,
39 | AsyncMonolithSettings? settings = null) where T : DbContext =>
40 | AddMariaDbAsyncMonolith(
41 | services,
42 | configuration => configuration.RegisterTypesFromAssembly(assembly),
43 | settings ?? AsyncMonolithSettings.Default);
44 |
45 | private static IServiceCollection AddMariaDbAsyncMonolith(
46 | this IServiceCollection services,
47 | Action configuration,
48 | AsyncMonolithSettings settings) where T : DbContext
49 | {
50 | configuration(settings);
51 |
52 | services.InternalAddAsyncMonolith(settings);
53 | services.AddScoped>();
54 | services.AddSingleton();
55 | services.AddSingleton();
56 | return services;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/AsyncMonolith.MsSql/AsyncMonolith.MsSql.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.MsSql
11 | 8.0.7
12 | Tim Jones
13 | Aptacode
14 | MsSql interface for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.MsSql
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | true
39 | AsyncMonolith.dll
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/AsyncMonolith.MsSql/MsSqlConsumerMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.Data.SqlClient;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace AsyncMonolith.MsSql;
8 |
9 | ///
10 | /// Represents a message fetcher for consuming messages from a SQL Server database.
11 | ///
12 | public sealed class MsSqlConsumerMessageFetcher : IConsumerMessageFetcher
13 | {
14 | private const string MsSql = @"
15 | SELECT TOP (@batchSize) *
16 | FROM consumer_messages WITH (ROWLOCK, READPAST)
17 | WHERE available_after <= @currentTime
18 | ORDER BY created_at";
19 |
20 | private readonly IOptions _options;
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | /// The options for the AsyncMonolith settings.
26 | public MsSqlConsumerMessageFetcher(IOptions options)
27 | {
28 | _options = options;
29 | }
30 |
31 | ///
32 | /// Fetches a batch of consumer messages from the database.
33 | ///
34 | /// The of consumer messages.
35 | /// The current time.
36 | /// The cancellation token.
37 | /// A task that represents the asynchronous operation. The task result contains the list of fetched consumer messages.
38 | public Task> Fetch(DbSet consumerSet, long currentTime,
39 | CancellationToken cancellationToken = default)
40 | {
41 | return consumerSet
42 | .FromSqlRaw(MsSql, new SqlParameter("@currentTime", currentTime),
43 | new SqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
44 | .ToListAsync(cancellationToken);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/AsyncMonolith.MsSql/MsSqlScheduledMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Scheduling;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.Data.SqlClient;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace AsyncMonolith.MsSql;
8 |
9 | ///
10 | /// Fetches scheduled messages from the MsSql database.
11 | ///
12 | public sealed class MsSqlScheduledMessageFetcher : IScheduledMessageFetcher
13 | {
14 | private const string MsSql = @"
15 | SELECT TOP (@batchSize) *
16 | FROM scheduled_messages WITH (ROWLOCK, READPAST)
17 | WHERE available_after <= @currentTime";
18 |
19 | private readonly IOptions _options;
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | /// The options for the AsyncMonolith settings.
25 | public MsSqlScheduledMessageFetcher(IOptions options)
26 | {
27 | _options = options;
28 | }
29 |
30 | ///
31 | /// Fetches scheduled messages from the database.
32 | ///
33 | /// The DbSet of scheduled messages.
34 | /// The current time.
35 | /// The cancellation token.
36 | /// A task that represents the asynchronous operation. The task result contains a list of scheduled messages.
37 | public Task> Fetch(DbSet set, long currentTime,
38 | CancellationToken cancellationToken = default)
39 | {
40 | return set
41 | .FromSqlRaw(MsSql, new SqlParameter("@currentTime", currentTime),
42 | new SqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
43 | .ToListAsync(cancellationToken);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/AsyncMonolith.MsSql/StartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Producers;
4 | using AsyncMonolith.Scheduling;
5 | using AsyncMonolith.Utilities;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace AsyncMonolith.MsSql;
10 |
11 | ///
12 | /// AsyncMonolith MsSql startup extensions
13 | ///
14 | public static class StartupExtensions
15 | {
16 | ///
17 | /// Adds the MsSqlAsyncMonolith services to the IServiceCollection.
18 | ///
19 | /// The DbContext type.
20 | /// The IServiceCollection to add the services to.
21 | /// The action used to configure the settings.
22 | /// A reference to this instance after the operation has completed.
23 | public static IServiceCollection AddMsSqlAsyncMonolith(
24 | this IServiceCollection services,
25 | Action settings) where T : DbContext =>
26 | AddMsSqlAsyncMonolith(services, settings, AsyncMonolithSettings.Default);
27 |
28 | ///
29 | /// Adds the MsSqlAsyncMonolith services to the IServiceCollection.
30 | ///
31 | /// The type of the DbContext.
32 | /// The IServiceCollection to add the services to.
33 | /// The assembly containing the DbContext and message handlers.
34 | /// The optional AsyncMonolithSettings.
35 | /// A reference to this instance after the operation has completed.
36 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
37 | public static IServiceCollection AddMsSqlAsyncMonolith(this IServiceCollection services, Assembly assembly,
38 | AsyncMonolithSettings? settings = null) where T : DbContext =>
39 | AddMsSqlAsyncMonolith(
40 | services,
41 | configuration => configuration.RegisterTypesFromAssembly(assembly),
42 | settings ?? AsyncMonolithSettings.Default);
43 |
44 | private static IServiceCollection AddMsSqlAsyncMonolith(
45 | this IServiceCollection services,
46 | Action configuration,
47 | AsyncMonolithSettings settings) where T : DbContext
48 | {
49 | configuration(settings);
50 | services.InternalAddAsyncMonolith(settings);
51 | services.AddScoped>();
52 | services.AddSingleton();
53 | services.AddSingleton();
54 | return services;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/AsyncMonolith.MySql/AsyncMonolith.MySql.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.MySql
11 | 8.0.7
12 | Tim Jones
13 | Aptacode
14 | MySql interface for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.MySql
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | true
39 | AsyncMonolith.dll
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AsyncMonolith.MySql/MySqlConsumerMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using MySqlConnector;
6 |
7 | namespace AsyncMonolith.MySql;
8 |
9 | ///
10 | /// Represents a message fetcher for consumer messages in MySQL.
11 | ///
12 | public sealed class MySqlConsumerMessageFetcher : IConsumerMessageFetcher
13 | {
14 | private const string MySql = @"
15 | SELECT *
16 | FROM consumer_messages
17 | WHERE available_after <= @currentTime
18 | ORDER BY created_at
19 | LIMIT @batchSize
20 | FOR UPDATE SKIP LOCKED";
21 |
22 | private readonly IOptions _options;
23 |
24 | ///
25 | /// Initializes a new instance of the class.
26 | ///
27 | /// The options for AsyncMonolith settings.
28 | public MySqlConsumerMessageFetcher(IOptions options)
29 | {
30 | _options = options;
31 | }
32 |
33 | ///
34 | /// Fetches consumer messages from the database.
35 | ///
36 | /// The DbSet of consumer messages.
37 | /// The current time.
38 | /// The cancellation token.
39 | /// A task that represents the asynchronous operation. The task result contains a list of consumer messages.
40 | public Task> Fetch(DbSet consumerSet, long currentTime,
41 | CancellationToken cancellationToken = default)
42 | {
43 | return consumerSet
44 | .FromSqlRaw(MySql, new MySqlParameter("@currentTime", currentTime),
45 | new MySqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
46 | .ToListAsync(cancellationToken);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/AsyncMonolith.MySql/MySqlScheduledMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Scheduling;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using MySqlConnector;
6 |
7 | namespace AsyncMonolith.MySql;
8 |
9 | ///
10 | /// Fetches scheduled messages from MySQL database.
11 | ///
12 | public sealed class MySqlScheduledMessageFetcher : IScheduledMessageFetcher
13 | {
14 | private const string MySql = @"
15 | SELECT *
16 | FROM scheduled_messages
17 | WHERE available_after <= @currentTime
18 | LIMIT @batchSize
19 | FOR UPDATE SKIP LOCKED";
20 |
21 | private readonly IOptions _options;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The options for AsyncMonolith.
27 | public MySqlScheduledMessageFetcher(IOptions options)
28 | {
29 | _options = options;
30 | }
31 |
32 | ///
33 | /// Fetches scheduled messages from the database.
34 | ///
35 | /// The of scheduled messages.
36 | /// The current time.
37 | /// The cancellation token.
38 | /// A task that represents the asynchronous operation. The task result contains a list of fetched scheduled messages.
39 | public Task> Fetch(DbSet set, long currentTime,
40 | CancellationToken cancellationToken = default)
41 | {
42 | return set
43 | .FromSqlRaw(MySql, new MySqlParameter("@currentTime", currentTime),
44 | new MySqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
45 | .ToListAsync(cancellationToken);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AsyncMonolith.MySql/StartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Producers;
4 | using AsyncMonolith.Scheduling;
5 | using AsyncMonolith.Utilities;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace AsyncMonolith.MySql;
10 | ///
11 | /// AsyncMonolith MySql startup extensions
12 | ///
13 | public static class StartupExtensions
14 | {
15 | ///
16 | /// Adds MySql implementation of AsyncMonolith to the IServiceCollection.
17 | ///
18 | /// The DbContext type.
19 | /// The IServiceCollection to add the services to.
20 | /// The action used to configure the settings.
21 | /// A reference to this instance after the operation has completed.
22 | public static IServiceCollection AddMySqlAsyncMonolith(
23 | this IServiceCollection services,
24 | Action settings) where T : DbContext =>
25 | AddMySqlAsyncMonolith(services, settings, AsyncMonolithSettings.Default);
26 |
27 | ///
28 | /// Adds MySql implementation of AsyncMonolith to the IServiceCollection.
29 | ///
30 | /// The DbContext type.
31 | /// The IServiceCollection to add the services to.
32 | /// The assembly containing the DbContext.
33 | /// Optional AsyncMonolith settings.
34 | /// A reference to this instance after the operation has completed.
35 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
36 | public static IServiceCollection AddMySqlAsyncMonolith(this IServiceCollection services, Assembly assembly,
37 | AsyncMonolithSettings? settings = null) where T : DbContext =>
38 | AddMySqlAsyncMonolith(
39 | services,
40 | configuration => configuration.RegisterTypesFromAssembly(assembly),
41 | settings ?? AsyncMonolithSettings.Default);
42 |
43 | private static IServiceCollection AddMySqlAsyncMonolith(
44 | this IServiceCollection services,
45 | Action configuration,
46 | AsyncMonolithSettings settings) where T : DbContext
47 | {
48 | configuration(settings);
49 | services.InternalAddAsyncMonolith(settings);
50 | services.AddScoped>();
51 | services.AddSingleton();
52 | services.AddSingleton();
53 | return services;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/AsyncMonolith.PostgreSql/AsyncMonolith.PostgreSql.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.PostgreSql
11 | 8.0.7
12 | Tim Jones
13 | Aptacode
14 | PostgreSql interface for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.PostgreSql
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | true
40 | AsyncMonolith.dll
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/AsyncMonolith.PostgreSql/PostgreSqlConsumerMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using Npgsql;
6 |
7 | namespace AsyncMonolith.PostgreSql;
8 |
9 | ///
10 | /// Represents a consumer message fetcher implementation for PostgreSQL.
11 | ///
12 | public sealed class PostgreSqlConsumerMessageFetcher : IConsumerMessageFetcher
13 | {
14 | private const string PgSql = @"
15 | SELECT *
16 | FROM consumer_messages
17 | WHERE available_after <= @currentTime
18 | ORDER BY created_at
19 | FOR UPDATE SKIP LOCKED
20 | LIMIT @batchSize";
21 |
22 | private readonly IOptions _options;
23 |
24 | ///
25 | /// Initializes a new instance of the class.
26 | ///
27 | /// The options for the AsyncMonolith settings.
28 | public PostgreSqlConsumerMessageFetcher(IOptions options)
29 | {
30 | _options = options;
31 | }
32 |
33 | ///
34 | /// Fetches a batch of consumer messages from the PostgreSQL database.
35 | ///
36 | /// The DbSet of consumer messages.
37 | /// The current time.
38 | /// The cancellation token.
39 | /// A task that represents the asynchronous operation. The task result contains the list of fetched consumer messages.
40 | public Task> Fetch(DbSet consumerSet, long currentTime,
41 | CancellationToken cancellationToken = default)
42 | {
43 | return consumerSet
44 | .FromSqlRaw(PgSql, new NpgsqlParameter("@currentTime", currentTime),
45 | new NpgsqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
46 | .ToListAsync(cancellationToken);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/AsyncMonolith.PostgreSql/PostgreSqlScheduledMessageFetcher.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Scheduling;
2 | using AsyncMonolith.Utilities;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Options;
5 | using Npgsql;
6 |
7 | namespace AsyncMonolith.PostgreSql;
8 |
9 | ///
10 | /// Fetches scheduled messages from a PostgreSQL database.
11 | ///
12 | public sealed class PostgreSqlScheduledMessageFetcher : IScheduledMessageFetcher
13 | {
14 | private const string PgSql = @"
15 | SELECT *
16 | FROM scheduled_messages
17 | WHERE available_after <= @currentTime
18 | FOR UPDATE SKIP LOCKED
19 | LIMIT @batchSize";
20 |
21 | private readonly IOptions _options;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The options for the AsyncMonolith.
27 | public PostgreSqlScheduledMessageFetcher(IOptions options)
28 | {
29 | _options = options;
30 | }
31 |
32 | ///
33 | /// Fetches scheduled messages from the database.
34 | ///
35 | /// The of scheduled messages.
36 | /// The current time.
37 | /// The cancellation token.
38 | /// A task that represents the asynchronous operation. The task result contains a list of fetched scheduled messages.
39 | public Task> Fetch(DbSet set, long currentTime,
40 | CancellationToken cancellationToken = default)
41 | {
42 | return set
43 | .FromSqlRaw(PgSql, new NpgsqlParameter("@currentTime", currentTime),
44 | new NpgsqlParameter("@batchSize", _options.Value.ProcessorBatchSize))
45 | .ToListAsync(cancellationToken);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AsyncMonolith.PostgreSql/StartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Producers;
4 | using AsyncMonolith.Scheduling;
5 | using AsyncMonolith.Utilities;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace AsyncMonolith.PostgreSql;
10 |
11 | ///
12 | /// AsyncMonolith PostgreSql startup extensions
13 | ///
14 | public static class StartupExtensions
15 | {
16 | ///
17 | /// Adds the PostgreSql implementation of the AsyncMonolith to the service collection.
18 | ///
19 | /// The type of the DbContext.
20 | /// The service collection.
21 | /// The action used to configure the settings.
22 | /// A reference to this instance after the operation has completed.
23 | public static IServiceCollection AddPostgreSqlAsyncMonolith(
24 | this IServiceCollection services,
25 | Action settings) where T : DbContext =>
26 | AddPostgreSqlAsyncMonolith(services, settings, AsyncMonolithSettings.Default);
27 |
28 | ///
29 | /// Adds the PostgreSql implementation of the AsyncMonolith to the service collection.
30 | ///
31 | /// The type of the DbContext.
32 | /// The service collection.
33 | /// The assembly containing the DbContext.
34 | /// The optional AsyncMonolith settings.
35 | /// A reference to this instance after the operation has completed.
36 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
37 | public static IServiceCollection AddPostgreSqlAsyncMonolith(
38 | this IServiceCollection services,
39 | Assembly assembly,
40 | AsyncMonolithSettings? settings = null) where T : DbContext =>
41 | AddPostgreSqlAsyncMonolith(
42 | services,
43 | configuration => configuration.RegisterTypesFromAssembly(assembly),
44 | settings ?? AsyncMonolithSettings.Default);
45 |
46 | private static IServiceCollection AddPostgreSqlAsyncMonolith(
47 | this IServiceCollection services,
48 | Action configuration,
49 | AsyncMonolithSettings settings) where T : DbContext
50 | {
51 | configuration(settings);
52 | services.InternalAddAsyncMonolith(settings);
53 | services.AddScoped>();
54 | services.AddSingleton();
55 | services.AddSingleton();
56 | return services;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/AsyncMonolith.TestHelpers/AsyncMonolith.TestHelpers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | true
8 | preview
9 | true
10 | AsyncMonolith.TestHelpers
11 | 8.0.5
12 | Tim Jones
13 | Aptacode
14 | Test helpers for AsyncMonolith
15 | https://github.com/Timmoth/AsyncMonolith
16 | https://github.com/Timmoth/AsyncMonolith
17 | git
18 | Monolith Messaging Scheduling Async
19 | AsyncMonolith.TestHelpers
20 | logo.png
21 | True
22 | true
23 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | True
37 | \
38 |
39 |
40 |
41 |
42 |
43 | true
44 | AsyncMonolith.dll
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/AsyncMonolith.TestHelpers/ConsumerTestBase.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using System.Globalization;
4 | using Microsoft.Extensions.Logging;
5 | using Microsoft.Extensions.Time.Testing;
6 | using Xunit.Abstractions;
7 | using Xunit;
8 |
9 | namespace AsyncMonolith.TestHelpers
10 | {
11 | ///
12 | /// Base class for consumer test classes.
13 | ///
14 | public abstract class ConsumerTestBase : IAsyncLifetime
15 | {
16 | private DateTime _startTime;
17 | private readonly LogLevel _logLevel;
18 | ///
19 | /// Fake time provider
20 | ///
21 | protected FakeTimeProvider FakeTime { get; private set; } = default!;
22 |
23 | ///
24 | /// Sets up the services for the test.
25 | ///
26 | /// The service collection.
27 | /// A task representing the asynchronous operation.
28 | protected abstract Task Setup(IServiceCollection services);
29 |
30 | private async Task Setup()
31 | {
32 | var services = new ServiceCollection();
33 | services.AddLogging(b =>
34 | {
35 | b.ClearProviders();
36 | b.AddFilter(logLevel => logLevel >= _logLevel);
37 | b.AddXUnit(TestOutput);
38 | });
39 |
40 | FakeTime = new FakeTimeProvider(DateTimeOffset.Parse("2020-08-31T10:00:00.0000000Z"));
41 | services.AddSingleton(FakeTime);
42 | await Setup(services);
43 |
44 | return services.BuildServiceProvider();
45 | }
46 |
47 | ///
48 | /// Initializes a new instance of the class.
49 | ///
50 | /// The test output helper.
51 | /// The log level.
52 | protected ConsumerTestBase(ITestOutputHelper testOutput, LogLevel logLevel = LogLevel.Information)
53 | {
54 | TestOutput = testOutput;
55 | _logLevel = logLevel;
56 | }
57 |
58 | ///
59 | /// Gets the test output helper.
60 | ///
61 | public ITestOutputHelper TestOutput { get; }
62 |
63 | ///
64 | /// Gets the service provider.
65 | ///
66 | public IServiceProvider Services { get; private set; } = default!;
67 |
68 | ///
69 | /// Initializes the test asynchronously.
70 | ///
71 | /// A task representing the asynchronous operation.
72 | public virtual async Task InitializeAsync()
73 | {
74 | _startTime = DateTime.Now;
75 | TestOutput.WriteLine($"[Lifecycle] Initialise {_startTime.ToString(CultureInfo.InvariantCulture)}");
76 | Services = await Setup();
77 | }
78 |
79 | ///
80 | /// Disposes the test asynchronously.
81 | ///
82 | /// A task representing the asynchronous operation.
83 | public virtual Task DisposeAsync()
84 | {
85 | TestOutput.WriteLine($"[Lifecycle] Dispose ({(DateTime.Now - _startTime).TotalSeconds}s)");
86 | return Task.CompletedTask;
87 | }
88 |
89 | ///
90 | /// Processes the consumer message.
91 | ///
92 | /// The type of the consumer.
93 | /// The type of the payload.
94 | /// The payload.
95 | /// The cancellation token.
96 | /// A task representing the asynchronous operation.
97 | protected async Task Process(V payload, CancellationToken cancellationToken = default) where T : BaseConsumer where V : IConsumerPayload
98 | {
99 | using var scope = Services.CreateScope();
100 | await TestConsumerMessageProcessor.Process(scope, payload, cancellationToken);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/AsyncMonolith.TestHelpers/FakeIdGenerator.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Utilities;
2 |
3 | namespace AsyncMonolith.TestHelpers;
4 |
5 | ///
6 | /// Represents a fake ID generator for testing purposes.
7 | ///
8 | public class FakeIdGenerator : IAsyncMonolithIdGenerator
9 | {
10 | ///
11 | /// Gets or sets the count of generated IDs.
12 | ///
13 | public int Count { get; private set; }
14 |
15 | ///
16 | /// Generates a fake ID.
17 | ///
18 | /// A string representing the generated ID.
19 | public string GenerateId()
20 | {
21 | return $"fake-id-{Count++}";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AsyncMonolith.TestHelpers/FakeScheduleService.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using AsyncMonolith.Consumers;
3 | using AsyncMonolith.Scheduling;
4 | using AsyncMonolith.Utilities;
5 | using Cronos;
6 |
7 | namespace AsyncMonolith.TestHelpers;
8 |
9 | ///
10 | /// Represents a fake implementation of the interface for testing purposes.
11 | ///
12 | public sealed class FakeScheduleService : IScheduleService
13 | {
14 | private readonly IAsyncMonolithIdGenerator _fakeIdGenerator;
15 | private readonly TimeProvider _timeProvider;
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | /// The time provider.
21 | /// The fake ID generator.
22 | public FakeScheduleService(TimeProvider timeProvider, IAsyncMonolithIdGenerator fakeIdGenerator)
23 | {
24 | _timeProvider = timeProvider;
25 | _fakeIdGenerator = fakeIdGenerator;
26 | }
27 |
28 | ///
29 | /// Gets or sets the list of created scheduled messages.
30 | ///
31 | public List CreatedScheduledMessages { get; set; } = new();
32 |
33 | ///
34 | /// Gets or sets the list of deleted scheduled message tags.
35 | ///
36 | public List DeletedScheduledMessageTags { get; set; } = new();
37 |
38 | ///
39 | /// Gets or sets the list of deleted scheduled message IDs.
40 | ///
41 | public List DeletedScheduledMessageIds { get; set; } = new();
42 |
43 | ///
44 | /// Schedules a message for future execution.
45 | ///
46 | /// The type of the message payload.
47 | /// The message to schedule.
48 | /// The cron expression for scheduling the message.
49 | /// The timezone for scheduling the message.
50 | /// The optional tag for the scheduled message.
51 | /// The ID of the scheduled message.
52 | /// Thrown when the cron expression or timezone is invalid.
53 | public string Schedule(TK message, string chronExpression, string chronTimezone, string? tag = null)
54 | where TK : IConsumerPayload
55 | {
56 | var payload = JsonSerializer.Serialize(message);
57 | var id = _fakeIdGenerator.GenerateId();
58 |
59 | var expression = CronExpression.Parse(chronExpression, CronFormat.IncludeSeconds);
60 | if (expression == null)
61 | {
62 | throw new InvalidOperationException(
63 | $"Couldn't determine scheduled message cron expression: '{chronExpression}'");
64 | }
65 |
66 | var timezone = TimeZoneInfo.FindSystemTimeZoneById(chronTimezone);
67 | if (timezone == null)
68 | {
69 | throw new InvalidOperationException(
70 | $"Couldn't determine scheduled message timezone: '{chronTimezone}'");
71 | }
72 |
73 | var next = expression.GetNextOccurrence(_timeProvider.GetUtcNow(), timezone);
74 | if (next == null)
75 | {
76 | throw new InvalidOperationException(
77 | $"Couldn't determine next scheduled message occurrence for cron expression: '{chronExpression}', timezone: '{chronTimezone}'");
78 | }
79 |
80 | CreatedScheduledMessages.Add(new ScheduledMessage
81 | {
82 | Id = id,
83 | PayloadType = typeof(TK).Name,
84 | AvailableAfter = next.Value.ToUnixTimeSeconds(),
85 | Tag = tag,
86 | ChronExpression = chronExpression,
87 | ChronTimezone = chronTimezone,
88 | Payload = payload
89 | });
90 |
91 | return id;
92 | }
93 |
94 | ///
95 | /// Deletes scheduled messages by tag.
96 | ///
97 | /// The tag of the scheduled messages to delete.
98 | /// The cancellation token.
99 | /// A task representing the asynchronous operation.
100 | public Task DeleteByTag(string tag, CancellationToken cancellationToken = default)
101 | {
102 | DeletedScheduledMessageTags.Add(tag);
103 | return Task.CompletedTask;
104 | }
105 |
106 | ///
107 | /// Deletes a scheduled message by ID.
108 | ///
109 | /// The ID of the scheduled message to delete.
110 | /// The cancellation token.
111 | /// A task representing the asynchronous operation.
112 | public Task DeleteById(string id, CancellationToken cancellationToken = default)
113 | {
114 | DeletedScheduledMessageIds.Add(id);
115 | return Task.CompletedTask;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/AsyncMonolith.TestHelpers/SetupTestHelpers.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AsyncMonolith.Producers;
3 | using AsyncMonolith.Scheduling;
4 | using AsyncMonolith.Utilities;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace AsyncMonolith.TestHelpers;
9 |
10 | ///
11 | ///
12 | ///
13 | public static class SetupTestHelpers
14 | {
15 | ///
16 | /// Adds fake implementations of AsyncMonolith base services for testing purposes.
17 | ///
18 | /// The service collection.
19 | /// The action used to configure the settings.
20 | public static IServiceCollection AddFakeAsyncMonolithBaseServices(
21 | this IServiceCollection services,
22 | Action settings) =>
23 | AddFakeAsyncMonolithBaseServices(services, settings, AsyncMonolithSettings.Default);
24 |
25 | ///
26 | /// Adds fake implementations of AsyncMonolith base services for testing purposes.
27 | ///
28 | /// The service collection.
29 | /// The assembly containing the consumers.
30 | /// Optional AsyncMonolith settings.
31 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
32 | public static IServiceCollection AddFakeAsyncMonolithBaseServices(
33 | this IServiceCollection services,
34 | Assembly assembly,
35 | AsyncMonolithSettings? settings = null) =>
36 | AddFakeAsyncMonolithBaseServices(
37 | services,
38 | configuration => configuration.RegisterTypesFromAssembly(assembly),
39 | settings ?? AsyncMonolithSettings.Default);
40 |
41 | ///
42 | /// Adds real implementations of AsyncMonolith base services for production use.
43 | ///
44 | /// The DbContext type.
45 | /// The service collection.
46 | /// The action used to configure the settings.
47 | public static IServiceCollection AddRealAsyncMonolithBaseServices(
48 | this IServiceCollection services,
49 | Action settings) where T : DbContext =>
50 | AddRealAsyncMonolithBaseServices(services, settings, AsyncMonolithSettings.Default);
51 |
52 | ///
53 | /// Adds real implementations of AsyncMonolith base services for production use.
54 | ///
55 | /// The DbContext type.
56 | /// The service collection.
57 | /// The assembly containing the consumers.
58 | /// Optional AsyncMonolith settings.
59 | [Obsolete("This method is obsolete. Use the method that accepts an Action instead.")]
60 | public static IServiceCollection AddRealAsyncMonolithBaseServices(
61 | this IServiceCollection services,
62 | Assembly assembly,
63 | AsyncMonolithSettings? settings = null) where T : DbContext =>
64 | AddRealAsyncMonolithBaseServices(
65 | services,
66 | configuration => configuration.RegisterTypesFromAssembly(assembly),
67 | settings ?? AsyncMonolithSettings.Default);
68 |
69 | private static IServiceCollection AddFakeAsyncMonolithBaseServices(
70 | this IServiceCollection services,
71 | Action configuration,
72 | AsyncMonolithSettings settings)
73 | {
74 | configuration(settings);
75 | services.InternalConfigureAsyncMonolithSettings(settings);
76 | services.InternalRegisterAsyncMonolithConsumers(settings);
77 | services.AddSingleton(new FakeIdGenerator());
78 | services.AddScoped();
79 | services.AddScoped();
80 | return services;
81 | }
82 |
83 | private static IServiceCollection AddRealAsyncMonolithBaseServices(
84 | this IServiceCollection services,
85 | Action configuration,
86 | AsyncMonolithSettings settings) where T : DbContext
87 | {
88 | configuration(settings);
89 | services.InternalConfigureAsyncMonolithSettings(settings);
90 | services.InternalRegisterAsyncMonolithConsumers(settings);
91 | services.AddSingleton(new AsyncMonolithIdGenerator());
92 | services.AddScoped>();
93 | return services;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/AsyncMonolith.Tests/AsyncMonolith.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 | preview
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | all
30 | runtime; build; native; contentfiles; analyzers; buildtransitive
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/AsyncMonolith.Tests/ConsumerMessageFetcherTests.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Producers;
3 | using AsyncMonolith.Tests.Infra;
4 | using AsyncMonolith.Utilities;
5 | using FluentAssertions;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace AsyncMonolith.Tests;
9 |
10 | public class ConsumerMessageFetcherTests : DbTestsBase
11 | {
12 | [Theory]
13 | [InlineData(DbType.Ef)]
14 | [InlineData(DbType.MySql)]
15 | [InlineData(DbType.MsSql)]
16 | [InlineData(DbType.PostgreSql)]
17 | [InlineData(DbType.MariaDb)]
18 | public async Task Fetch_Returns_Batch_Of_Messages(DbType dbType)
19 | {
20 | var dbContainer = GetTestDbContainer(dbType);
21 |
22 | try
23 | {
24 | // Given
25 | var settings = AsyncMonolithSettings.Default;
26 | var serviceProvider = await Setup(dbContainer, settings);
27 | var dbContext = serviceProvider.GetRequiredService();
28 | var producer = serviceProvider.GetRequiredService();
29 | var fetcher = serviceProvider.GetRequiredService();
30 |
31 | var messages = new List();
32 | for (var i = 0; i < 2 * settings.ProcessorBatchSize; i++)
33 | {
34 | messages.Add(new SingleConsumerMessage
35 | {
36 | Name = "test-name"
37 | });
38 | }
39 |
40 | await producer.ProduceList(messages);
41 | await dbContext.SaveChangesAsync();
42 |
43 | // When
44 | var dbMessages = await fetcher.Fetch(dbContext.ConsumerMessages, FakeTime.GetUtcNow().ToUnixTimeSeconds(),
45 | CancellationToken.None);
46 |
47 | // Then
48 | dbMessages.Count.Should().Be(settings.ProcessorBatchSize);
49 | }
50 | finally
51 | {
52 | await dbContainer.DisposeAsync();
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/AsyncMonolith.Tests/Infra/DbTestsBase.cs:
--------------------------------------------------------------------------------
1 | using AsyncMonolith.Consumers;
2 | using AsyncMonolith.Scheduling;
3 | using AsyncMonolith.Utilities;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Time.Testing;
6 | using System.Diagnostics;
7 |
8 | namespace AsyncMonolith.Tests.Infra;
9 |
10 | public abstract class DbTestsBase
11 | {
12 | protected FakeTimeProvider FakeTime = default!;
13 | protected TestConsumerInvocations TestConsumerInvocations = default!;
14 |
15 | protected async Task Setup(TestDbContainerBase dbContainer, AsyncMonolithSettings? settings = null)
16 | {
17 | await dbContainer.InitializeAsync();
18 |
19 | var services = new ServiceCollection();
20 |
21 | dbContainer.AddDb(services);
22 |
23 | var (fakeTime, invocations) =
24 | services.AddTestServices(dbContainer.DbType, settings ?? AsyncMonolithSettings.Default);
25 | TestConsumerInvocations = invocations;
26 | FakeTime = fakeTime;
27 |
28 | services.AddSingleton>();
29 | services.AddSingleton>();
30 |
31 | var serviceProvider = services.BuildServiceProvider();
32 |
33 | using var scope = serviceProvider.CreateScope();
34 | var dbContext = scope.ServiceProvider.GetRequiredService();
35 | await dbContext.Database.EnsureCreatedAsync();
36 |
37 | await dbContext.SaveChangesAsync();
38 |
39 | return serviceProvider;
40 | }
41 |
42 | public Activity? GetActivity()
43 | {
44 | var activitySource = new ActivitySource("AsyncMonolith.Tests");
45 | var listener = new ActivityListener
46 | {
47 | ShouldListenTo = (a) => a.Name == activitySource.Name,
48 | Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded,
49 | ActivityStarted = activity => Console.WriteLine($"Activity started: {activity.DisplayName}"),
50 | ActivityStopped = activity => Console.WriteLine($"Activity stopped: {activity.DisplayName}")
51 | };
52 | ActivitySource.AddActivityListener(listener);
53 | return activitySource.StartActivity(
54 | "TestActivity",
55 | ActivityKind.Internal
56 | );
57 | }
58 | public static IEnumerable