mirror of https://github.com/abpframework/abp.git
Browse Source
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
53 changed files with 2073 additions and 346 deletions
@ -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 |
||||
@ -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> |
||||
@ -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(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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"); |
||||
@ -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" }); |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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" |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -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"; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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" |
||||
|
} |
||||
|
} |
||||
@ -1,44 +1,59 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
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.Distributed; |
||||
using Volo.Abp.EventBus.RabbitMq; |
using Volo.Abp.EventBus.RabbitMq; |
||||
using Volo.Abp.Modularity; |
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.RabbitMQ; |
||||
|
|
||||
namespace DistDemoApp |
namespace DistDemoApp |
||||
{ |
{ |
||||
|
#if DISTDEMO_USE_MONGODB
|
||||
[DependsOn( |
[DependsOn( |
||||
typeof(AbpEntityFrameworkCoreSqlServerModule), |
typeof(DistDemoAppMongoDbInfrastructureModule), |
||||
typeof(AbpEventBusRabbitMqModule), |
typeof(AbpEventBusRabbitMqModule), |
||||
typeof(DistDemoAppSharedModule) |
typeof(DistDemoAppSharedModule) |
||||
)] |
)] |
||||
|
#else
|
||||
|
[DependsOn( |
||||
|
typeof(DistDemoAppEntityFrameworkCoreInfrastructureModule), |
||||
|
typeof(AbpEventBusRabbitMqModule), |
||||
|
typeof(DistDemoAppSharedModule) |
||||
|
)] |
||||
|
#endif
|
||||
public class DistDemoAppEfCoreRabbitMqModule : AbpModule |
public class DistDemoAppEfCoreRabbitMqModule : AbpModule |
||||
{ |
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
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 => |
options.ConnectionName = "Default"; |
||||
{ |
options.ClientName = "DistDemoApp"; |
||||
config.UseDbContext<TodoDbContext>(); |
options.ExchangeName = "DistDemo"; |
||||
}); |
|
||||
|
|
||||
options.Inboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseDbContext<TodoDbContext>(); |
|
||||
}); |
|
||||
}); |
}); |
||||
|
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -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
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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" }); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,57 +1,34 @@ |
|||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Hosting; |
|
||||
using Serilog; |
using Serilog; |
||||
using Serilog.Events; |
using Volo.Abp; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
namespace DistDemoApp |
namespace DistDemoApp |
||||
{ |
{ |
||||
public class Program |
public class Program |
||||
{ |
{ |
||||
public static async Task<int> Main(string[] args) |
public static void Main(string[] args) |
||||
{ |
{ |
||||
Log.Logger = new LoggerConfiguration() |
using (var application = AbpApplicationFactory.Create<DistDemoAppEfCoreRabbitMqModule>(options => |
||||
#if DEBUG
|
{ |
||||
.MinimumLevel.Debug() |
options.UseAutofac(); |
||||
#else
|
options.Services.AddSerilog((_, _) => |
||||
.MinimumLevel.Information() |
{ |
||||
#endif
|
}); |
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) |
options.Services.AddLogging(c => c.AddSerilog()); |
||||
.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(); |
Log.Information("Starting DistDemoApp.EfCoreRabbitMq."); |
||||
} |
application.Initialize(); |
||||
|
|
||||
} |
AsyncHelper.RunSync( |
||||
|
() => application |
||||
|
.ServiceProvider |
||||
|
.GetRequiredService<DemoService>().CreateTodoItemAsync() |
||||
|
); |
||||
|
|
||||
internal static IHostBuilder CreateHostBuilder(string[] args) => |
application.Shutdown(); |
||||
Host.CreateDefaultBuilder(args) |
} |
||||
.UseAutofac() |
} |
||||
.UseSerilog() |
|
||||
.ConfigureAppConfiguration((context, config) => |
|
||||
{ |
|
||||
//setup your additional configuration sources
|
|
||||
}) |
|
||||
.ConfigureServices((hostContext, services) => |
|
||||
{ |
|
||||
services.AddApplication<DistDemoAppEfCoreRabbitMqModule>(); |
|
||||
}); |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +1,36 @@ |
|||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Hosting; |
|
||||
using Serilog; |
using Serilog; |
||||
using Serilog.Events; |
using Volo.Abp; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
namespace DistDemoApp |
namespace DistDemoApp |
||||
{ |
{ |
||||
public class Program |
public class Program |
||||
{ |
{ |
||||
public static async Task<int> Main(string[] args) |
public static void Main(string[] args) |
||||
{ |
{ |
||||
Log.Logger = new LoggerConfiguration() |
using (var application = AbpApplicationFactory.Create<DistDemoAppMongoDbKafkaModule>(options => |
||||
#if DEBUG
|
{ |
||||
.MinimumLevel.Debug() |
options.UseAutofac(); |
||||
#else
|
options.Services.AddSerilog((_, _) => |
||||
.MinimumLevel.Information() |
{ |
||||
#endif
|
}); |
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) |
options.Services.AddLogging(c => c.AddSerilog()); |
||||
.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(); |
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>(); |
|
||||
}); |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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 {} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -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>(); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -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> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.EntityFrameworkCore.SqlServer; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace DistDemoApp; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpEntityFrameworkCoreSqlServerModule), |
||||
|
typeof(DistDemoAppSharedModule) |
||||
|
)] |
||||
|
public class DistDemoAppEntityFrameworkCoreInfrastructureModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -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(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.MongoDB; |
||||
|
|
||||
|
namespace DistDemoApp; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpMongoDbModule), |
||||
|
typeof(DistDemoAppSharedModule) |
||||
|
)] |
||||
|
public class DistDemoAppMongoDbInfrastructureModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -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 { } |
||||
|
} |
||||
|
} |
||||
@ -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>(); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -1,29 +1,21 @@ |
|||||
using System; |
using System; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Domain.Repositories; |
|
||||
|
|
||||
namespace DistDemoApp |
namespace DistDemoApp |
||||
{ |
{ |
||||
public class DemoService : ITransientDependency |
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( |
await _scenarioRunner.RunAsync(DistEventScenarioProfile.Default()); |
||||
new TodoItem |
|
||||
{ |
|
||||
Text = "todo item " + DateTime.Now.Ticks |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
Console.WriteLine("Created a new todo item: " + todoItem); |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DistDemoApp; |
||||
|
|
||||
|
public interface IDistEventScenarioRunner |
||||
|
{ |
||||
|
Task RunAsync(DistEventScenarioProfile profile); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.EventBus; |
||||
|
|
||||
|
namespace DistDemoApp; |
||||
|
|
||||
|
[EventName("DistDemoApp.ProviderScenarioEvent")] |
||||
|
public class ProviderScenarioEvent |
||||
|
{ |
||||
|
public int Value { get; set; } |
||||
|
} |
||||
@ -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: "" |
||||
@ -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" |
||||
@ -0,0 +1,22 @@ |
|||||
|
{ |
||||
|
"UserConfig": { |
||||
|
"Namespaces": [ |
||||
|
{ |
||||
|
"Name": "sbemulatorns", |
||||
|
"Topics": [ |
||||
|
{ |
||||
|
"Name": "DistDemoAzureTopic", |
||||
|
"Subscriptions": [ |
||||
|
{ |
||||
|
"Name": "DistDemoAzureSubscriber" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
], |
||||
|
"Logging": { |
||||
|
"Type": "File" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue