Browse Source

Merge pull request #10008 from abpframework/eventboxes

Outbox & Inbox patterns for the distributed event bus
pull/10178/head
Halil İbrahim Kalkan 5 years ago
committed by GitHub
parent
commit
1ef5656db9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      framework/Volo.Abp.sln
  2. 7
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs
  3. 2
      framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj
  4. 3
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs
  5. 3
      framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xml
  6. 30
      framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xsd
  7. 25
      framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj
  8. 9
      framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs
  9. 13
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs
  10. 13
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs
  11. 8
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
  12. 7
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
  13. 49
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
  14. 31
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
  15. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
  16. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
  17. 7
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs
  18. 8
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
  19. 7
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
  20. 48
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
  21. 31
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
  22. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
  23. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
  24. 7
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs
  25. 8
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs
  26. 7
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs
  27. 44
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs
  28. 25
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs
  29. 13
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs
  30. 13
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs
  31. 7
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs
  32. 13
      framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs
  33. 13
      framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs
  34. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs
  35. 13
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs
  36. 69
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  37. 7
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs
  38. 89
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs
  39. 60
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs
  40. 13
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreInboxConfigExtensions.cs
  41. 13
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreOutboxConfigExtensions.cs
  42. 24
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventInboxDbContextModelBuilderExtensions.cs
  43. 23
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventOutboxDbContextModelBuilderExtensions.cs
  44. 10
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventInbox.cs
  45. 10
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventOutbox.cs
  46. 9
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventInbox.cs
  47. 9
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventOutbox.cs
  48. 7
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs
  49. 7
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs
  50. 66
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IncomingEventRecord.cs
  51. 52
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/OutgoingEventRecord.cs
  52. 43
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs
  53. 26
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs
  54. 3
      framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xml
  55. 30
      framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xsd
  56. 23
      framework/src/Volo.Abp.EventBus.Boxes/Volo.Abp.EventBus.Boxes.csproj
  57. 18
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpDistributedEventBusExtensions.cs
  58. 18
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesModule.cs
  59. 48
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesOptions.cs
  60. 13
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IInboxProcessor.cs
  61. 13
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IOutboxSender.cs
  62. 48
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessManager.cs
  63. 140
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessor.cs
  64. 108
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSender.cs
  65. 48
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSenderManager.cs
  66. 20
      framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/TaskHelper.cs
  67. 101
      framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs
  68. 84
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  69. 13
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/IRebusSerializer.cs
  70. 69
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs
  71. 2
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventHandlerAdapter.cs
  72. 32
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/Utf8JsonRabbitMqSerializer.cs
  73. 1
      framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj
  74. 5
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
  75. 7
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs
  76. 166
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs
  77. 13
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs
  78. 20
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventInbox.cs
  79. 16
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventOutbox.cs
  80. 17
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/ISupportsEventBoxes.cs
  81. 28
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfig.cs
  82. 19
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfigDictionary.cs
  83. 44
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IncomingEventInfo.cs
  84. 10
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
  85. 10
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullDistributedEventBus.cs
  86. 26
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfig.cs
  87. 19
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfigDictionary.cs
  88. 39
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutgoingEventInfo.cs
  89. 34
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  90. 13
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/UnitOfWorkEventPublisher.cs
  91. 3
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
  92. 3
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  93. 11
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs
  94. 9
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventInbox.cs
  95. 9
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventOutbox.cs
  96. 10
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventInbox.cs
  97. 10
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventOutbox.cs
  98. 66
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IncomingEventRecord.cs
  99. 112
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs
  100. 72
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventOutbox.cs

13
framework/Volo.Abp.sln

@ -387,6 +387,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.IdentityModel.Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Threading.Tests", "test\Volo.Abp.Threading.Tests\Volo.Abp.Threading.Tests.csproj", "{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Boxes", "src\Volo.Abp.EventBus.Boxes\Volo.Abp.EventBus.Boxes.csproj", "{6E289F31-7924-418B-9DAC-62A7CFADF916}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking", "src\Volo.Abp.DistributedLocking\Volo.Abp.DistributedLocking.csproj", "{9A7EEA08-15BE-476D-8168-53039867038E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Contracts", "src\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj", "{508B6355-AD28-4E60-8549-266D21DBF2CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Web", "src\Volo.Abp.Http.Client.Web\Volo.Abp.Http.Client.Web.csproj", "{F7407459-8AFA-45E4-83E9-9BB01412CC08}"
@ -1157,6 +1160,14 @@ Global
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.Build.0 = Release|Any CPU
{6E289F31-7924-418B-9DAC-62A7CFADF916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E289F31-7924-418B-9DAC-62A7CFADF916}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E289F31-7924-418B-9DAC-62A7CFADF916}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E289F31-7924-418B-9DAC-62A7CFADF916}.Release|Any CPU.Build.0 = Release|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A7EEA08-15BE-476D-8168-53039867038E}.Release|Any CPU.Build.0 = Release|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1360,6 +1371,8 @@ Global
{90B1866A-EF99-40B9-970E-B898E5AA523F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{40C6740E-BFCA-4D37-8344-3D84E2044BB2} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{6E289F31-7924-418B-9DAC-62A7CFADF916} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{9A7EEA08-15BE-476D-8168-53039867038E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{508B6355-AD28-4E60-8549-266D21DBF2CF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{F7407459-8AFA-45E4-83E9-9BB01412CC08} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection

7
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs

@ -20,12 +20,7 @@ namespace Volo.Abp.BackgroundJobs
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundJobOptions>>().Value;
if (options.IsJobExecutionEnabled)
{
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
context.ServiceProvider
.GetRequiredService<IBackgroundJobWorker>()
);
context.AddBackgroundWorker<IBackgroundJobWorker>();
}
}
}

2
framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj

@ -17,7 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus.Boxes\Volo.Abp.EventBus.Boxes.csproj" />
<ProjectReference Include="..\Volo.Abp.ExceptionHandling\Volo.Abp.ExceptionHandling.csproj" />
<ProjectReference Include="..\Volo.Abp.Guids\Volo.Abp.Guids.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />

3
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs

@ -3,6 +3,7 @@ using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
@ -18,7 +19,7 @@ namespace Volo.Abp.Domain
[DependsOn(
typeof(AbpAuditingModule),
typeof(AbpDataModule),
typeof(AbpEventBusModule),
typeof(AbpEventBusBoxesModule),
typeof(AbpGuidsModule),
typeof(AbpMultiTenancyModule),
typeof(AbpThreadingModule),

3
framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

25
framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.DistributedLocking</AssemblyName>
<PackageId>Volo.Abp.DistributedLocking</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DistributedLock.Core" Version="1.0.2" />
</ItemGroup>
</Project>

9
framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.DistributedLocking
{
public class AbpDistributedLockingModule : AbpModule
{
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class MySQLInboxConfigExtensions
{
public static void UseMySQL<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class MySQLOutboxConfigExtensions
{
public static void UseMySQL<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox<TDbContext>);
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IOracleDbContextEventInbox<TDbContext> : IDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IOracleDbContextEventOutbox<TDbContext> : IDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
}
}

49
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs

@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class OracleDbContextEventInbox<TDbContext> : DbContextEventInbox<TDbContext> , IOracleDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
public OracleDbContextEventInbox(
IDbContextProvider<TDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
: base(dbContextProvider, clock, eventBusBoxesOptions)
{
}
[UnitOfWork]
public override async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = TO_DATE('{Clock.Now}', 'yyyy-mm-dd hh24:mi:ss') WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
[UnitOfWork]
public override async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < TO_DATE('{timeToKeepEvents}', 'yyyy-mm-dd hh24:mi:ss')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
protected virtual string GuidToOracleType(Guid id)
{
return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
}
}
}

31
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs

@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class OracleDbContextEventOutbox<TDbContext> : DbContextEventOutbox<TDbContext> , IOracleDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
public OracleDbContextEventOutbox(IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
[UnitOfWork]
public override async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
protected virtual string GuidToOracleType(Guid id)
{
return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class OracleInboxConfigExtensions
{
public static void UseOracle<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class OracleOutboxConfigExtensions
{
public static void UseOracle<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox<TDbContext>);
}
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs

@ -1,4 +1,6 @@
using Volo.Abp.Guids;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.Oracle.Devart
@ -17,6 +19,9 @@ namespace Volo.Abp.EntityFrameworkCore.Oracle.Devart
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
}
});
context.Services.AddTransient(typeof(IOracleDbContextEventOutbox<>), typeof(OracleDbContextEventOutbox<>));
context.Services.AddTransient(typeof(IOracleDbContextEventInbox<>), typeof(OracleDbContextEventInbox<>));
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IOracleDbContextEventInbox<TDbContext> : IDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IOracleDbContextEventOutbox<TDbContext> : IDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
}
}

48
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs

