Browse Source

Add DistEvents demo apps and migrations

Introduce a new DistEvents test/demo suite and supporting infra.

- Add AspNetCoreDapr and AzureEmulator sample apps (modules, programs, controllers, event handlers, appsettings).
- Add persistence projects for EntityFrameworkCore and MongoDB, including EF Core migrations and updated model snapshot.
- Update EfCoreRabbitMq project to support selectable DB provider (DistDemoDbProvider) and refactor RabbitMQ/Dapr event-bus configuration.
- Add shared demo utilities (scenario runner/profile/hosted service), Dapr pubsub component, docker-compose and Service Bus emulator config.
- Add comprehensive Visual Studio/.NET .gitignore and tweak .claude local permissions to allow "Bash(git show:*)".
pull/25023/head
SALİH ÖZKARA 5 months ago
parent
commit
92b2e84f2f
  1. 3
      .claude/settings.local.json
  2. 398
      test/DistEvents/.gitignore
  3. 39
      test/DistEvents/DistDemoApp.AspNetCoreDapr/DistDemoApp.AspNetCoreDapr.csproj
  4. 67
      test/DistEvents/DistDemoApp.AspNetCoreDapr/DistDemoAppAspNetCoreDaprModule.cs
  5. 12
      test/DistEvents/DistDemoApp.AspNetCoreDapr/Program.cs
  6. 23
      test/DistEvents/DistDemoApp.AspNetCoreDapr/ProviderScenarioController.cs
  7. 17
      test/DistEvents/DistDemoApp.AspNetCoreDapr/ProviderScenarioEventHandler.cs
  8. 14
      test/DistEvents/DistDemoApp.AspNetCoreDapr/appsettings.json
  9. 37
      test/DistEvents/DistDemoApp.AzureEmulator/DistDemoApp.AzureEmulator.csproj
  10. 51
      test/DistEvents/DistDemoApp.AzureEmulator/DistDemoAppAzureEmulatorModule.cs
  11. 79
      test/DistEvents/DistDemoApp.AzureEmulator/EmulatorProcessorPool.cs
  12. 64
      test/DistEvents/DistDemoApp.AzureEmulator/EmulatorPublisherPool.cs
  13. 32
      test/DistEvents/DistDemoApp.AzureEmulator/Program.cs
  14. 22
      test/DistEvents/DistDemoApp.AzureEmulator/appsettings.json
  15. 18
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj
  16. 53
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoAppEfCoreRabbitMqModule.cs
  17. 182
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20260304073807_Update-10.2.Designer.cs
  18. 233
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20260304073807_Update-10.2.cs
  19. 43
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/TodoDbContextModelSnapshot.cs
  20. 65
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/Program.cs
  21. 34
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContext.cs
  22. 29
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContextFactory.cs
  23. 2
      test/DistEvents/DistDemoApp.EfCoreRabbitMq/appsettings.json
  24. 18
      test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj
  25. 39
      test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoAppMongoDbKafkaModule.cs
  26. 65
      test/DistEvents/DistDemoApp.MongoDbKafka/Program.cs
  27. 26
      test/DistEvents/DistDemoApp.MongoDbKafka/TodoMongoDbContext.cs
  28. 2
      test/DistEvents/DistDemoApp.MongoDbKafka/appsettings.json
  29. 18
      test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj
  30. 38
      test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoAppMongoDbRebusModule.cs
  31. 53
      test/DistEvents/DistDemoApp.MongoDbRebus/Program.cs
  32. 19
      test/DistEvents/DistDemoApp.MongoDbRebus/TodoMongoDbContext.cs
  33. 2
      test/DistEvents/DistDemoApp.MongoDbRebus/appsettings.json
  34. 18
      test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoApp.Persistence.EntityFrameworkCore.csproj
  35. 12
      test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoAppEntityFrameworkCoreInfrastructureModule.cs
  36. 21
      test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoEntityFrameworkServiceCollectionExtensions.cs
  37. 34
      test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/TodoDbContext.cs
  38. 28
      test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/TodoDbContextFactory.cs
  39. 14
      test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoApp.Persistence.MongoDb.csproj
  40. 12
      test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoAppMongoDbInfrastructureModule.cs
  41. 26
      test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoMongoDbContext.cs
  42. 42
      test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoMongoServiceCollectionExtensions.cs
  43. 22
      test/DistEvents/DistDemoApp.Shared/DemoService.cs
  44. 9
      test/DistEvents/DistDemoApp.Shared/DistDemoAppHostedService.cs
  45. 4
      test/DistEvents/DistDemoApp.Shared/DistDemoAppSharedModule.cs
  46. 60
      test/DistEvents/DistDemoApp.Shared/DistEventScenarioProfile.cs
  47. 134
      test/DistEvents/DistDemoApp.Shared/DistEventScenarioRunner.cs
  48. 8
      test/DistEvents/DistDemoApp.Shared/IDistEventScenarioRunner.cs
  49. 9
      test/DistEvents/DistDemoApp.Shared/ProviderScenarioEvent.cs
  50. 4
      test/DistEvents/DistEventsDemo.slnx
  51. 12
      test/DistEvents/dapr/components/pubsub.yaml
  52. 131
      test/DistEvents/docker-compose.yml
  53. 22
      test/DistEvents/servicebus-emulator/Config.json

3
.claude/settings.local.json

@ -2,7 +2,8 @@
"permissions": {
"allow": [
"Bash(yarn nx g:*)",
"Bash(npx vitest:*)"
"Bash(npx vitest:*)",
"Bash(git show:*)"
]
}
}

398
test/DistEvents/.gitignore

@ -0,0 +1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

39
test/DistEvents/DistDemoApp.AspNetCoreDapr/DistDemoApp.AspNetCoreDapr.csproj

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>MongoDb</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<ProjectReference Include="..\DistDemoApp.Persistence.MongoDb\DistDemoApp.Persistence.MongoDb.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<ProjectReference Include="..\DistDemoApp.Persistence.EntityFrameworkCore\DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_MONGODB</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_EFCORE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

67
test/DistEvents/DistDemoApp.AspNetCoreDapr/DistDemoAppAspNetCoreDaprModule.cs

@ -0,0 +1,67 @@
using Dapr;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
using Volo.Abp.Autofac;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.Modularity;
namespace DistDemoApp;
#if DISTDEMO_USE_MONGODB
[DependsOn(
typeof(DistDemoAppMongoDbInfrastructureModule),
typeof(AbpAspNetCoreMvcDaprEventBusModule),
typeof(DistDemoAppSharedModule),
typeof(AbpAutofacModule)
)]
#else
[DependsOn(
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule),
typeof(AbpAspNetCoreMvcDaprEventBusModule),
typeof(DistDemoAppSharedModule),
typeof(AbpAutofacModule)
)]
#endif
public class DistDemoAppAspNetCoreDaprModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
#if DISTDEMO_USE_MONGODB
context.ConfigureDistDemoMongoInfrastructure();
#else
context.ConfigureDistDemoEntityFrameworkInfrastructure();
#endif
Configure<AbpDaprOptions>(options =>
{
options.HttpEndpoint = "http://localhost:3500";
});
Configure<AbpDaprEventBusOptions>(options =>
{
options.PubSubName = "pubsub";
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCloudEvents();
app.UseConfiguredEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapSubscribeHandler();
});
}
}

12
test/DistEvents/DistDemoApp.AspNetCoreDapr/Program.cs

@ -0,0 +1,12 @@
using DistDemoApp;
using Dapr;
using Volo.Abp;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac();
await builder.AddApplicationAsync<DistDemoAppAspNetCoreDaprModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync("http://localhost:8090");

23
test/DistEvents/DistDemoApp.AspNetCoreDapr/ProviderScenarioController.cs

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DistDemoApp;
[ApiController]
[Route("api/dist-demo/dapr")]
public class ProviderScenarioController : AbpController
{
private readonly IDistEventScenarioRunner _scenarioRunner;
public ProviderScenarioController(IDistEventScenarioRunner scenarioRunner)
{
_scenarioRunner = scenarioRunner;
}
[HttpGet]
public async Task<IActionResult> RunAsync()
{
await _scenarioRunner.RunAsync(DistEventScenarioProfile.DaprWeb());
return Ok(new { Status = "ScenarioCompleted", Profile = "dapr-web" });
}
}

17
test/DistEvents/DistDemoApp.AspNetCoreDapr/ProviderScenarioEventHandler.cs

@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace DistDemoApp;
public class ProviderScenarioEventHandler :
IDistributedEventHandler<ProviderScenarioEvent>,
ITransientDependency
{
public Task HandleEventAsync(ProviderScenarioEvent eventData)
{
Console.WriteLine($"Dapr ASP.NET Core handler received ProviderScenarioEvent: {eventData.Value}");
return Task.CompletedTask;
}
}

14
test/DistEvents/DistDemoApp.AspNetCoreDapr/appsettings.json

@ -0,0 +1,14 @@
{
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/DistEventsDemo?retryWrites=false"
},
"Dapr": {
"HttpEndpoint": "http://localhost:3500"
},
"DaprEventBus": {
"PubSubName": "pubsub"
},
"Redis": {
"Configuration": "127.0.0.1"
}
}

37
test/DistEvents/DistDemoApp.AzureEmulator/DistDemoApp.AzureEmulator.csproj

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>MongoDb</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.Azure\Volo.Abp.EventBus.Azure.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<ProjectReference Include="..\DistDemoApp.Persistence.MongoDb\DistDemoApp.Persistence.MongoDb.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<ProjectReference Include="..\DistDemoApp.Persistence.EntityFrameworkCore\DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_MONGODB</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_EFCORE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

51
test/DistEvents/DistDemoApp.AzureEmulator/DistDemoAppAzureEmulatorModule.cs

@ -0,0 +1,51 @@
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EventBus.Azure;
using Volo.Abp.Modularity;
using Volo.Abp.AzureServiceBus;
namespace DistDemoApp;
#if DISTDEMO_USE_MONGODB
[DependsOn(
typeof(DistDemoAppMongoDbInfrastructureModule),
typeof(AbpEventBusAzureModule),
typeof(DistDemoAppSharedModule)
)]
#else
[DependsOn(
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule),
typeof(AbpEventBusAzureModule),
typeof(DistDemoAppSharedModule)
)]
#endif
public class DistDemoAppAzureEmulatorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IPublisherPool, EmulatorPublisherPool>();
context.Services.AddSingleton<IProcessorPool, EmulatorProcessorPool>();
#if DISTDEMO_USE_MONGODB
context.ConfigureDistDemoMongoInfrastructure();
#else
context.ConfigureDistDemoEntityFrameworkInfrastructure();
#endif
Configure<AbpAzureServiceBusOptions>(options =>
{
options.Connections.Default.ConnectionString =
"Endpoint=sb://localhost:5673;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
options.Connections.Default.Processor = new ServiceBusProcessorOptions
{
AutoCompleteMessages = false
};
});
Configure<AbpAzureEventBusOptions>(options =>
{
options.ConnectionName = "Default";
options.SubscriberName = "DistDemoAzureSubscriber";
options.TopicName = "DistDemoAzureTopic";
});
}
}

79
test/DistEvents/DistDemoApp.AzureEmulator/EmulatorProcessorPool.cs

@ -0,0 +1,79 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AzureServiceBus;
using Volo.Abp.DependencyInjection;
namespace DistDemoApp;
public class EmulatorProcessorPool : IProcessorPool, ISingletonDependency
{
public ILogger<EmulatorProcessorPool> Logger { get; set; }
private bool _isDisposed;
private readonly AbpAzureServiceBusOptions _options;
private readonly IConnectionPool _connectionPool;
private readonly ConcurrentDictionary<string, Lazy<ServiceBusProcessor>> _processors;
public EmulatorProcessorPool(
IOptions<AbpAzureServiceBusOptions> options,
IConnectionPool connectionPool)
{
_options = options.Value;
_connectionPool = connectionPool;
_processors = new ConcurrentDictionary<string, Lazy<ServiceBusProcessor>>();
Logger = NullLogger<EmulatorProcessorPool>.Instance;
}
public Task<ServiceBusProcessor> GetAsync(string subscriptionName, string topicName, string connectionName)
{
var processor = _processors.GetOrAdd(
$"{topicName}-{subscriptionName}",
new Lazy<ServiceBusProcessor>(() =>
{
var config = _options.Connections.GetOrDefault(connectionName);
var client = _connectionPool.GetClient(connectionName);
return client.CreateProcessor(topicName, subscriptionName, config.Processor);
})
).Value;
return Task.FromResult(processor);
}
public async ValueTask DisposeAsync()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
if (!_processors.Any())
{
return;
}
foreach (var item in _processors.Values)
{
var processor = item.Value;
if (processor.IsProcessing)
{
await processor.StopProcessingAsync();
}
if (!processor.IsClosed)
{
await processor.CloseAsync();
}
await processor.DisposeAsync();
}
_processors.Clear();
}
}

64
test/DistEvents/DistDemoApp.AzureEmulator/EmulatorPublisherPool.cs

@ -0,0 +1,64 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AzureServiceBus;
using Volo.Abp.DependencyInjection;
#nullable enable
namespace DistDemoApp;
public class EmulatorPublisherPool : IPublisherPool, ISingletonDependency
{
public ILogger<EmulatorPublisherPool> Logger { get; set; }
private bool _isDisposed;
private readonly IConnectionPool _connectionPool;
private readonly ConcurrentDictionary<string, Lazy<ServiceBusSender>> _publishers;
public EmulatorPublisherPool(IConnectionPool connectionPool)
{
_connectionPool = connectionPool;
_publishers = new ConcurrentDictionary<string, Lazy<ServiceBusSender>>();
Logger = NullLogger<EmulatorPublisherPool>.Instance;
}
public Task<ServiceBusSender> GetAsync(string topicName, string? connectionName)
{
var sender = _publishers.GetOrAdd(
topicName,
new Lazy<ServiceBusSender>(() =>
{
var client = _connectionPool.GetClient(connectionName);
return client.CreateSender(topicName);
})
).Value;
return Task.FromResult(sender);
}
public async ValueTask DisposeAsync()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
if (!_publishers.Any())
{
return;
}
foreach (var publisher in _publishers.Values)
{
await publisher.Value.CloseAsync();
await publisher.Value.DisposeAsync();
}
_publishers.Clear();
}
}

32
test/DistEvents/DistDemoApp.AzureEmulator/Program.cs

@ -0,0 +1,32 @@
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Volo.Abp;
using Volo.Abp.Threading;
namespace DistDemoApp;
public class Program
{
public static void Main(string[] args)
{
using var application = AbpApplicationFactory.Create<DistDemoAppAzureEmulatorModule>(options =>
{
options.UseAutofac();
options.Services.AddSerilog((serviceProvider, configuration) =>
{
});
options.Services.AddLogging(c => c.AddSerilog());
});
Log.Information("Starting DistDemoApp.AzureEmulator.");
application.Initialize();
AsyncHelper.RunSync(() => application
.ServiceProvider
.GetRequiredService<IDistEventScenarioRunner>()
.RunAsync(DistEventScenarioProfile.AzureEmulator()));
application.Shutdown();
}
}

22
test/DistEvents/DistDemoApp.AzureEmulator/appsettings.json

@ -0,0 +1,22 @@
{
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/DistEventsDemo?retryWrites=false"
},
"Azure": {
"ServiceBus": {
"Connections": {
"Default": {
"ConnectionString": "Endpoint=sb://localhost:5673;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;"
}
}
},
"EventBus": {
"ConnectionName": "Default",
"SubscriberName": "DistDemoAzureSubscriber",
"TopicName": "DistDemoAzureTopic"
}
},
"Redis": {
"Configuration": "127.0.0.1"
}
}

18
test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj

@ -4,14 +4,30 @@
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>EntityFrameworkCore</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.RabbitMQ\Volo.Abp.EventBus.RabbitMQ.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<ProjectReference Include="..\DistDemoApp.Persistence.MongoDb\DistDemoApp.Persistence.MongoDb.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<ProjectReference Include="..\DistDemoApp.Persistence.EntityFrameworkCore\DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_MONGODB</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_EFCORE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>

53
test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoAppEfCoreRabbitMqModule.cs

@ -1,44 +1,59 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
namespace DistDemoApp
{
#if DISTDEMO_USE_MONGODB
[DependsOn(
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(DistDemoAppMongoDbInfrastructureModule),
typeof(AbpEventBusRabbitMqModule),
typeof(DistDemoAppSharedModule)
)]
#else
[DependsOn(
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule),
typeof(AbpEventBusRabbitMqModule),
typeof(DistDemoAppSharedModule)
)]
#endif
public class DistDemoAppEfCoreRabbitMqModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<TodoDbContext>(options =>
#if DISTDEMO_USE_MONGODB
context.ConfigureDistDemoMongoInfrastructure();
#else
context.ConfigureDistDemoEntityFrameworkInfrastructure();
#endif
Configure<AbpDistributedEventBusOptions>(options =>
{
options.AddDefaultRepositories();
// options.Outboxes.Configure(config =>
// {
// config.UseDbContext<TodoDbContext>();
// });
//
// options.Inboxes.Configure(config =>
// {
// config.UseDbContext<TodoDbContext>();
// });
});
Configure<AbpDbContextOptions>(options =>
Configure<AbpRabbitMqOptions>(options =>
{
options.UseSqlServer();
options.Connections.Default.HostName = "localhost";
});
Configure<AbpDistributedEventBusOptions>(options =>
Configure<AbpRabbitMqEventBusOptions>(options =>
{
options.Outboxes.Configure(config =>
{
config.UseDbContext<TodoDbContext>();
});
options.Inboxes.Configure(config =>
{
config.UseDbContext<TodoDbContext>();
});
options.ConnectionName = "Default";
options.ClientName = "DistDemoApp";
options.ExchangeName = "DistDemo";
});
}
}
}

182
test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20260304073807_Update-10.2.Designer.cs

@ -0,0 +1,182 @@
// <auto-generated />
using System;
using DistDemoApp;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace DistDemoApp.Migrations
{
[DbContext(typeof(TodoDbContext))]
[Migration("20260304073807_Update-10.2")]
partial class Update102
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "10.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("DistDemoApp.TodoItem", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Text")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.ToTable("TodoItems");
});
modelBuilder.Entity("DistDemoApp.TodoSummary", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<byte>("Day")
.HasColumnType("tinyint");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<byte>("Month")
.HasColumnType("tinyint");
b.Property<int>("TotalCount")
.HasColumnType("int");
b.Property<int>("Year")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("TodoSummaries");
});
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.IncomingEventRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<byte[]>("EventData")
.IsRequired()
.HasColumnType("varbinary(max)");
b.Property<string>("EventName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<DateTime?>("HandledTime")
.HasColumnType("datetime2");
b.Property<string>("MessageId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<DateTime?>("NextRetryTime")
.HasColumnType("datetime2");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("MessageId");
b.HasIndex("Status", "CreationTime");
b.ToTable("AbpEventInbox", (string)null);
});
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<byte[]>("EventData")
.IsRequired()
.HasColumnType("varbinary(max)");
b.Property<string>("EventName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.HasKey("Id");
b.HasIndex("CreationTime");
b.ToTable("AbpEventOutbox", (string)null);
});
#pragma warning restore 612, 618
}
}
}