@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class OracleDbContextEventInbox<TDbContext> : DbContextEventInbox<TDbContext> , IOracleDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
public OracleDbContextEventInbox(
IDbContextProvider<TDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
: base(dbContextProvider, clock, eventBusBoxesOptions)
{
}
[UnitOfWork]
public override async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = TO_DATE('{Clock.Now}', 'yyyy-mm-dd hh24:mi:ss') WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
[UnitOfWork]
public override async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < TO_DATE('{timeToKeepEvents}', 'yyyy-mm-dd hh24:mi:ss')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
protected virtual string GuidToOracleType(Guid id)
{
return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
}
}
}

31
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs

@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class OracleDbContextEventOutbox<TDbContext> : DbContextEventOutbox<TDbContext> , IOracleDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
public OracleDbContextEventOutbox(IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
[UnitOfWork]
public override async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
protected virtual string GuidToOracleType(Guid id)
{
return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class OracleInboxConfigExtensions
{
public static void UseOracle<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class OracleOutboxConfigExtensions
{
public static void UseOracle<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox<TDbContext>);
}
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs

@ -1,4 +1,6 @@
using Volo.Abp.Guids;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.Oracle
@ -15,6 +17,9 @@ namespace Volo.Abp.EntityFrameworkCore.Oracle
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
}
});
context.Services.AddTransient(typeof(IOracleDbContextEventOutbox<>), typeof(OracleDbContextEventOutbox<>));
context.Services.AddTransient(typeof(IOracleDbContextEventInbox<>), typeof(OracleDbContextEventInbox<>));
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IPostgreSqlDbContextEventInbox<TDbContext> : IDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IPostgreSqlDbContextEventOutbox<TDbContext> : IDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
}
}

44
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs

@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class PostgreSqlDbContextEventInbox<TDbContext> : DbContextEventInbox<TDbContext>, IPostgreSqlDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
public PostgreSqlDbContextEventInbox(
IDbContextProvider<TDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
: base(dbContextProvider, clock, eventBusBoxesOptions)
{
}
[UnitOfWork]
public override async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = '{Clock.Now}' WHERE \"Id\" = '{id}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
[UnitOfWork]
public override async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < '{timeToKeepEvents}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
}
}

25
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class PostgreSqlDbContextEventOutbox<TDbContext> : DbContextEventOutbox<TDbContext> , IPostgreSqlDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
public PostgreSqlDbContextEventOutbox(IDbContextProvider<TDbContext> dbContextProvider) : base(dbContextProvider)
{
}
[UnitOfWork]
public override async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = '{id}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class PostgreSqlInboxConfigExtensions
{
public static void UseNpgsql<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class PostgreSqlOutboxConfigExtensions
{
public static void UseNpgsql<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventOutbox<TDbContext>);
}
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs

@ -1,4 +1,6 @@
using Volo.Abp.Guids;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.PostgreSql
@ -17,6 +19,9 @@ namespace Volo.Abp.EntityFrameworkCore.PostgreSql
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString;
}
});
context.Services.AddTransient(typeof(IPostgreSqlDbContextEventOutbox<>), typeof(PostgreSqlDbContextEventOutbox<>));
context.Services.AddTransient(typeof(IPostgreSqlDbContextEventInbox<>), typeof(PostgreSqlDbContextEventInbox<>));
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class SqlServerInboxConfigExtensions
{
public static void UseSqlServer<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class SqlServerOutboxConfigExtensions
{
public static void UseSqlServer<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class SqliteInboxConfigExtensions
{
public static void UseSqlite<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class SqliteOutboxConfigExtensions
{
public static void UseSqlite<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox<TDbContext>);
}
}
}

69
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -166,6 +166,10 @@ namespace Volo.Abp.EntityFrameworkCore
entityChangeList = EntityHistoryHelper.CreateChangeList(ChangeTracker.Entries().ToList());
}
HandlePropertiesBeforeSave();
var eventReport = CreateEventReport();
var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
PublishEntityEvents(eventReport);
@ -291,10 +295,12 @@ namespace Volo.Abp.EntityFrameworkCore
switch (entry.State)
{
case EntityState.Added:
ApplyAbpConceptsForAddedEntity(entry);
EntityChangeEventHelper.PublishEntityCreatingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityCreatedEvent(entry.Entity);
break;
case EntityState.Modified:
ApplyAbpConceptsForModifiedEntity(entry);
if (entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never))
{
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
@ -311,17 +317,23 @@ namespace Volo.Abp.EntityFrameworkCore
break;
case EntityState.Deleted:
ApplyAbpConceptsForDeletedEntity(entry);
EntityChangeEventHelper.PublishEntityDeletingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
break;
}
}
protected virtual void ApplyAbpConcepts()
protected virtual void HandlePropertiesBeforeSave()
{
foreach (var entry in ChangeTracker.Entries().ToList())
{
ApplyAbpConcepts(entry);
HandleExtraPropertiesOnSave(entry);
if (entry.State.IsIn(EntityState.Modified, EntityState.Deleted))
{
UpdateConcurrencyStamp(entry);
}
}
}
@ -369,24 +381,6 @@ namespace Volo.Abp.EntityFrameworkCore
return eventReport;
}
protected virtual void ApplyAbpConcepts(EntityEntry entry)
{
switch (entry.State)
{
case EntityState.Added:
ApplyAbpConceptsForAddedEntity(entry);
break;
case EntityState.Modified:
ApplyAbpConceptsForModifiedEntity(entry);
break;
case EntityState.Deleted:
ApplyAbpConceptsForDeletedEntity(entry);
break;
}
HandleExtraPropertiesOnSave(entry);
}
protected virtual void HandleExtraPropertiesOnSave(EntityEntry entry)
{
@ -473,7 +467,6 @@ namespace Volo.Abp.EntityFrameworkCore
{
if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never))
{
UpdateConcurrencyStamp(entry);
SetModificationAuditProperties(entry);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
@ -485,11 +478,19 @@ namespace Volo.Abp.EntityFrameworkCore
protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry)
{
if (TryCancelDeletionForSoftDelete(entry))
if (!(entry.Entity is ISoftDelete))
{
return;
}
if (IsHardDeleted(entry))
{
UpdateConcurrencyStamp(entry);
SetDeletionAuditProperties(entry);
return;
}
entry.Reload();
entry.Entity.As<ISoftDelete>().IsDeleted = true;
entry.State = EntityState.Modified;
}
protected virtual bool IsHardDeleted(EntityEntry entry)
@ -510,7 +511,7 @@ namespace Volo.Abp.EntityFrameworkCore
{
return;
}
Entry(entity).Property(x => x.ConcurrencyStamp).OriginalValue = entity.ConcurrencyStamp;
entity.ConcurrencyStamp = Guid.NewGuid().ToString("N");
}
@ -531,24 +532,6 @@ namespace Volo.Abp.EntityFrameworkCore
entity.ConcurrencyStamp = Guid.NewGuid().ToString("N");
}
protected virtual bool TryCancelDeletionForSoftDelete(EntityEntry entry)
{
if (!(entry.Entity is ISoftDelete))
{
return false;
}
if (IsHardDeleted(entry))
{
return false;
}
entry.Reload();
entry.State = EntityState.Modified;
entry.Entity.As<ISoftDelete>().IsDeleted = true;
return true;
}
protected virtual void CheckAndSetId(EntityEntry entry)
{
if (entry.Entity is IEntity<Guid> entityWithGuidId)

7
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs

@ -2,7 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Domain;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Modularity;
using Volo.Abp.Uow.EntityFrameworkCore;
@ -26,6 +26,11 @@ namespace Volo.Abp.EntityFrameworkCore
});
context.Services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>));
context.Services.AddTransient(typeof(IDbContextEventOutbox<>), typeof(DbContextEventOutbox<>));
context.Services.AddTransient(typeof(IDbContextEventInbox<>), typeof(DbContextEventInbox<>));
context.Services.AddTransient(typeof(ISqlRawDbContextEventOutbox<>), typeof(SqlRawDbContextEventOutbox<>));
context.Services.AddTransient(typeof(ISqlRawDbContextEventInbox<>), typeof(SqlRawDbContextEventInbox<>));
}
}
}

89
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class DbContextEventInbox<TDbContext> : IDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
protected IDbContextProvider<TDbContext> DbContextProvider { get; }
protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; }
protected IClock Clock { get; }
public DbContextEventInbox(
IDbContextProvider<TDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
{
DbContextProvider = dbContextProvider;
Clock = clock;
EventBusBoxesOptions = eventBusBoxesOptions.Value;
}
[UnitOfWork]
public virtual async Task EnqueueAsync(IncomingEventInfo incomingEvent)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
dbContext.IncomingEvents.Add(
new IncomingEventRecord(incomingEvent)
);
}
[UnitOfWork]
public virtual async Task<List<IncomingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var outgoingEventRecords = await dbContext
.IncomingEvents
.AsNoTracking()
.Where(x => !x.Processed)
.OrderBy(x => x.CreationTime)
.Take(maxCount)
.ToListAsync(cancellationToken: cancellationToken);
return outgoingEventRecords
.Select(x => x.ToIncomingEventInfo())
.ToList();
}
[UnitOfWork]
public virtual async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var incomingEvent = await dbContext.IncomingEvents.FindAsync(id);
if (incomingEvent != null)
{
incomingEvent.MarkAsProcessed(Clock.Now);
}
}
[UnitOfWork]
public virtual async Task<bool> ExistsByMessageIdAsync(string messageId)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
return await dbContext.IncomingEvents.AnyAsync(x => x.MessageId == messageId);
}
[UnitOfWork]
public virtual async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
var oldEvents = await dbContext.IncomingEvents
.Where(x => x.Processed && x.CreationTime < timeToKeepEvents)
.ToListAsync();
dbContext.IncomingEvents.RemoveRange(oldEvents);
}
}
}

60
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class DbContextEventOutbox<TDbContext> : IDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
protected IDbContextProvider<TDbContext> DbContextProvider { get; }
public DbContextEventOutbox(
IDbContextProvider<TDbContext> dbContextProvider)
{
DbContextProvider = dbContextProvider;
}
[UnitOfWork]
public virtual async Task EnqueueAsync(OutgoingEventInfo outgoingEvent)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
dbContext.OutgoingEvents.Add(
new OutgoingEventRecord(outgoingEvent)
);
}
[UnitOfWork]
public virtual async Task<List<OutgoingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var outgoingEventRecords = await dbContext
.OutgoingEvents
.AsNoTracking()
.OrderBy(x => x.CreationTime)
.Take(maxCount)
.ToListAsync(cancellationToken: cancellationToken);
return outgoingEventRecords
.Select(x => x.ToOutgoingEventInfo())
.ToList();
}
[UnitOfWork]
public virtual async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var outgoingEvent = await dbContext.OutgoingEvents.FindAsync(id);
if (outgoingEvent != null)
{
dbContext.Remove(outgoingEvent);
}
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreInboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class EfCoreInboxConfigExtensions
{
public static void UseDbContext<TDbContext>(this InboxConfig outboxConfig)
where TDbContext : IHasEventInbox
{
outboxConfig.ImplementationType = typeof(IDbContextEventInbox<TDbContext>);
}
}
}

13
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreOutboxConfigExtensions.cs

@ -0,0 +1,13 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class EfCoreOutboxConfigExtensions
{
public static void UseDbContext<TDbContext>(this OutboxConfig outboxConfig)
where TDbContext : IHasEventOutbox
{
outboxConfig.ImplementationType = typeof(IDbContextEventOutbox<TDbContext>);
}
}
}

24
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventInboxDbContextModelBuilderExtensions.cs

@ -0,0 +1,24 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class EventInboxDbContextModelBuilderExtensions
{
public static void ConfigureEventInbox([NotNull] this ModelBuilder builder)
{
builder.Entity<IncomingEventRecord>(b =>
{
b.ToTable(AbpCommonDbProperties.DbTablePrefix + "EventInbox", AbpCommonDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.EventName).IsRequired().HasMaxLength(IncomingEventRecord.MaxEventNameLength);
b.Property(x => x.EventData).IsRequired();
b.HasIndex(x => new { x.Processed, x.CreationTime });
b.HasIndex(x => x.MessageId);
});
}
}
}

23
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventOutboxDbContextModelBuilderExtensions.cs

@ -0,0 +1,23 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public static class EventOutboxDbContextModelBuilderExtensions
{
public static void ConfigureEventOutbox([NotNull] this ModelBuilder builder)
{
builder.Entity<OutgoingEventRecord>(b =>
{
b.ToTable(AbpCommonDbProperties.DbTablePrefix + "EventOutbox", AbpCommonDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.EventName).IsRequired().HasMaxLength(OutgoingEventRecord.MaxEventNameLength);
b.Property(x => x.EventData).IsRequired();
b.HasIndex(x => x.CreationTime);
});
}
}
}

10
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventInbox.cs

@ -0,0 +1,10 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IDbContextEventInbox<TDbContext> : IEventInbox
where TDbContext : IHasEventInbox
{
}
}

10
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventOutbox.cs

@ -0,0 +1,10 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IDbContextEventOutbox<TDbContext> : IEventOutbox
where TDbContext : IHasEventOutbox
{
}
}

9
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventInbox.cs

@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IHasEventInbox : IEfCoreDbContext
{
DbSet<IncomingEventRecord> IncomingEvents { get; set; }
}
}

9
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventOutbox.cs

@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface IHasEventOutbox : IEfCoreDbContext
{
DbSet<OutgoingEventRecord> OutgoingEvents { get; set; }
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface ISqlRawDbContextEventInbox<TDbContext> : IDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
}
}

7
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public interface ISqlRawDbContextEventOutbox<TDbContext> : IDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
}
}

66
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IncomingEventRecord.cs

@ -0,0 +1,66 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class IncomingEventRecord :
BasicAggregateRoot<Guid>,
IHasExtraProperties,
IHasCreationTime
{
public static int MaxEventNameLength { get; set; } = 256;
public ExtraPropertyDictionary ExtraProperties { get; private set; }
public string MessageId { get; private set; }
public string EventName { get; private set; }
public byte[] EventData { get; private set; }
public DateTime CreationTime { get; private set; }
public bool Processed { get; set; }
public DateTime? ProcessedTime { get; set; }
protected IncomingEventRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public IncomingEventRecord(
IncomingEventInfo eventInfo)
: base(eventInfo.Id)
{
MessageId = eventInfo.MessageId;
EventName = eventInfo.EventName;
EventData = eventInfo.EventData;
CreationTime = eventInfo.CreationTime;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public IncomingEventInfo ToIncomingEventInfo()
{
return new IncomingEventInfo(
Id,
MessageId,
EventName,
EventData,
CreationTime
);
}
public void MarkAsProcessed(DateTime processedTime)
{
Processed = true;
ProcessedTime = processedTime;
}
}
}

52
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/OutgoingEventRecord.cs

@ -0,0 +1,52 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class OutgoingEventRecord :
BasicAggregateRoot<Guid>,
IHasExtraProperties,
IHasCreationTime
{
public static int MaxEventNameLength { get; set; } = 256;
public ExtraPropertyDictionary ExtraProperties { get; private set; }
public string EventName { get; private set; }
public byte[] EventData { get; private set; }
public DateTime CreationTime { get; private set; }
protected OutgoingEventRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public OutgoingEventRecord(
OutgoingEventInfo eventInfo)
: base(eventInfo.Id)
{
EventName = eventInfo.EventName;
EventData = eventInfo.EventData;
CreationTime = eventInfo.CreationTime;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public OutgoingEventInfo ToOutgoingEventInfo()
{
return new OutgoingEventInfo(
Id,
EventName,
EventData,
CreationTime
);
}
}
}

43
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs

@ -0,0 +1,43 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class SqlRawDbContextEventInbox<TDbContext> : DbContextEventInbox<TDbContext> , ISqlRawDbContextEventInbox<TDbContext>
where TDbContext : IHasEventInbox
{
public SqlRawDbContextEventInbox(
IDbContextProvider<TDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
: base(dbContextProvider, clock, eventBusBoxesOptions)
{
}
[UnitOfWork]
public override async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"UPDATE {tableName} SET Processed = '1', ProcessedTime = '{Clock.Now}' WHERE Id = '{id.ToString().ToUpper()}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
[UnitOfWork]
public override async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
var sql = $"DELETE FROM {tableName} WHERE Processed = '1' AND CreationTime < '{timeToKeepEvents}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
}
}

26
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore.DistributedEvents
{
public class SqlRawDbContextEventOutbox<TDbContext> : DbContextEventOutbox<TDbContext> , ISqlRawDbContextEventOutbox<TDbContext>
where TDbContext : IHasEventOutbox
{
public SqlRawDbContextEventOutbox(IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
[UnitOfWork]
public override async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await DbContextProvider.GetDbContextAsync();
var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
var sql = $"DELETE FROM {tableName} WHERE Id = '{id.ToString().ToUpper()}'";
await dbContext.Database.ExecuteSqlRawAsync(sql);
}
}
}

3
framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

23
framework/src/Volo.Abp.EventBus.Boxes/Volo.Abp.EventBus.Boxes.csproj

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.EventBus.Boxes</AssemblyName>
<PackageId>Volo.Abp.EventBus.Boxes</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj" />
<ProjectReference Include="..\Volo.Abp.DistributedLocking\Volo.Abp.DistributedLocking.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj" />
</ItemGroup>
</Project>

18
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpDistributedEventBusExtensions.cs

@ -0,0 +1,18 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EventBus.Boxes
{
public static class AbpDistributedEventBusExtensions
{
public static ISupportsEventBoxes AsSupportsEventBoxes(this IDistributedEventBus eventBus)
{
var supportsEventBoxes = eventBus as ISupportsEventBoxes;
if (supportsEventBoxes == null)
{
throw new AbpException($"Given type ({eventBus.GetType().AssemblyQualifiedName}) should implement {nameof(ISupportsEventBoxes)}!");
}
return supportsEventBoxes;
}
}
}

18
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesModule.cs

@ -0,0 +1,18 @@
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Modularity;
namespace Volo.Abp.EventBus.Boxes
{
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpBackgroundWorkersModule)
)]
public class AbpEventBusBoxesModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context.AddBackgroundWorker<OutboxSenderManager>();
context.AddBackgroundWorker<InboxProcessManager>();
}
}
}

48
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesOptions.cs

@ -0,0 +1,48 @@
using System;
namespace Volo.Abp.EventBus.Boxes
{
public class AbpEventBusBoxesOptions
{
/// <summary>
/// Default: 6 hours
/// </summary>
public TimeSpan CleanOldEventTimeIntervalSpan { get; set; }
/// <summary>
/// Default: 1000
/// </summary>
public int InboxWaitingEventMaxCount { get; set; }
/// <summary>
/// Default: 1000
/// </summary>
public int OutboxWaitingEventMaxCount { get; set; }
/// <summary>
/// Period time of <see cref="InboxProcessor"/> and <see cref="OutboxSender"/>
/// Default: 2 seconds
/// </summary>
public TimeSpan PeriodTimeSpan { get; set; }
/// <summary>
/// Default: 15 seconds
/// </summary>
public TimeSpan DistributedLockWaitDuration { get; set; }
/// <summary>
/// Default: 2 hours
/// </summary>
public TimeSpan WaitTimeToDeleteProcessedInboxEvents { get; set; }
public AbpEventBusBoxesOptions()
{
CleanOldEventTimeIntervalSpan = TimeSpan.FromHours(6);
InboxWaitingEventMaxCount = 1000;
OutboxWaitingEventMaxCount = 1000;
PeriodTimeSpan = TimeSpan.FromSeconds(2);
DistributedLockWaitDuration = TimeSpan.FromSeconds(15);
WaitTimeToDeleteProcessedInboxEvents = TimeSpan.FromHours(2);
}
}
}

13
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IInboxProcessor.cs

@ -0,0 +1,13 @@
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EventBus.Boxes
{
public interface IInboxProcessor
{
Task StartAsync(InboxConfig inboxConfig, CancellationToken cancellationToken = default);
Task StopAsync(CancellationToken cancellationToken = default);
}
}

13
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IOutboxSender.cs

@ -0,0 +1,13 @@
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EventBus.Boxes
{
public interface IOutboxSender
{
Task StartAsync(OutboxConfig outboxConfig, CancellationToken cancellationToken = default);
Task StopAsync(CancellationToken cancellationToken = default);
}
}

48
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessManager.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EventBus.Boxes
{
public class InboxProcessManager : IBackgroundWorker
{
protected AbpDistributedEventBusOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected List<IInboxProcessor> Processors { get; }
public InboxProcessManager(
IOptions<AbpDistributedEventBusOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
Processors = new List<IInboxProcessor>();
}
public async Task StartAsync(CancellationToken cancellationToken = default)
{
foreach (var inboxConfig in Options.Inboxes.Values)
{
if (inboxConfig.IsProcessingEnabled)
{
var processor = ServiceProvider.GetRequiredService<IInboxProcessor>();
await processor.StartAsync(inboxConfig, cancellationToken);
Processors.Add(processor);
}
}
}
public async Task StopAsync(CancellationToken cancellationToken = default)
{
foreach (var processor in Processors)
{
await processor.StopAsync(cancellationToken);
}
}
}
}

140
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessor.cs