233
test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20260304073807_Update-10.2.cs

@ -0,0 +1,233 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DistDemoApp.Migrations
{
/// <inheritdoc />
public partial class Update102 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AbpEventInbox_Processed_CreationTime",
table: "AbpEventInbox");
migrationBuilder.DropColumn(
name: "Processed",
table: "AbpEventInbox");
migrationBuilder.RenameColumn(
name: "ProcessedTime",
table: "AbpEventInbox",
newName: "NextRetryTime");
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "TodoSummaries",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "TodoSummaries",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "TodoItems",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "TodoItems",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpEventOutbox",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "MessageId",
table: "AbpEventInbox",
type: "nvarchar(450)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(450)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpEventInbox",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "HandledTime",
table: "AbpEventInbox",
type: "datetime2",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "RetryCount",
table: "AbpEventInbox",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "Status",
table: "AbpEventInbox",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_AbpEventOutbox_CreationTime",
table: "AbpEventOutbox",
column: "CreationTime");
migrationBuilder.CreateIndex(
name: "IX_AbpEventInbox_Status_CreationTime",
table: "AbpEventInbox",
columns: new[] { "Status", "CreationTime" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AbpEventOutbox_CreationTime",
table: "AbpEventOutbox");
migrationBuilder.DropIndex(
name: "IX_AbpEventInbox_Status_CreationTime",
table: "AbpEventInbox");
migrationBuilder.DropColumn(
name: "HandledTime",
table: "AbpEventInbox");
migrationBuilder.DropColumn(
name: "RetryCount",
table: "AbpEventInbox");
migrationBuilder.DropColumn(
name: "Status",
table: "AbpEventInbox");
migrationBuilder.RenameColumn(
name: "NextRetryTime",
table: "AbpEventInbox",
newName: "ProcessedTime");
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "TodoSummaries",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "TodoSummaries",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "TodoItems",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "TodoItems",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpEventOutbox",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "MessageId",
table: "AbpEventInbox",
type: "nvarchar(450)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(450)");
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpEventInbox",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AddColumn<bool>(
name: "Processed",
table: "AbpEventInbox",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.CreateIndex(
name: "IX_AbpEventInbox_Processed_CreationTime",
table: "AbpEventInbox",
columns: new[] { "Processed", "CreationTime" });
}
}
}

43
test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/TodoDbContextModelSnapshot.cs

@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace DistDemoApp.Migrations
{
[DbContext(typeof(TodoDbContext))]
@ -17,9 +19,10 @@ namespace DistDemoApp.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "5.0.9")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
.HasAnnotation("ProductVersion", "10.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("DistDemoApp.TodoItem", b =>
{
@ -28,6 +31,7 @@ namespace DistDemoApp.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -41,6 +45,7 @@ namespace DistDemoApp.Migrations
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -58,11 +63,13 @@ namespace DistDemoApp.Migrations
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -71,6 +78,7 @@ namespace DistDemoApp.Migrations
.HasColumnType("tinyint");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -107,25 +115,33 @@ namespace DistDemoApp.Migrations
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<DateTime?>("HandledTime")
.HasColumnType("datetime2");
b.Property<string>("MessageId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<bool>("Processed")
.HasColumnType("bit");
b.Property<DateTime?>("ProcessedTime")
b.Property<DateTime?>("NextRetryTime")
.HasColumnType("datetime2");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("MessageId");
b.HasIndex("Processed", "CreationTime");
b.HasIndex("Status", "CreationTime");
b.ToTable("AbpEventInbox");
b.ToTable("AbpEventInbox", (string)null);
});
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b =>
@ -147,12 +163,15 @@ namespace DistDemoApp.Migrations
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.HasKey("Id");
b.ToTable("AbpEventOutbox");
b.HasIndex("CreationTime");
b.ToTable("AbpEventOutbox", (string)null);
});
#pragma warning restore 612, 618
}

65
test/DistEvents/DistDemoApp.EfCoreRabbitMq/Program.cs

@ -1,57 +1,34 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Volo.Abp;
using Volo.Abp.Threading;
namespace DistDemoApp
{
public class Program
{
public static async Task<int> Main(string[] args)
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
try
{
Log.Information("Starting console host.");
await CreateHostBuilder(args).RunConsoleAsync();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
using (var application = AbpApplicationFactory.Create<DistDemoAppEfCoreRabbitMqModule>(options =>
{
options.UseAutofac();
options.Services.AddSerilog((_, _) =>
{
});
options.Services.AddLogging(c => c.AddSerilog());
}))
{
Log.CloseAndFlush();
}
Log.Information("Starting DistDemoApp.EfCoreRabbitMq.");
application.Initialize();
}
AsyncHelper.RunSync(
() => application
.ServiceProvider
.GetRequiredService<DemoService>().CreateTodoItemAsync()
);
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureAppConfiguration((context, config) =>
{
//setup your additional configuration sources
})
.ConfigureServices((hostContext, services) =>
{
services.AddApplication<DistDemoAppEfCoreRabbitMqModule>();
});
application.Shutdown();
}
}
}
}

34
test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContext.cs

@ -1,34 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
namespace DistDemoApp
{
public class TodoDbContext : AbpDbContext<TodoDbContext>, IHasEventOutbox, IHasEventInbox
{
public DbSet<TodoItem> TodoItems { get; set; }
public DbSet<TodoSummary> TodoSummaries { get; set; }
public DbSet<OutgoingEventRecord> OutgoingEvents { get; set; }
public DbSet<IncomingEventRecord> IncomingEvents { get; set; }
public TodoDbContext(DbContextOptions<TodoDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureEventOutbox();
modelBuilder.ConfigureEventInbox();
modelBuilder.Entity<TodoItem>(b =>
{
b.Property(x => x.Text).IsRequired().HasMaxLength(128);
});
}
}
}

29
test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContextFactory.cs