@ -0,0 +1,140 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Medallion.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Boxes
{
public class InboxProcessor : IInboxProcessor, ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpAsyncTimer Timer { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected IDistributedLockProvider DistributedLockProvider { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IClock Clock { get; }
protected IEventInbox Inbox { get; private set; }
protected InboxConfig InboxConfig { get; private set; }
protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; }
protected DateTime? LastCleanTime { get; set; }
protected string DistributedLockName => "Inbox_" + InboxConfig.Name;
public ILogger<InboxProcessor> Logger { get; set; }
protected CancellationTokenSource StoppingTokenSource { get; }
protected CancellationToken StoppingToken { get; }
public InboxProcessor(
IServiceProvider serviceProvider,
AbpAsyncTimer timer,
IDistributedEventBus distributedEventBus,
IDistributedLockProvider distributedLockProvider,
IUnitOfWorkManager unitOfWorkManager,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
{
ServiceProvider = serviceProvider;
Timer = timer;
DistributedEventBus = distributedEventBus;
DistributedLockProvider = distributedLockProvider;
UnitOfWorkManager = unitOfWorkManager;
Clock = clock;
EventBusBoxesOptions = eventBusBoxesOptions.Value;
Timer.Period = EventBusBoxesOptions.PeriodTimeSpan.Milliseconds;
Timer.Elapsed += TimerOnElapsed;
Logger = NullLogger<InboxProcessor>.Instance;
StoppingTokenSource = new CancellationTokenSource();
StoppingToken = StoppingTokenSource.Token;
}
private async Task TimerOnElapsed(AbpAsyncTimer arg)
{
await RunAsync();
}
public Task StartAsync(InboxConfig inboxConfig, CancellationToken cancellationToken = default)
{
InboxConfig = inboxConfig;
Inbox = (IEventInbox)ServiceProvider.GetRequiredService(inboxConfig.ImplementationType);
Timer.Start(cancellationToken);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken = default)
{
StoppingTokenSource.Cancel();
Timer.Stop(cancellationToken);
StoppingTokenSource.Dispose();
return Task.CompletedTask;
}
protected virtual async Task RunAsync()
{
if (StoppingToken.IsCancellationRequested)
{
return;
}
await using (var handle = await DistributedLockProvider.TryAcquireLockAsync(DistributedLockName, cancellationToken: StoppingToken))
{
if (handle != null)
{
await DeleteOldEventsAsync();
while (true)
{
var waitingEvents = await Inbox.GetWaitingEventsAsync(EventBusBoxesOptions.InboxWaitingEventMaxCount, StoppingToken);
if (waitingEvents.Count <= 0)
{
break;
}
Logger.LogInformation($"Found {waitingEvents.Count} events in the inbox.");
foreach (var waitingEvent in waitingEvents)
{
using (var uow = UnitOfWorkManager.Begin(isTransactional: true, requiresNew: true))
{
await DistributedEventBus
.AsSupportsEventBoxes()
.ProcessFromInboxAsync(waitingEvent, InboxConfig);
await Inbox.MarkAsProcessedAsync(waitingEvent.Id);
await uow.CompleteAsync();
}
Logger.LogInformation($"Processed the incoming event with id = {waitingEvent.Id:N}");
}
}
}
else
{
Logger.LogDebug("Could not obtain the distributed lock: " + DistributedLockName);
await TaskDelayHelper.DelayAsync(EventBusBoxesOptions.DistributedLockWaitDuration.Milliseconds, StoppingToken);
}
}
}
protected virtual async Task DeleteOldEventsAsync()
{
if (LastCleanTime != null && LastCleanTime + EventBusBoxesOptions.CleanOldEventTimeIntervalSpan > Clock.Now)
{
return;
}
await Inbox.DeleteOldEventsAsync();
LastCleanTime = Clock.Now;
}
}
}

108
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSender.cs

@ -0,0 +1,108 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Medallion.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Boxes
{
public class OutboxSender : IOutboxSender, ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpAsyncTimer Timer { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected IDistributedLockProvider DistributedLockProvider { get; }
protected IEventOutbox Outbox { get; private set; }
protected OutboxConfig OutboxConfig { get; private set; }
protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; }
protected string DistributedLockName => "Outbox_" + OutboxConfig.Name;
public ILogger<OutboxSender> Logger { get; set; }
protected CancellationTokenSource StoppingTokenSource { get; }
protected CancellationToken StoppingToken { get; }
public OutboxSender(
IServiceProvider serviceProvider,
AbpAsyncTimer timer,
IDistributedEventBus distributedEventBus,
IDistributedLockProvider distributedLockProvider,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
{
ServiceProvider = serviceProvider;
Timer = timer;
DistributedEventBus = distributedEventBus;
DistributedLockProvider = distributedLockProvider;
EventBusBoxesOptions = eventBusBoxesOptions.Value;
Timer.Period = EventBusBoxesOptions.PeriodTimeSpan.Milliseconds;
Timer.Elapsed += TimerOnElapsed;
Logger = NullLogger<OutboxSender>.Instance;
StoppingTokenSource = new CancellationTokenSource();
StoppingToken = StoppingTokenSource.Token;
}
public virtual Task StartAsync(OutboxConfig outboxConfig, CancellationToken cancellationToken = default)
{
OutboxConfig = outboxConfig;
Outbox = (IEventOutbox)ServiceProvider.GetRequiredService(outboxConfig.ImplementationType);
Timer.Start(cancellationToken);
return Task.CompletedTask;
}
public virtual Task StopAsync(CancellationToken cancellationToken = default)
{
StoppingTokenSource.Cancel();
Timer.Stop(cancellationToken);
StoppingTokenSource.Dispose();
return Task.CompletedTask;
}
private async Task TimerOnElapsed(AbpAsyncTimer arg)
{
await RunAsync();
}
protected virtual async Task RunAsync()
{
await using (var handle = await DistributedLockProvider.TryAcquireLockAsync(DistributedLockName, cancellationToken: StoppingToken))
{
if (handle != null)
{
while (true)
{
var waitingEvents = await Outbox.GetWaitingEventsAsync(EventBusBoxesOptions.OutboxWaitingEventMaxCount, StoppingToken);
if (waitingEvents.Count <= 0)
{
break;
}
Logger.LogInformation($"Found {waitingEvents.Count} events in the outbox.");
foreach (var waitingEvent in waitingEvents)
{
await DistributedEventBus
.AsSupportsEventBoxes()
.PublishFromOutboxAsync(
waitingEvent,
OutboxConfig
);
await Outbox.DeleteAsync(waitingEvent.Id);
Logger.LogInformation($"Sent the event to the message broker with id = {waitingEvent.Id:N}");
}
}
}
else
{
Logger.LogDebug("Could not obtain the distributed lock: " + DistributedLockName);
await TaskDelayHelper.DelayAsync(EventBusBoxesOptions.DistributedLockWaitDuration.Milliseconds, StoppingToken);
}
}
}
}
}

48
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSenderManager.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.EventBus.Boxes
{
public class OutboxSenderManager : IBackgroundWorker
{
protected AbpDistributedEventBusOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected List<IOutboxSender> Senders { get; }
public OutboxSenderManager(
IOptions<AbpDistributedEventBusOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
Senders = new List<IOutboxSender>();
}
public async Task StartAsync(CancellationToken cancellationToken = default)
{
foreach (var outboxConfig in Options.Outboxes.Values)
{
if (outboxConfig.IsSendingEnabled)
{
var sender = ServiceProvider.GetRequiredService<IOutboxSender>();
await sender.StartAsync(outboxConfig, cancellationToken);
Senders.Add(sender);
}
}
}
public async Task StopAsync(CancellationToken cancellationToken = default)
{
foreach (var sender in Senders)
{
await sender.StopAsync(cancellationToken);
}
}
}
}

20
framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/TaskHelper.cs

@ -0,0 +1,20 @@
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Boxes
{
internal static class TaskDelayHelper
{
public static async Task DelayAsync(int milliseconds, CancellationToken cancellationToken = default)
{
try
{
await Task.Delay(milliseconds, cancellationToken);
}
catch (TaskCanceledException)
{
return;
}
}
}
}

101
framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs

@ -9,20 +9,21 @@ using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
using Volo.Abp.Kafka;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Kafka
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(KafkaDistributedEventBus))]
public class KafkaDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected AbpEventBusOptions AbpEventBusOptions { get; }
protected AbpKafkaEventBusOptions AbpKafkaEventBusOptions { get; }
protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; }
protected IKafkaMessageConsumerFactory MessageConsumerFactory { get; }
protected IKafkaSerializer Serializer { get; }
protected IProducerPool ProducerPool { get; }
@ -41,11 +42,19 @@ namespace Volo.Abp.EventBus.Kafka
IKafkaSerializer serializer,
IProducerPool producerPool,
IEventErrorHandler errorHandler,
IOptions<AbpEventBusOptions> abpEventBusOptions)
: base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler)
IOptions<AbpEventBusOptions> abpEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock)
: base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
abpDistributedEventBusOptions,
guidGenerator,
clock)
{
AbpKafkaEventBusOptions = abpKafkaEventBusOptions.Value;
AbpDistributedEventBusOptions = abpDistributedEventBusOptions.Value;
AbpEventBusOptions = abpEventBusOptions.Value;
MessageConsumerFactory = messageConsumerFactory;
Serializer = serializer;
@ -78,6 +87,18 @@ namespace Volo.Abp.EventBus.Kafka
return;
}
string messageId = null;
if (message.Headers.TryGetLastBytes("messageId", out var messageIdBytes))
{
messageId = System.Text.Encoding.UTF8.GetString(messageIdBytes);
}
if (await AddToInboxAsync(messageId, eventName, eventType, message.Value))
{
return;
}
var eventData = Serializer.Deserialize(message.Value, eventType);
await TriggerHandlersAsync(eventType, eventData, errorContext =>
@ -94,11 +115,6 @@ namespace Volo.Abp.EventBus.Kafka
});
}
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class
{
return Subscribe(typeof(TEvent), handler);
}
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
var handlerFactories = GetOrCreateHandlerFactories(eventType);
@ -169,7 +185,15 @@ namespace Volo.Abp.EventBus.Kafka
protected override async Task PublishToEventBusAsync(Type eventType, object eventData)
{
await PublishAsync(eventType, eventData, new Headers {{"messageId", Serializer.Serialize(Guid.NewGuid())}}, null);
await PublishAsync(
eventType,
eventData,
new Headers
{
{ "messageId", System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N")) }
},
null
);
}
protected override void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord)
@ -177,9 +201,55 @@ namespace Volo.Abp.EventBus.Kafka
unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
}
public override Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig)
{
return PublishAsync(
AbpKafkaEventBusOptions.TopicName,
outgoingEvent.EventName,
outgoingEvent.EventData,
new Headers
{
{ "messageId", System.Text.Encoding.UTF8.GetBytes(outgoingEvent.Id.ToString("N")) }
},
null
);
}
public override async Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig)
{
var eventType = EventTypes.GetOrDefault(incomingEvent.EventName);
if (eventType == null)
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
if (exceptions.Any())
{
ThrowOriginalExceptions(eventType, exceptions);
}
}
protected override byte[] Serialize(object eventData)
{
return Serializer.Serialize(eventData);
}
public virtual async Task PublishAsync(Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
{
await PublishAsync(AbpKafkaEventBusOptions.TopicName, eventType, eventData, headers, headersArguments);
await PublishAsync(
AbpKafkaEventBusOptions.TopicName,
eventType,
eventData,
headers,
headersArguments
);
}
public virtual async Task PublishToDeadLetterAsync(Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
@ -187,11 +257,16 @@ namespace Volo.Abp.EventBus.Kafka
await PublishAsync(DeadLetterTopicName, eventType, eventData, headers, headersArguments);
}
private async Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
{
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData);
return PublishAsync(topicName, eventName, body, headers, headersArguments);
}
private async Task PublishAsync(string topicName, string eventName, byte[] body, Headers headers, Dictionary<string, object> headersArguments)
{
var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName);
SetEventMessageHeaders(headers, headersArguments);

84
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -10,23 +10,22 @@ using RabbitMQ.Client.Events;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.RabbitMq
{
/* TODO: How to handle unsubscribe to unbind on RabbitMq (may not be possible for)
* TODO: Implement Retry system
* TODO: Should be improved
*/
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus))]
public class RabbitMqDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
public class RabbitMqDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected AbpRabbitMqEventBusOptions AbpRabbitMqEventBusOptions { get; }
protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; }
protected AbpEventBusOptions AbpEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; }
@ -47,14 +46,22 @@ namespace Volo.Abp.EventBus.RabbitMq
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler,
IOptions<AbpEventBusOptions> abpEventBusOptions)
: base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler)
IOptions<AbpEventBusOptions> abpEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock)
: base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
distributedEventBusOptions,
guidGenerator,
clock)
{
ConnectionPool = connectionPool;
Serializer = serializer;
MessageConsumerFactory = messageConsumerFactory;
AbpEventBusOptions = abpEventBusOptions.Value;
AbpDistributedEventBusOptions = distributedEventBusOptions.Value;
AbpRabbitMqEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
@ -96,7 +103,14 @@ namespace Volo.Abp.EventBus.RabbitMq
return;
}
var eventData = Serializer.Deserialize(ea.Body.ToArray(), eventType);
var eventBytes = ea.Body.ToArray();
if (await AddToInboxAsync(ea.BasicProperties.MessageId, eventName, eventType, eventBytes))
{
return;
}
var eventData = Serializer.Deserialize(eventBytes, eventType);
await TriggerHandlersAsync(eventType, eventData, errorContext =>
{
@ -107,17 +121,12 @@ namespace Volo.Abp.EventBus.RabbitMq
retryAttempt = (int)ea.BasicProperties.Headers[EventErrorHandlerBase.RetryAttemptKey];
}
errorContext.EventData = Serializer.Deserialize(ea.Body.ToArray(), eventType);
errorContext.EventData = Serializer.Deserialize(eventBytes, eventType);
errorContext.SetProperty(EventErrorHandlerBase.HeadersKey, ea.BasicProperties);
errorContext.SetProperty(EventErrorHandlerBase.RetryAttemptKey, retryAttempt);
});
}
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class
{
return Subscribe(typeof(TEvent), handler);
}
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
var handlerFactories = GetOrCreateHandlerFactories(eventType);
@ -201,11 +210,52 @@ namespace Volo.Abp.EventBus.RabbitMq
unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
}
public override Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig)
{
return PublishAsync(outgoingEvent.EventName, outgoingEvent.EventData, null, eventId: outgoingEvent.Id);
}
public override async Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig)
{
var eventType = EventTypes.GetOrDefault(incomingEvent.EventName);
if (eventType == null)
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
if (exceptions.Any())
{
ThrowOriginalExceptions(eventType, exceptions);
}
}
protected override byte[] Serialize(object eventData)
{
return Serializer.Serialize(eventData);
}
public Task PublishAsync(Type eventType, object eventData, IBasicProperties properties, Dictionary<string, object> headersArguments = null)
{
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData);
return PublishAsync(eventName, body, properties, headersArguments);
}
protected Task PublishAsync(
string eventName,
byte[] body,
IBasicProperties properties,
Dictionary<string, object> headersArguments = null,
Guid? eventId = null)
{
using (var channel = ConnectionPool.Get(AbpRabbitMqEventBusOptions.ConnectionName).CreateModel())
{
channel.ExchangeDeclare(
@ -218,9 +268,13 @@ namespace Volo.Abp.EventBus.RabbitMq
{
properties = channel.CreateBasicProperties();
properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent;
properties.MessageId = Guid.NewGuid().ToString("N");
}
if (properties.MessageId.IsNullOrEmpty())
{
properties.MessageId = (eventId ?? GuidGenerator.Create()).ToString("N");
}
SetEventMessageHeaders(properties, headersArguments);
channel.BasicPublish(

13
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/IRebusSerializer.cs

@ -0,0 +1,13 @@
using System;
namespace Volo.Abp.EventBus.Rebus
{
public interface IRebusSerializer
{
byte[] Serialize(object obj);
object Deserialize(byte[] value, Type type);
T Deserialize<T>(byte[] value);
}
}

69
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs

@ -6,24 +6,27 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Rebus.Bus;
using Rebus.Pipeline;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Rebus
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(RebusDistributedEventBus))]
public class RebusDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
public class RebusDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected IBus Rebus { get; }
protected IRebusSerializer Serializer { get; }
//TODO: Accessing to the List<IEventHandlerFactory> may not be thread-safe!
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected ConcurrentDictionary<string, Type> EventTypes { get; }
protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; }
protected AbpRebusEventBusOptions AbpRebusEventBusOptions { get; }
public RebusDistributedEventBus(
@ -33,12 +36,22 @@ namespace Volo.Abp.EventBus.Rebus
IBus rebus,
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IOptions<AbpRebusEventBusOptions> abpEventBusRebusOptions,
IEventErrorHandler errorHandler) :
base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler)
IEventErrorHandler errorHandler,
IRebusSerializer serializer,
IGuidGenerator guidGenerator,
IClock clock) :
base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
abpDistributedEventBusOptions,
guidGenerator,
clock)
{
Rebus = rebus;
Serializer = serializer;
AbpRebusEventBusOptions = abpEventBusRebusOptions.Value;
AbpDistributedEventBusOptions = abpDistributedEventBusOptions.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
@ -122,9 +135,17 @@ namespace Volo.Abp.EventBus.Rebus
Rebus.Unsubscribe(eventType);
}
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class
public async Task ProcessEventAsync(Type eventType, object eventData)
{
return Subscribe(typeof(TEvent), handler);
var messageId = MessageContext.Current.TransportMessage.GetMessageId();
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
if (await AddToInboxAsync(messageId, eventName, eventType, MessageContext.Current.TransportMessage.Body))
{
return;
}
await TriggerHandlersAsync(eventType, eventData);
}
protected override async Task PublishToEventBusAsync(Type eventType, object eventData)
@ -180,5 +201,39 @@ namespace Volo.Abp.EventBus.Rebus
return false;
}
public override Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig)
{
var eventType = EventTypes.GetOrDefault(outgoingEvent.EventName);
var eventData = Serializer.Deserialize(outgoingEvent.EventData, eventType);
return PublishToEventBusAsync(eventType, eventData);
}
public override async Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig)
{
var eventType = EventTypes.GetOrDefault(incomingEvent.EventName);
if (eventType == null)
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
if (exceptions.Any())
{
ThrowOriginalExceptions(eventType, exceptions);
}
}
protected override byte[] Serialize(object eventData)
{
return Serializer.Serialize(eventData);
}
}
}

2
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventHandlerAdapter.cs

@ -14,7 +14,7 @@ namespace Volo.Abp.EventBus.Rebus
public async Task Handle(TEventData message)
{
await RebusDistributedEventBus.TriggerHandlersAsync(typeof(TEventData), message);
await RebusDistributedEventBus.ProcessEventAsync(message.GetType(), message);
}
}
}

32
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/Utf8JsonRabbitMqSerializer.cs

@ -0,0 +1,32 @@
using System;
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.EventBus.Rebus
{
public class Utf8JsonRebusSerializer : IRebusSerializer, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
public Utf8JsonRebusSerializer(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
}
public byte[] Serialize(object obj)
{
return Encoding.UTF8.GetBytes(_jsonSerializer.Serialize(obj));
}
public object Deserialize(byte[] value, Type type)
{
return _jsonSerializer.Deserialize(type, Encoding.UTF8.GetString(value));
}
public T Deserialize<T>(byte[] value)
{
return _jsonSerializer.Deserialize<T>(Encoding.UTF8.GetString(value));
}
}
}

1
framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.EventBus.Abstractions\Volo.Abp.EventBus.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Guids\Volo.Abp.Guids.csproj" />
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />
</ItemGroup>

5
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs

@ -4,6 +4,7 @@ using System.Collections.Generic;
using Volo.Abp.EventBus.Abstractions;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
@ -14,7 +15,9 @@ namespace Volo.Abp.EventBus
[DependsOn(
typeof(AbpEventBusAbstractionsModule),
typeof(AbpMultiTenancyModule),
typeof(AbpJsonModule))]
typeof(AbpJsonModule),
typeof(AbpGuidsModule)
)]
public class AbpEventBusModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)

7
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs

@ -6,9 +6,14 @@ namespace Volo.Abp.EventBus.Distributed
{
public ITypeList<IEventHandler> Handlers { get; }
public OutboxConfigDictionary Outboxes { get; }
public InboxConfigDictionary Inboxes { get; }
public AbpDistributedEventBusOptions()
{
Handlers = new TypeList<IEventHandler>();
Outboxes = new OutboxConfigDictionary();
Inboxes = new InboxConfigDictionary();
}
}
}
}

166
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Distributed
{
public abstract class DistributedEventBusBase : EventBusBase, IDistributedEventBus, ISupportsEventBoxes
{
protected IGuidGenerator GuidGenerator { get; }
protected IClock Clock { get; }
protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; }
protected DistributedEventBusBase(
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler,
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock
) : base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler)
{
GuidGenerator = guidGenerator;
Clock = clock;
AbpDistributedEventBusOptions = abpDistributedEventBusOptions.Value;
}
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class
{
return Subscribe(typeof(TEvent), handler);
}
public override Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true)
{
return PublishAsync(eventType, eventData, onUnitOfWorkComplete, useOutbox: true);
}
public Task PublishAsync<TEvent>(
TEvent eventData,
bool onUnitOfWorkComplete = true,
bool useOutbox = true)
where TEvent : class
{
return PublishAsync(typeof(TEvent), eventData, onUnitOfWorkComplete, useOutbox);
}
public async Task PublishAsync(
Type eventType,
object eventData,
bool onUnitOfWorkComplete = true,
bool useOutbox = true)
{
if (onUnitOfWorkComplete && UnitOfWorkManager.Current != null)
{
AddToUnitOfWork(
UnitOfWorkManager.Current,
new UnitOfWorkEventRecord(eventType, eventData, EventOrderGenerator.GetNext(), useOutbox)
);
return;
}
if (useOutbox)
{
if (await AddToOutboxAsync(eventType, eventData))
{
return;
}
}
await PublishToEventBusAsync(eventType, eventData);
}
public abstract Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig
);
public abstract Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig);
private async Task<bool> AddToOutboxAsync(Type eventType, object eventData)
{
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null)
{
return false;
}
foreach (var outboxConfig in AbpDistributedEventBusOptions.Outboxes.Values)
{
if (outboxConfig.Selector == null || outboxConfig.Selector(eventType))
{
var eventOutbox = (IEventOutbox)unitOfWork.ServiceProvider.GetRequiredService(outboxConfig.ImplementationType);
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
await eventOutbox.EnqueueAsync(
new OutgoingEventInfo(
GuidGenerator.Create(),
eventName,
Serialize(eventData),
Clock.Now
)
);
return true;
}
}
return false;
}
protected async Task<bool> AddToInboxAsync(
string messageId,
string eventName,
Type eventType,
byte[] eventBytes)
{
if (AbpDistributedEventBusOptions.Inboxes.Count <= 0)
{
return false;
}
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var inboxConfig in AbpDistributedEventBusOptions.Inboxes.Values)
{
if (inboxConfig.EventSelector == null || inboxConfig.EventSelector(eventType))
{
var eventInbox = (IEventInbox) scope.ServiceProvider.GetRequiredService(inboxConfig.ImplementationType);
if (!messageId.IsNullOrEmpty())
{
if (await eventInbox.ExistsByMessageIdAsync(messageId))
{
continue;
}
}
await eventInbox.EnqueueAsync(
new IncomingEventInfo(
GuidGenerator.Create(),
messageId,
eventName,
eventBytes,
Clock.Now
)
);
}
}
}
return true;
}
protected abstract byte[] Serialize(object eventData);
}
}