@ -1,29 +0,0 @@
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace DistDemoApp
{
public class TodoDbContextFactory : IDesignTimeDbContextFactory<TodoDbContext>
{
public TodoDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<TodoDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
return new TodoDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}

2
test/DistEvents/DistDemoApp.EfCoreRabbitMq/appsettings.json

@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=DistEventsDemo;Trusted_Connection=True;TrustServerCertificate=True"
"Default": "Server=localhost,1433;Database=DistEventsDemo;User Id=sa;Password=AbpDemo_123456;TrustServerCertificate=True"
},
"RabbitMQ": {
"Connections": {

18
test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj

@ -4,14 +4,30 @@
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>MongoDb</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" />
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.Kafka\Volo.Abp.EventBus.Kafka.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<ProjectReference Include="..\DistDemoApp.Persistence.MongoDb\DistDemoApp.Persistence.MongoDb.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<ProjectReference Include="..\DistDemoApp.Persistence.EntityFrameworkCore\DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_MONGODB</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_EFCORE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

39
test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoAppMongoDbKafkaModule.cs

@ -1,37 +1,42 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Kafka;
using Volo.Abp.Kafka;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp
{
#if DISTDEMO_USE_MONGODB
[DependsOn(
typeof(AbpMongoDbModule),
typeof(DistDemoAppMongoDbInfrastructureModule),
typeof(AbpEventBusKafkaModule),
typeof(DistDemoAppSharedModule)
)]
#else
[DependsOn(
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule),
typeof(AbpEventBusKafkaModule),
typeof(DistDemoAppSharedModule)
)]
#endif
public class DistDemoAppMongoDbKafkaModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<TodoMongoDbContext>(options =>
#if DISTDEMO_USE_MONGODB
context.ConfigureDistDemoMongoInfrastructure();
#else
context.ConfigureDistDemoEntityFrameworkInfrastructure();
#endif
Configure<AbpKafkaOptions>(options =>
{
options.AddDefaultRepositories();
options.Connections.Default.BootstrapServers = "localhost:9092";
});
Configure<AbpDistributedEventBusOptions>(options =>
Configure<AbpKafkaEventBusOptions>(options =>
{
options.Outboxes.Configure(config =>
{
config.UseMongoDbContext<TodoMongoDbContext>();
});
options.Inboxes.Configure(config =>
{
config.UseMongoDbContext<TodoMongoDbContext>();
});
options.ConnectionName = "Default";
options.TopicName = "DistDemoTopic";
options.GroupId = "DistDemoApp";
});
}
}

65
test/DistEvents/DistDemoApp.MongoDbKafka/Program.cs

@ -1,57 +1,36 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Volo.Abp;
using Volo.Abp.Threading;
namespace DistDemoApp
{
public class Program
{
public static async Task<int> Main(string[] args)
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
try
{
Log.Information("Starting console host.");
await CreateHostBuilder(args).RunConsoleAsync();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
using (var application = AbpApplicationFactory.Create<DistDemoAppMongoDbKafkaModule>(options =>
{
options.UseAutofac();
options.Services.AddSerilog((_, _) =>
{
});
options.Services.AddLogging(c => c.AddSerilog());
}))
{
Log.CloseAndFlush();
Log.Information("Starting DistDemoApp.MongoDbKafka.");
application.Initialize();
AsyncHelper.RunSync(
() => application
.ServiceProvider
.GetRequiredService<DemoService>().CreateTodoItemAsync()
);
application.Shutdown();
}
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureAppConfiguration((context, config) =>
{
//setup your additional configuration sources
})
.ConfigureServices((hostContext, services) =>
{
services.AddApplication<DistDemoAppMongoDbKafkaModule>();
});
}
}

26
test/DistEvents/DistDemoApp.MongoDbKafka/TodoMongoDbContext.cs

@ -1,26 +0,0 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp
{
[ConnectionStringName("Default")]
public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox
{
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>();
public IMongoCollection<TodoSummary> TodoSummaries => Collection<TodoSummary>();
public IMongoCollection<OutgoingEventRecord> OutgoingEvents
{
get => Collection<OutgoingEventRecord>();
set {}
}
public IMongoCollection<IncomingEventRecord> IncomingEvents
{
get => Collection<IncomingEventRecord>();
set {}
}
}
}

2
test/DistEvents/DistDemoApp.MongoDbKafka/appsettings.json

@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo"
"Default": "mongodb://localhost:27017/DistEventsDemo?retryWrites=false"
},
"Kafka": {
"Connections": {

18
test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj

@ -4,14 +4,30 @@
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>MongoDb</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" />
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.Rebus\Volo.Abp.EventBus.Rebus.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<ProjectReference Include="..\DistDemoApp.Persistence.MongoDb\DistDemoApp.Persistence.MongoDb.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<ProjectReference Include="..\DistDemoApp.Persistence.EntityFrameworkCore\DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'MongoDb'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_MONGODB</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(DistDemoDbProvider)' == 'EntityFrameworkCore'">
<DefineConstants>$(DefineConstants);DISTDEMO_USE_EFCORE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

38
test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoAppMongoDbRebusModule.cs

@ -1,19 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
using Rebus.Persistence.InMem;
using Rebus.Transport.InMem;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Rebus;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp
{
#if DISTDEMO_USE_MONGODB
[DependsOn(
typeof(AbpMongoDbModule),
typeof(DistDemoAppMongoDbInfrastructureModule),
typeof(AbpEventBusRebusModule),
typeof(DistDemoAppSharedModule)
)]
#else
[DependsOn(
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule),
typeof(AbpEventBusRebusModule),
typeof(DistDemoAppSharedModule)
)]
#endif
public class DistDemoAppMongoDbRebusModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
@ -24,30 +27,17 @@ namespace DistDemoApp
options.Configurer = rebusConfigurer =>
{
rebusConfigurer.Transport(t => t.UseInMemoryTransport(new InMemNetwork(), "eventbus"));
rebusConfigurer.Subscriptions(s => s.StoreInMemory());
};
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<TodoMongoDbContext>(options =>
{
options.AddDefaultRepositories();
});
Configure<AbpDistributedEventBusOptions>(options =>
{
options.Outboxes.Configure(config =>
{
config.UseMongoDbContext<TodoMongoDbContext>();
});
options.Inboxes.Configure(config =>
{
config.UseMongoDbContext<TodoMongoDbContext>();
});
});
#if DISTDEMO_USE_MONGODB
context.ConfigureDistDemoMongoInfrastructure();
#else
context.ConfigureDistDemoEntityFrameworkInfrastructure();
#endif
}
}
}

53
test/DistEvents/DistDemoApp.MongoDbRebus/Program.cs

@ -1,9 +1,5 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Volo.Abp;
using Volo.Abp.Threading;
@ -13,48 +9,16 @@ namespace DistDemoApp
{
public static void Main(string[] args)
{
// Log.Logger = new LoggerConfiguration()
// #if DEBUG
// .MinimumLevel.Debug()
// #else
// .MinimumLevel.Information()
// #endif
// .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
// .Enrich.FromLogContext()
// .WriteTo.Async(c => c.File("Logs/logs.txt"))
// .WriteTo.Async(c => c.Console())
// .CreateLogger();
//
// try
// {
// Log.Information("Starting console host.");
// await CreateHostBuilder(args).RunConsoleAsync();
// return 0;
// }
// catch (Exception ex)
// {
// Log.Fatal(ex, "Host terminated unexpectedly!");
// return 1;
// }
// finally
// {
// Log.CloseAndFlush();
// }
using (var application = AbpApplicationFactory.Create<DistDemoAppMongoDbRebusModule>(options =>
{
options.UseAutofac();
options.Services.AddSerilog((serviceProvider, c) =>
options.Services.AddSerilog((_, _) =>
{
// c.Enrich.FromLogContext()
// .WriteTo.Async(c => c.File("Logs/logs.txt"))
// .WriteTo.Async(c => c.Console())
// .WriteTo.AbpStudio(serviceProvider);
});
options.Services.AddLogging(c => c.AddSerilog());
}))
{
Log.Information("Starting Volo.AbpIo.DbMigrator.");
Log.Information("Starting DistDemoApp.MongoDbRebus.");
application.Initialize();
@ -68,18 +32,5 @@ namespace DistDemoApp
}
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureAppConfiguration((context, config) =>
{
//setup your additional configuration sources
})
.ConfigureServices((hostContext, services) =>
{
services.AddApplication<DistDemoAppMongoDbRebusModule>();
});
}
}

19
test/DistEvents/DistDemoApp.MongoDbRebus/TodoMongoDbContext.cs

@ -1,19 +0,0 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp
{
[ConnectionStringName("Default")]
public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox
{
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>();
public IMongoCollection<TodoSummary> TodoSummaries => Collection<TodoSummary>();
public IMongoCollection<OutgoingEventRecord> OutgoingEvents => Collection<OutgoingEventRecord>();
public IMongoCollection<IncomingEventRecord> IncomingEvents => Collection<IncomingEventRecord>();
}
}

2
test/DistEvents/DistDemoApp.MongoDbRebus/appsettings.json

@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo"
"Default": "mongodb://localhost:27017/DistEventsDemo?retryWrites=false"
},
"Kafka": {
"Connections": {

18
test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoApp.Persistence.EntityFrameworkCore.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>EntityFrameworkCore</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
</ItemGroup>
</Project>

12
test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoAppEntityFrameworkCoreInfrastructureModule.cs

@ -0,0 +1,12 @@
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Modularity;
namespace DistDemoApp;
[DependsOn(
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(DistDemoAppSharedModule)
)]
public class DistDemoAppEntityFrameworkCoreInfrastructureModule : AbpModule
{
}

21
test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/DistDemoEntityFrameworkServiceCollectionExtensions.cs

@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace DistDemoApp;
public static class DistDemoEntityFrameworkServiceCollectionExtensions
{
public static void ConfigureDistDemoEntityFrameworkInfrastructure(this ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<TodoDbContext>(options =>
{
options.AddDefaultRepositories();
});
context.Services.Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
}
}

34
test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/TodoDbContext.cs

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
namespace DistDemoApp;
public class TodoDbContext : AbpDbContext<TodoDbContext>, IHasEventOutbox, IHasEventInbox
{
public DbSet<TodoItem> TodoItems { get; set; } = null!;
public DbSet<TodoSummary> TodoSummaries { get; set; } = null!;
public DbSet<OutgoingEventRecord> OutgoingEvents { get; set; } = null!;
public DbSet<IncomingEventRecord> IncomingEvents { get; set; } = null!;
public TodoDbContext(DbContextOptions<TodoDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureEventOutbox();
modelBuilder.ConfigureEventInbox();
modelBuilder.Entity<TodoItem>(b =>
{
b.Property(x => x.Text).IsRequired().HasMaxLength(128);
});
}
}

28
test/DistEvents/DistDemoApp.Persistence.EntityFrameworkCore/TodoDbContextFactory.cs

@ -0,0 +1,28 @@
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace DistDemoApp;
public class TodoDbContextFactory : IDesignTimeDbContextFactory<TodoDbContext>
{
public TodoDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<TodoDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
return new TodoDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}

14
test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoApp.Persistence.MongoDb.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>DistDemoApp</RootNamespace>
<DistDemoDbProvider>MongoDb</DistDemoDbProvider>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" />
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" />
</ItemGroup>
</Project>

12
test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoAppMongoDbInfrastructureModule.cs

@ -0,0 +1,12 @@
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
namespace DistDemoApp;
[DependsOn(
typeof(AbpMongoDbModule),
typeof(DistDemoAppSharedModule)
)]
public class DistDemoAppMongoDbInfrastructureModule : AbpModule
{
}

26
test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoMongoDbContext.cs

@ -0,0 +1,26 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp;
[ConnectionStringName("Default")]
public class DistDemoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox
{
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>();
public IMongoCollection<TodoSummary> TodoSummaries => Collection<TodoSummary>();
public IMongoCollection<OutgoingEventRecord> OutgoingEvents
{
get => Collection<OutgoingEventRecord>();
set { }
}
public IMongoCollection<IncomingEventRecord> IncomingEvents
{
get => Collection<IncomingEventRecord>();
set { }
}
}

42
test/DistEvents/DistDemoApp.Persistence.MongoDb/DistDemoMongoServiceCollectionExtensions.cs

@ -0,0 +1,42 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace DistDemoApp;
public static class DistDemoMongoServiceCollectionExtensions
{
private const string DefaultMongoConnectionString = "mongodb://localhost:27017/DistEventsDemo?retryWrites=false";
public static void ConfigureDistDemoMongoInfrastructure(this ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<DistDemoMongoDbContext>(options =>
{
options.AddDefaultRepositories();
});
context.Services.Configure<AbpDbConnectionOptions>(options =>
{
if (string.IsNullOrWhiteSpace(options.ConnectionStrings.Default))
{
options.ConnectionStrings.Default = DefaultMongoConnectionString;
}
});
context.Services.Configure<AbpDistributedEventBusOptions>(options =>
{
options.Outboxes.Configure(config =>
{
config.UseMongoDbContext<DistDemoMongoDbContext>();
});
options.Inboxes.Configure(config =>
{
config.UseMongoDbContext<DistDemoMongoDbContext>();
});
});
}
}

22
test/DistEvents/DistDemoApp.Shared/DemoService.cs

@ -1,29 +1,21 @@
using System;
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace DistDemoApp
{
public class DemoService : ITransientDependency
{
private readonly IRepository<TodoItem, Guid> _todoItemRepository;
private readonly IDistEventScenarioRunner _scenarioRunner;
public DemoService(IRepository<TodoItem, Guid> todoItemRepository)
public DemoService(IDistEventScenarioRunner scenarioRunner)
{
_todoItemRepository = todoItemRepository;
_scenarioRunner = scenarioRunner;
}
public async Task CreateTodoItemAsync()
public virtual async Task CreateTodoItemAsync()
{
var todoItem = await _todoItemRepository.InsertAsync(
new TodoItem
{
Text = "todo item " + DateTime.Now.Ticks
}
);
Console.WriteLine("Created a new todo item: " + todoItem);
await _scenarioRunner.RunAsync(DistEventScenarioProfile.Default());
}
}
}

9
test/DistEvents/DistDemoApp.Shared/DistDemoAppHostedService.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
@ -11,21 +11,22 @@ namespace DistDemoApp
private readonly IAbpApplicationWithExternalServiceProvider _application;
private readonly IServiceProvider _serviceProvider;
private readonly DemoService _demoService;
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public DistDemoAppHostedService(
IAbpApplicationWithExternalServiceProvider application,
IServiceProvider serviceProvider,
DemoService demoService)
DemoService demoService,
IHostApplicationLifetime hostApplicationLifetime)
{
_application = application;
_serviceProvider = serviceProvider;
_demoService = demoService;
_hostApplicationLifetime = hostApplicationLifetime;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_application.Initialize(_serviceProvider);
await _demoService.CreateTodoItemAsync();
}

4
test/DistEvents/DistDemoApp.Shared/DistDemoAppSharedModule.cs

@ -1,4 +1,4 @@
using Medallion.Threading;
using Medallion.Threading;
using Medallion.Threading.Redis;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
@ -21,7 +21,7 @@ namespace DistDemoApp
{
var configuration = context.Services.GetConfiguration();
context.Services.AddHostedService<DistDemoAppHostedService>();
// context.Services.AddHostedService<DistDemoAppHostedService>();
Configure<AbpDistributedEntityEventOptions>(options =>
{

60
test/DistEvents/DistDemoApp.Shared/DistEventScenarioProfile.cs

@ -0,0 +1,60 @@
namespace DistDemoApp;
public class DistEventScenarioProfile
{
public string Name { get; set; } = "default";
public string AnonymousOnlyEventName { get; set; } = "dist-demo.anonymous-only";
public string AnonymousOnlyMessage { get; set; } = "hello-anonymous";
public int TypedFromTypedValue { get; set; } = 7;
public int TypedFromAnonymousValue { get; set; } = 11;
public bool EnableTypedFromTypedScenario { get; set; } = true;
public bool EnableTypedFromAnonymousScenario { get; set; } = true;
public bool EnableAnonymousOnlyScenario { get; set; } = true;
public bool OnUnitOfWorkComplete { get; set; } = true;
public bool UseOutbox { get; set; } = true;
public bool UseUnitOfWork { get; set; } = true;
public int WarmupDelayMs { get; set; } = 1500;
public int TimeoutSeconds { get; set; } = 60;
public static DistEventScenarioProfile Default()
{
return new DistEventScenarioProfile();
}
public static DistEventScenarioProfile DaprWeb()
{
return new DistEventScenarioProfile
{
Name = "dapr-web",
AnonymousOnlyEventName = "dist-demo.dapr.anonymous-only",
AnonymousOnlyMessage = "hello-dapr-web",
EnableTypedFromTypedScenario = false,
EnableTypedFromAnonymousScenario = false,
EnableAnonymousOnlyScenario = false
};
}
public static DistEventScenarioProfile AzureEmulator()
{
return new DistEventScenarioProfile
{
Name = "azure-emulator",
AnonymousOnlyEventName = "DistDemoApp.Azure.AnonymousOnly",
AnonymousOnlyMessage = "hello-azure-emulator",
TypedFromTypedValue = 21,
TypedFromAnonymousValue = 34
};
}
}

134
test/DistEvents/DistDemoApp.Shared/DistEventScenarioRunner.cs

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
namespace DistDemoApp;
public class DistEventScenarioRunner : IDistEventScenarioRunner, ITransientDependency
{
private readonly IDistributedEventBus _distributedEventBus;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public DistEventScenarioRunner(
IDistributedEventBus distributedEventBus,
IUnitOfWorkManager unitOfWorkManager)
{
_distributedEventBus = distributedEventBus;
_unitOfWorkManager = unitOfWorkManager;
}
public async Task RunAsync(DistEventScenarioProfile profile)
{
var typedEventName = EventNameAttribute.GetNameOrDefault<ProviderScenarioEvent>();
var typedFromTypedPublish = profile.EnableTypedFromTypedScenario
? new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously)
: null;
var typedFromAnonymousPublish = profile.EnableTypedFromAnonymousScenario
? new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously)
: null;
var anonymousOnlyPublish = profile.EnableAnonymousOnlyScenario
? new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously)
: null;
using var typedSubscription = _distributedEventBus.Subscribe<ProviderScenarioEvent>(eventData =>
{
if (typedFromTypedPublish != null && eventData.Value == profile.TypedFromTypedValue)
{
typedFromTypedPublish.TrySetResult(eventData.Value);
}
if (typedFromAnonymousPublish != null && eventData.Value == profile.TypedFromAnonymousValue)
{
typedFromAnonymousPublish.TrySetResult(eventData.Value);
}
return Task.CompletedTask;
});
IDisposable? anonymousOnlySubscription = null;
if (profile.EnableAnonymousOnlyScenario)
{
anonymousOnlySubscription = _distributedEventBus.Subscribe(
profile.AnonymousOnlyEventName,
new SingleInstanceHandlerFactory(
new ActionEventHandler<AnonymousEventData>(eventData =>
{
var converted = eventData.ConvertToTypedObject();
if (converted is Dictionary<string, object> payload &&
payload.TryGetValue("Message", out var message) &&
message?.ToString() == profile.AnonymousOnlyMessage)
{
anonymousOnlyPublish!.TrySetResult(true);
}
return Task.CompletedTask;
})));
}
await Task.Delay(profile.WarmupDelayMs);
if (profile.UseUnitOfWork)
{
using var uow = _unitOfWorkManager.Begin();
await PublishScenarioEventsAsync(profile, typedEventName);
await uow.CompleteAsync();
}
else
{
await PublishScenarioEventsAsync(profile, typedEventName);
}
if (typedFromTypedPublish != null)
{
await typedFromTypedPublish.Task.WaitAsync(TimeSpan.FromSeconds(profile.TimeoutSeconds));
}
if (typedFromAnonymousPublish != null)
{
await typedFromAnonymousPublish.Task.WaitAsync(TimeSpan.FromSeconds(profile.TimeoutSeconds));
}
if (anonymousOnlyPublish != null)
{
await anonymousOnlyPublish.Task.WaitAsync(TimeSpan.FromSeconds(profile.TimeoutSeconds));
}
anonymousOnlySubscription?.Dispose();
Console.WriteLine($"All distributed event scenarios passed ({profile.Name}).");
}
private async Task PublishScenarioEventsAsync(DistEventScenarioProfile profile, string typedEventName)
{
if (profile.EnableTypedFromTypedScenario)
{
await _distributedEventBus.PublishAsync(
new ProviderScenarioEvent { Value = profile.TypedFromTypedValue },
onUnitOfWorkComplete: profile.OnUnitOfWorkComplete,
useOutbox: profile.UseOutbox);
}
if (profile.EnableTypedFromAnonymousScenario)
{
await _distributedEventBus.PublishAsync(
typedEventName,
new { Value = profile.TypedFromAnonymousValue },
onUnitOfWorkComplete: profile.OnUnitOfWorkComplete,
useOutbox: profile.UseOutbox);
}
if (profile.EnableAnonymousOnlyScenario)
{
await _distributedEventBus.PublishAsync(
profile.AnonymousOnlyEventName,
new { Message = profile.AnonymousOnlyMessage },
onUnitOfWorkComplete: profile.OnUnitOfWorkComplete,
useOutbox: profile.UseOutbox);
}
}
}

8
test/DistEvents/DistDemoApp.Shared/IDistEventScenarioRunner.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace DistDemoApp;
public interface IDistEventScenarioRunner
{
Task RunAsync(DistEventScenarioProfile profile);
}

9
test/DistEvents/DistDemoApp.Shared/ProviderScenarioEvent.cs

@ -0,0 +1,9 @@
using Volo.Abp.EventBus;
namespace DistDemoApp;
[EventName("DistDemoApp.ProviderScenarioEvent")]
public class ProviderScenarioEvent
{
public int Value { get; set; }
}

4
test/DistEvents/DistEventsDemo.slnx

@ -1,5 +1,9 @@
<Solution>
<Project Path="DistDemoApp.AzureEmulator/DistDemoApp.AzureEmulator.csproj" />
<Project Path="DistDemoApp.AspNetCoreDapr/DistDemoApp.AspNetCoreDapr.csproj" />
<Project Path="DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj" />
<Project Path="DistDemoApp.Persistence.EntityFrameworkCore/DistDemoApp.Persistence.EntityFrameworkCore.csproj" />
<Project Path="DistDemoApp.Persistence.MongoDb/DistDemoApp.Persistence.MongoDb.csproj" />
<Project Path="DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj" />
<Project Path="DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj" />
<Project Path="DistDemoApp.Shared/DistDemoApp.Shared.csproj" />

12
test/DistEvents/dapr/components/pubsub.yaml

@ -0,0 +1,12 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: redis:6379
- name: redisPassword
value: ""

131
test/DistEvents/docker-compose.yml

@ -0,0 +1,131 @@
services:
rabbitmq:
image: rabbitmq:3.13-management
container_name: distevents-rabbitmq
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
zookeeper:
image: confluentinc/cp-zookeeper:7.6.0
container_name: distevents-zookeeper
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: "2181"
ZOOKEEPER_TICK_TIME: "2000"
kafka:
image: confluentinc/cp-kafka:7.6.0
container_name: distevents-kafka
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: "1"
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
mongodb:
image: mongo:7
container_name: distevents-mongodb
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
ports:
- "27017:27017"
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"]
interval: 5s
timeout: 3s
retries: 20
mongodb-rs-init:
image: mongo:7
container_name: distevents-mongodb-rs-init
depends_on:
mongodb:
condition: service_healthy
restart: "no"
command:
[
"mongosh",
"--host", "mongodb:27017",
"--quiet",
"--eval",
"try { rs.status() } catch (e) { rs.initiate({_id:'rs0',members:[{_id:0,host:'mongodb:27017'}]}) }"
]
redis:
image: redis:7-alpine
container_name: distevents-redis
ports:
- "6379:6379"
dapr-placement:
image: daprio/dapr:1.14.4
container_name: distevents-dapr-placement
command: ["./placement", "-port", "50006"]
ports:
- "50006:50006"
daprd:
image: daprio/dapr:1.14.4
container_name: distevents-daprd
depends_on:
- redis
- dapr-placement
command:
[
"./daprd",
"--app-id", "dist-demo-dapr",
"--app-port", "8090",
"--app-channel-address", "host.docker.internal",
"--app-protocol", "http",
"--resources-path", "/components",
"--placement-host-address", "dapr-placement:50006",
"--dapr-http-port", "3500",
"--dapr-grpc-port", "50001"
]
volumes:
- ./dapr/components:/components
ports:
- "3500:3500"
- "50001:50001"
servicebus-sql:
image: mcr.microsoft.com/azure-sql-edge:latest
container_name: distevents-servicebus-sql
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "AbpDemo_123456"
servicebus-emulator:
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
container_name: distevents-servicebus-emulator
depends_on:
- servicebus-sql
environment:
SQL_SERVER: servicebus-sql
MSSQL_SA_PASSWORD: "AbpDemo_123456"
ACCEPT_EULA: "Y"
volumes:
- ./servicebus-emulator/Config.json:/ServiceBus_Emulator/ConfigFiles/Config.json
ports:
- "5673:5672"
- "5300:5300"
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: distevents-sqlserver
ports:
- "1433:1433"
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "AbpDemo_123456"

22
test/DistEvents/servicebus-emulator/Config.json

@ -0,0 +1,22 @@
{
"UserConfig": {
"Namespaces": [
{
"Name": "sbemulatorns",
"Topics": [
{
"Name": "DistDemoAzureTopic",
"Subscriptions": [
{
"Name": "DistDemoAzureSubscriber"
}
]
}
]
}
],
"Logging": {
"Type": "File"
}
}
}
Loading…
Cancel
Save