13
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs

@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{
@ -12,5 +13,17 @@ namespace Volo.Abp.EventBus.Distributed
/// <param name="handler">Object to handle the event</param>
IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler)
where TEvent : class;
Task PublishAsync<TEvent>(
TEvent eventData,
bool onUnitOfWorkComplete = true,
bool useOutbox = true)
where TEvent : class;
Task PublishAsync(
Type eventType,
object eventData,
bool onUnitOfWorkComplete = true,
bool useOutbox = true);
}
}

20
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventInbox.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{
public interface IEventInbox
{
Task EnqueueAsync(IncomingEventInfo incomingEvent);
Task<List<IncomingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default);
Task MarkAsProcessedAsync(Guid id);
Task<bool> ExistsByMessageIdAsync(string messageId);
Task DeleteOldEventsAsync();
}
}

16
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventOutbox.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{
public interface IEventOutbox
{
Task EnqueueAsync(OutgoingEventInfo outgoingEvent);
Task<List<OutgoingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default);
Task DeleteAsync(Guid id);
}
}

17
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/ISupportsEventBoxes.cs

@ -0,0 +1,17 @@
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{
public interface ISupportsEventBoxes
{
Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig
);
Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig
);
}
}

28
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfig.cs

@ -0,0 +1,28 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.EventBus.Distributed
{
public class InboxConfig
{
[NotNull]
public string Name { get; }
public Type ImplementationType { get; set; }
public Func<Type, bool> EventSelector { get; set; }
public Func<Type, bool> HandlerSelector { get; set; }
/// <summary>
/// Used to enable/disable processing incoming events.
/// Default: true.
/// </summary>
public bool IsProcessingEnabled { get; set; } = true;
public InboxConfig([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
}
}
}

19
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfigDictionary.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.EventBus.Distributed
{
public class InboxConfigDictionary : Dictionary<string, InboxConfig>
{
public void Configure(Action<InboxConfig> configAction)
{
Configure("Default", configAction);
}
public void Configure(string outboxName, Action<InboxConfig> configAction)
{
var outboxConfig = this.GetOrAdd(outboxName, () => new InboxConfig(outboxName));
configAction(outboxConfig);
}
}
}

44
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IncomingEventInfo.cs

@ -0,0 +1,44 @@
using System;
using Volo.Abp.Data;
namespace Volo.Abp.EventBus.Distributed
{
public class IncomingEventInfo : IHasExtraProperties
{
public static int MaxEventNameLength { get; set; } = 256;
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public Guid Id { get; }
public string MessageId { get; }
public string EventName { get; }
public byte[] EventData { get; }
public DateTime CreationTime { get; }
protected IncomingEventInfo()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public IncomingEventInfo(
Guid id,
string messageId,
string eventName,
byte[] eventData,
DateTime creationTime)
{
Id = id;
MessageId = messageId;
EventName = Check.NotNullOrWhiteSpace(eventName, nameof(eventName), MaxEventNameLength);
EventData = eventData;
CreationTime = creationTime;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
}
}

10
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs

@ -132,5 +132,15 @@ namespace Volo.Abp.EventBus.Distributed
{
return _localEventBus.PublishAsync(eventType, eventData, onUnitOfWorkComplete);
}
public Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true, bool useOutbox = true) where TEvent : class
{
return _localEventBus.PublishAsync(eventData, onUnitOfWorkComplete);
}
public Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true, bool useOutbox = true)
{
return _localEventBus.PublishAsync(eventType, eventData, onUnitOfWorkComplete);
}
}
}

10
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullDistributedEventBus.cs

@ -86,5 +86,15 @@ namespace Volo.Abp.EventBus.Distributed
{
return Task.CompletedTask;
}
public Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true, bool useOutbox = true) where TEvent : class
{
return Task.CompletedTask;
}
public Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true, bool useOutbox = true)
{
return Task.CompletedTask;
}
}
}

26
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfig.cs

@ -0,0 +1,26 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.EventBus.Distributed
{
public class OutboxConfig
{
[NotNull]
public string Name { get; }
public Type ImplementationType { get; set; }
public Func<Type, bool> Selector { get; set; }
/// <summary>
/// Used to enable/disable sending events from outbox to the message broker.
/// Default: true.
/// </summary>
public bool IsSendingEnabled { get; set; } = true;
public OutboxConfig([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
}
}
}

19
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfigDictionary.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.EventBus.Distributed
{
public class OutboxConfigDictionary : Dictionary<string, OutboxConfig>
{
public void Configure(Action<OutboxConfig> configAction)
{
Configure("Default", configAction);
}
public void Configure(string outboxName, Action<OutboxConfig> configAction)
{
var outboxConfig = this.GetOrAdd(outboxName, () => new OutboxConfig(outboxName));
configAction(outboxConfig);
}
}
}

39
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutgoingEventInfo.cs

@ -0,0 +1,39 @@
using System;
using Volo.Abp.Data;
namespace Volo.Abp.EventBus.Distributed
{
public class OutgoingEventInfo : IHasExtraProperties
{
public static int MaxEventNameLength { get; set; } = 256;
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public Guid Id { get; }
public string EventName { get; }
public byte[] EventData { get; }
public DateTime CreationTime { get; }
protected OutgoingEventInfo()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public OutgoingEventInfo(
Guid id,
string eventName,
byte[] eventData,
DateTime creationTime)
{
Id = id;
EventName = Check.NotNullOrWhiteSpace(eventName, nameof(eventName), MaxEventNameLength);
EventData = eventData;
CreationTime = creationTime;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
}}

34
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs

@ -92,13 +92,17 @@ namespace Volo.Abp.EventBus
public abstract void UnsubscribeAll(Type eventType);
/// <inheritdoc/>
public Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true) where TEvent : class
public Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true)
where TEvent : class
{
return PublishAsync(typeof(TEvent), eventData, onUnitOfWorkComplete);
}
/// <inheritdoc/>
public async Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true)
public virtual async Task PublishAsync(
Type eventType,
object eventData,
bool onUnitOfWorkComplete = true)
{
if (onUnitOfWorkComplete && UnitOfWorkManager.Current != null)
{
@ -130,7 +134,7 @@ namespace Volo.Abp.EventBus
}
}
protected virtual async Task TriggerHandlersAsync(Type eventType, object eventData , List<Exception> exceptions)
protected virtual async Task TriggerHandlersAsync(Type eventType, object eventData, List<Exception> exceptions, InboxConfig inboxConfig = null)
{
await new SynchronizationContextRemover();
@ -138,7 +142,7 @@ namespace Volo.Abp.EventBus
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData, exceptions);
await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData, exceptions, inboxConfig);
}
}
@ -158,6 +162,19 @@ namespace Volo.Abp.EventBus
}
}
}
protected void ThrowOriginalExceptions(Type eventType, List<Exception> exceptions)
{
if (exceptions.Count == 1)
{
exceptions[0].ReThrow();
}
throw new AggregateException(
"More than one error has occurred while triggering the event: " + eventType,
exceptions
);
}
protected virtual void SubscribeHandlers(ITypeList<IEventHandler> handlers)
{
@ -182,7 +199,8 @@ namespace Volo.Abp.EventBus
protected abstract IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType);
protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List<Exception> exceptions)
protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType,
object eventData, List<Exception> exceptions, InboxConfig inboxConfig = null)
{
using (var eventHandlerWrapper = asyncHandlerFactory.GetHandler())
{
@ -190,6 +208,12 @@ namespace Volo.Abp.EventBus
{
var handlerType = eventHandlerWrapper.EventHandler.GetType();
if (inboxConfig?.HandlerSelector != null &&
!inboxConfig.HandlerSelector(handlerType))
{
return;
}
using (CurrentTenant.Change(GetEventDataTenantId(eventData)))
{
if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)))

13
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/UnitOfWorkEventPublisher.cs

@ -25,7 +25,11 @@ namespace Volo.Abp.EventBus
{
foreach (var localEvent in localEvents)
{
await _localEventBus.PublishAsync(localEvent.EventType, localEvent.EventData, onUnitOfWorkComplete: false);
await _localEventBus.PublishAsync(
localEvent.EventType,
localEvent.EventData,
onUnitOfWorkComplete: false
);
}
}
@ -33,7 +37,12 @@ namespace Volo.Abp.EventBus
{
foreach (var distributedEvent in distributedEvents)
{
await _distributedEventBus.PublishAsync(distributedEvent.EventType, distributedEvent.EventData, onUnitOfWorkComplete: false);
await _distributedEventBus.PublishAsync(
distributedEvent.EventType,
distributedEvent.EventData,
onUnitOfWorkComplete: false,
useOutbox: distributedEvent.UseOutbox
);
}
}
}

3
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs

@ -100,7 +100,8 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
new UnitOfWorkEventRecord(
distributedEvent.EventData.GetType(),
distributedEvent.EventData,
distributedEvent.EventOrder
distributedEvent.EventOrder,
useOutbox: true
)
);
}

3
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -570,12 +570,13 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
);
}
protected virtual async Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity)
protected virtual Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity)
{
CheckAndSetId(entity);
SetCreationAuditProperties(entity);
TriggerEntityCreateEvents(entity);
TriggerDomainEvents(entity);
return Task.CompletedTask;
}
private void TriggerEntityCreateEvents(TEntity entity)

11
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs

@ -5,6 +5,7 @@ using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.DependencyInjection;
using Volo.Abp.Uow.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
namespace Volo.Abp.MongoDB
{
@ -32,6 +33,16 @@ namespace Volo.Abp.MongoDB
typeof(IMongoDbRepositoryFilterer<,>),
typeof(MongoDbRepositoryFilterer<,>)
);
context.Services.AddTransient(
typeof(IMongoDbContextEventOutbox<>),
typeof(MongoDbContextEventOutbox<>)
);
context.Services.AddTransient(
typeof(IMongoDbContextEventInbox<>),
typeof(MongoDbContextEventInbox<>)
);
}
}
}

9
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventInbox.cs

@ -0,0 +1,9 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public interface IHasEventInbox : IAbpMongoDbContext
{
IMongoCollection<IncomingEventRecord> IncomingEvents { get; }
}
}

9
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventOutbox.cs

@ -0,0 +1,9 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public interface IHasEventOutbox : IAbpMongoDbContext
{
IMongoCollection<OutgoingEventRecord> OutgoingEvents { get; }
}
}

10
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventInbox.cs

@ -0,0 +1,10 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public interface IMongoDbContextEventInbox<TDbContext> : IEventInbox
where TDbContext : IHasEventInbox
{
}
}

10
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventOutbox.cs

@ -0,0 +1,10 @@
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public interface IMongoDbContextEventOutbox<TDbContext> : IEventOutbox
where TDbContext : IHasEventOutbox
{
}
}

66
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IncomingEventRecord.cs

@ -0,0 +1,66 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public class IncomingEventRecord :
BasicAggregateRoot<Guid>,
IHasExtraProperties,
IHasCreationTime
{
public static int MaxEventNameLength { get; set; } = 256;
public ExtraPropertyDictionary ExtraProperties { get; private set; }
public string MessageId { get; private set; }
public string EventName { get; private set; }
public byte[] EventData { get; private set; }
public DateTime CreationTime { get; private set; }
public bool Processed { get; set; }
public DateTime? ProcessedTime { get; set; }
protected IncomingEventRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public IncomingEventRecord(
IncomingEventInfo eventInfo)
: base(eventInfo.Id)
{
MessageId = eventInfo.MessageId;
EventName = eventInfo.EventName;
EventData = eventInfo.EventData;
CreationTime = eventInfo.CreationTime;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public IncomingEventInfo ToIncomingEventInfo()
{
return new IncomingEventInfo(
Id,
MessageId,
EventName,
EventData,
CreationTime
);
}
public void MarkAsProcessed(DateTime processedTime)
{
Processed = true;
ProcessedTime = processedTime;
}
}
}

112
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp.EventBus.Boxes;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public class MongoDbContextEventInbox<TMongoDbContext> : IMongoDbContextEventInbox<TMongoDbContext>
where TMongoDbContext : IHasEventInbox
{
protected IMongoDbContextProvider<TMongoDbContext> DbContextProvider { get; }
protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; }
protected IClock Clock { get; }
public MongoDbContextEventInbox(
IMongoDbContextProvider<TMongoDbContext> dbContextProvider,
IClock clock,
IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
{
DbContextProvider = dbContextProvider;
Clock = clock;
EventBusBoxesOptions = eventBusBoxesOptions.Value;
}
[UnitOfWork]
public virtual async Task EnqueueAsync(IncomingEventInfo incomingEvent)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
if (dbContext.SessionHandle != null)
{
await dbContext.IncomingEvents.InsertOneAsync(
dbContext.SessionHandle,
new IncomingEventRecord(incomingEvent)
);
}
else
{
await dbContext.IncomingEvents.InsertOneAsync(
new IncomingEventRecord(incomingEvent)
);
}
}
[UnitOfWork]
public virtual async Task<List<IncomingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default)
{
var dbContext = await DbContextProvider.GetDbContextAsync(cancellationToken);
var outgoingEventRecords = await dbContext
.IncomingEvents
.AsQueryable()
.Where(x => !x.Processed)
.OrderBy(x => x.CreationTime)
.Take(maxCount)
.ToListAsync(cancellationToken: cancellationToken);
return outgoingEventRecords
.Select(x => x.ToIncomingEventInfo())
.ToList();
}
[UnitOfWork]
public virtual async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var filter = Builders<IncomingEventRecord>.Filter.Eq(x => x.Id, id);
var update = Builders<IncomingEventRecord>.Update.Set(x => x.Processed, true).Set(x => x.ProcessedTime, Clock.Now);
if (dbContext.SessionHandle != null)
{
await dbContext.IncomingEvents.UpdateOneAsync(dbContext.SessionHandle, filter, update);
}
else
{
await dbContext.IncomingEvents.UpdateOneAsync(filter, update);
}
}
[UnitOfWork]
public virtual async Task<bool> ExistsByMessageIdAsync(string messageId)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
return await dbContext.IncomingEvents.AsQueryable().AnyAsync(x => x.MessageId == messageId);
}
[UnitOfWork]
public virtual async Task DeleteOldEventsAsync()
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
if (dbContext.SessionHandle != null)
{
await dbContext.IncomingEvents.DeleteManyAsync(dbContext.SessionHandle, x => x.Processed && x.CreationTime < timeToKeepEvents);
}
else
{
await dbContext.IncomingEvents.DeleteManyAsync(x => x.Processed && x.CreationTime < timeToKeepEvents);
}
}
}
}

72
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventOutbox.cs

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
namespace Volo.Abp.MongoDB.DistributedEvents
{
public class MongoDbContextEventOutbox<TMongoDbContext> : IMongoDbContextEventOutbox<TMongoDbContext>
where TMongoDbContext : IHasEventOutbox
{
protected IMongoDbContextProvider<TMongoDbContext> MongoDbContextProvider { get; }
public MongoDbContextEventOutbox(IMongoDbContextProvider<TMongoDbContext> mongoDbContextProvider)
{
MongoDbContextProvider = mongoDbContextProvider;
}
[UnitOfWork]
public virtual async Task EnqueueAsync(OutgoingEventInfo outgoingEvent)
{
var dbContext = (IHasEventOutbox) await MongoDbContextProvider.GetDbContextAsync();
if (dbContext.SessionHandle != null)
{
await dbContext.OutgoingEvents.InsertOneAsync(
dbContext.SessionHandle,
new OutgoingEventRecord(outgoingEvent)
);
}
else
{
await dbContext.OutgoingEvents.InsertOneAsync(
new OutgoingEventRecord(outgoingEvent)
);
}
}
[UnitOfWork]
public virtual async Task<List<OutgoingEventInfo>> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default)
{
var dbContext = (IHasEventOutbox) await MongoDbContextProvider.GetDbContextAsync(cancellationToken);
var outgoingEventRecords = await dbContext
.OutgoingEvents.AsQueryable()
.OrderBy(x => x.CreationTime)
.Take(maxCount)
.ToListAsync(cancellationToken: cancellationToken);
return outgoingEventRecords
.Select(x => x.ToOutgoingEventInfo())
.ToList();
}
[UnitOfWork]
public virtual async Task DeleteAsync(Guid id)
{
var dbContext = (IHasEventOutbox) await MongoDbContextProvider.GetDbContextAsync();
if (dbContext.SessionHandle != null)
{
await dbContext.OutgoingEvents.DeleteOneAsync(dbContext.SessionHandle, x => x.Id.Equals(id));
}
else
{
await dbContext.OutgoingEvents.DeleteOneAsync(x => x.Id.Equals(id));
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save