diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index f7094c94a7..9410a7813f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -312,7 +312,6 @@ "PaymentStateSetTo" : "Payment state set to {0}", "ChangeState": "Change State", "Permission:TrialLicense" : "Trial License", - "Permission:ManageTrialLicense": "Manage Trial License", "Menu:TrialLicenses": "Trial Licenses", "TrialLicenses": "Trial Licenses", "UserNameFilter": "Username", @@ -340,8 +339,8 @@ "Activated": "Activated", "PurchasedToNormalLicense": "Purchased", "Expired": "Expired", - "TrialLicenseDeletionWarningMessage": "Trial license and if any organization and qa organization will be deleted!", - "IsTrial": "Is trial", + "TrialLicenseDeletionWarningMessage": "Are you sure you want to delete the trial license? Trial license, organization, support accounts will be deleted!", + "LicenseCategoryFilter": "License category", "Volo.AbpIo.Commercial:030000": "You already used your trial period.", "Volo.AbpIo.Commercial:030001": "This organization name already exists.", "Volo.AbpIo.Commercial:030002": "Once activated, trial license cannot be set to requested!", @@ -349,6 +348,7 @@ "Volo.AbpIo.Commercial:030004": "Status could not be changed due to an unexpected error!", "Volo.AbpIo.Commercial:030005": "Start date and end date cannot be given when the trial license is in the requested state!", "Volo.AbpIo.Commercial:030006": "End date must always be greater than start date!", - "Volo.AbpIo.Commercial:030007": "This trial license has already been activated once!" + "Volo.AbpIo.Commercial:030007": "This trial license has already been activated once!", + "Volo.AbpIo.Commercial:030008": "Purchase date can be set only when status is Purchased!", } } diff --git a/docs/en/Migration-Guides/Abp-5_0-Angular.md b/docs/en/Migration-Guides/Abp-5_0-Angular.md new file mode 100644 index 0000000000..6cdb7af7c9 --- /dev/null +++ b/docs/en/Migration-Guides/Abp-5_0-Angular.md @@ -0,0 +1,101 @@ +# Angular UI v4.x to v5.0 Migration Guide + +## Breaking Changes + +### Overall + +See the overall list of breaking changes: + +- Bootstrap 5 implementation [#10067](https://github.com/abpframework/abp/issues/10067) +- Remove NGXS dependency & states [#9952](https://github.com/abpframework/abp/issues/9952) +- Install @angular/localize package to startup templates [#10099](https://github.com/abpframework/abp/issues/10099) +- Create new secondary entrypoints and move the related proxies to there [#10060](https://github.com/abpframework/abp/issues/10060) +- Move SettingTabsService to @abp/ng.setting-management/config package from @abp/ng.core [#10061](https://github.com/abpframework/abp/issues/10061) +- Make the @abp/ng.account dependent on @abp/ng.identity [#10059](https://github.com/abpframework/abp/issues/10059) +- Set default abp-modal size medium [#10118](https://github.com/abpframework/abp/issues/10118) +- Update all dependency versions to the latest [#9806](https://github.com/abpframework/abp/issues/9806) +- Chart.js big include with CommonJS warning [#7472](https://github.com/abpframework/abp/issues/7472) + +### Angular v12 + +The new ABP Angular UI is based on Angular v12. We started to compile Angular UI packages with the Ivy compilation. Therefore, **new packages only work with Angular v12**. If you are still on the older version of Angular v12, you have to update to Angular v12. The update is usually very easy. See [Angular Update Guide](https://update.angular.io/?l=2&v=11.0-12.0) for further information. + +### Bootstrap 5 + +TODO + +### NGXS has been removed + +We aim to make the ABP Framework free of any state-management solutions. ABP developers should be able to use the ABP Framework with any library/framework of their choice. So, we decided to remove NGXS from ABP packages. + +If you'd like to use NGXS after upgrading to v5.0, you have to install the NGXS to your project. The package can be installed with the following command: + +```bash +npm install @ngxs/store + +# or + +yarn add @ngxs/store +``` + +NGXS states and actions, some namespaces have been removed. See [this issue](https://github.com/abpframework/abp/issues/9952) for the details. + +If you don't want to use the NGXS, you should remove all NGXS related imports, injections, etc., from your project. + +### @angular/localize package + +[`@angular/localize`](https://angular.io/api/localize) dependency has been removed from `@abp/ng.core` package. The package must be installed in your app. Run the following command to install: + +```bash +npm install @angular/localize + +# or + +yarn add @angular/localize +``` + +> ABP Angular UI packages are not dependent on the `@angular/localize` package. However, some packages (like `@ng-bootstrap/ng-bootstrap`) depend on the package. Thus, this package needs to be installed in your project. + +### Proxy endpoints + +New endpoints named proxy have been created, related proxies have moved. +For example; before v5.0, `IdentityUserService` could be imported from `@abp/ng.identity`. As of v5.0, the service can be imported from `@abp/ng.identity/proxy`. See an example: + +```ts +import { IdentityUserService } from '@abp/ng.identity/proxy'; + +@Component({}) +export class YourComponent { + constructor(private identityUserService: IdentityUserService) {} +} +``` + +Following proxies have been affected: + +- `@abp/ng.account` to `@abp/ng.account.core/proxy` +- `@abp/ng.feature-management` to `@abp/ng.feature-management/proxy` +- `@abp/ng.identity` to `@abp/ng.identity/proxy` +- `@abp/ng.permission-management` to `@abp/ng.permission-management/proxy` +- `@abp/ng.tenant-management` to `@abp/ng.tenant-management/proxy` +- **ProfileService** is deleted from `@abp/ng.core`. Instead, you can import it from `@abp/ng.identity/proxy` + +### SettingTabsService + +**SettingTabsService** has moved from `@abp/ng.core` to `@abp/ng.setting-management/config`. + +### ChartComponent + +`ChartComponent` has moved from `@abp/ng.theme.shared` to `@abp/ng.components/chart.js`. To use the component, you need to import the `ChartModule` to your module as follows: + +```ts +import { ChartModule } from '@abp/ng.components/chart.js'; + +@NgModule({ + imports: [ + ChartModule, + // ... + ], + // ... +}) +export class YourFeatureModule {} +``` diff --git a/docs/en/Migration-Guides/Abp-5_0.md b/docs/en/Migration-Guides/Abp-5_0.md new file mode 100644 index 0000000000..e36e5269cb --- /dev/null +++ b/docs/en/Migration-Guides/Abp-5_0.md @@ -0,0 +1,14 @@ +# ABP Framework v4.x to v5.0 Migration Guide + +## MongoDB + +ABP Framework will serialize the datetime based on [AbpClockOptions](https://docs.abp.io/en/abp/latest/Timing#clock-options) start from 5.0, before `DateTime` values in MongoDB are [always saved as UTC](https://mongodb.github.io/mongo-csharp-driver/2.13/reference/bson/mapping/#datetime-serialization-options). + +You can disable this behavior by configure `AbpMongoDbOptions`. +```cs +services.Configure(x => x.UseAbpClockHandleDateTime = false); +``` + +## Angular UI + +See the [Angular UI Migration Guide](Abp-5_0-Angular.md). diff --git a/docs/en/Migration-Guides/Index.md b/docs/en/Migration-Guides/Index.md index c7bee5a82a..909f6c0998 100644 --- a/docs/en/Migration-Guides/Index.md +++ b/docs/en/Migration-Guides/Index.md @@ -1,7 +1,7 @@ # ABP Framework Migration Guides -* [4.2 to 4.3](Abp-4_3.md) -* [4.x to 4.2](Abp-4_2.md) -* [3.3.x to 4.0](Abp-4_0.md) -* [2.9.x to 3.0](../UI/Angular/Migration-Guide-v3.md) - +- [4.x to 5.0](Abp-5_0.md) +- [4.2 to 4.3](Abp-4_3.md) +- [4.x to 4.2](Abp-4_2.md) +- [3.3.x to 4.0](Abp-4_0.md) +- [2.9.x to 3.0](../UI/Angular/Migration-Guide-v3.md) diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 42df38e020..6ea15635e9 100644 --- a/framework/Volo.Abp.sln +++ b/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 diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs index cea8a1eee6..dccf2006dc 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs @@ -20,12 +20,7 @@ namespace Volo.Abp.BackgroundJobs var options = context.ServiceProvider.GetRequiredService>().Value; if (options.IsJobExecutionEnabled) { - context.ServiceProvider - .GetRequiredService() - .Add( - context.ServiceProvider - .GetRequiredService() - ); + context.AddBackgroundWorker(); } } } diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs index 54123bc843..528a8c67a1 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs @@ -153,19 +153,6 @@ namespace Volo.Abp.Application.Services return query; } - /// - /// This method should create based on given input. - /// It should filter query if needed, but should not do sorting or paging. - /// Sorting should be done in and paging should be done in - /// methods. - /// - /// The input. - [Obsolete("Override the CreateFilteredQueryAsync method instead.")] - protected virtual IQueryable CreateFilteredQuery(TGetListInput input) - { - return ReadOnlyRepository; - } - /// /// This method should create based on given input. /// It should filter query if needed, but should not do sorting or paging. @@ -175,17 +162,6 @@ namespace Volo.Abp.Application.Services /// The input. protected virtual async Task> CreateFilteredQueryAsync(TGetListInput input) { - /* If user has overridden the CreateFilteredQuery method, - * we don't want to make breaking change in this point. - */ -#pragma warning disable 618 - var query = CreateFilteredQuery(input); -#pragma warning restore 618 - if (!ReferenceEquals(query, ReadOnlyRepository)) - { - return query; - } - return await ReadOnlyRepository.GetQueryableAsync(); } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj index 87879334af..68c90489ff 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj @@ -17,7 +17,7 @@ - + diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs index 523a6ee4f2..38e2d9fc84 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs +++ b/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), diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs index 7aeffdad12..bf0704f085 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs @@ -10,7 +10,7 @@ using Volo.Abp.Linq; namespace Volo.Abp.Domain.Repositories { - public interface IReadOnlyRepository : IQueryable, IReadOnlyBasicRepository + public interface IReadOnlyRepository: IReadOnlyBasicRepository where TEntity : class, IEntity { IAsyncQueryableExecuter AsyncExecuter { get; } @@ -26,7 +26,7 @@ namespace Volo.Abp.Domain.Repositories Task> WithDetailsAsync(params Expression>[] propertySelectors); //TODO: CancellationToken Task> GetQueryableAsync(); //TODO: CancellationToken - + /// /// Gets a list of entities by the given . /// diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs index 0b32d54d45..da10901420 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs @@ -1,6 +1,5 @@ using JetBrains.Annotations; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -15,15 +14,6 @@ namespace Volo.Abp.Domain.Repositories public abstract class RepositoryBase : BasicRepositoryBase, IRepository, IUnitOfWorkManagerAccessor where TEntity : class, IEntity { - [Obsolete("This method will be removed in future versions.")] - public virtual Type ElementType => GetQueryable().ElementType; - - [Obsolete("This method will be removed in future versions.")] - public virtual Expression Expression => GetQueryable().Expression; - - [Obsolete("This method will be removed in future versions.")] - public virtual IQueryProvider Provider => GetQueryable().Provider; - [Obsolete("Use WithDetailsAsync method.")] public virtual IQueryable WithDetails() { @@ -46,18 +36,6 @@ namespace Volo.Abp.Domain.Repositories return GetQueryableAsync(); } - [Obsolete("This method will be removed in future versions.")] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - [Obsolete("This method will be removed in future versions.")] - public IEnumerator GetEnumerator() - { - return GetQueryable().GetEnumerator(); - } - [Obsolete("Use GetQueryableAsync method.")] protected abstract IQueryable GetQueryable(); diff --git a/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xml b/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xsd b/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj b/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj new file mode 100644 index 0000000000..c4b590412d --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0 + Volo.Abp.DistributedLocking + Volo.Abp.DistributedLocking + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs new file mode 100644 index 0000000000..877ac54244 --- /dev/null +++ b/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 + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs new file mode 100644 index 0000000000..ecda92702b --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs new file mode 100644 index 0000000000..8657ba92ab --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs new file mode 100644 index 0000000000..0cc7ae5531 --- /dev/null +++ b/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 : IDbContextEventInbox + where TDbContext : IHasEventInbox + { + + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs new file mode 100644 index 0000000000..a588f36e43 --- /dev/null +++ b/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 : IDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs new file mode 100644 index 0000000000..c19ca6746a --- /dev/null +++ b/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 : DbContextEventInbox , IOracleDbContextEventInbox + where TDbContext : IHasEventInbox + { + public OracleDbContextEventInbox( + IDbContextProvider dbContextProvider, + IClock clock, + IOptions 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(); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs new file mode 100644 index 0000000000..a5c4566ced --- /dev/null +++ b/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 : DbContextEventOutbox , IOracleDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + public OracleDbContextEventOutbox(IDbContextProvider 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(); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs new file mode 100644 index 0000000000..ca79019e28 --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs new file mode 100644 index 0000000000..e2d7d33761 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs index 9580219cfc..76d0ecdd21 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs +++ b/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<>)); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs new file mode 100644 index 0000000000..0cc7ae5531 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface IOracleDbContextEventInbox : IDbContextEventInbox + where TDbContext : IHasEventInbox + { + + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs new file mode 100644 index 0000000000..a588f36e43 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface IOracleDbContextEventOutbox : IDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs new file mode 100644 index 0000000000..6d6d04d267 --- /dev/null +++ b/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 : DbContextEventInbox , IOracleDbContextEventInbox + where TDbContext : IHasEventInbox + { + public OracleDbContextEventInbox( + IDbContextProvider dbContextProvider, + IClock clock, + IOptions 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(); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs new file mode 100644 index 0000000000..a5c4566ced --- /dev/null +++ b/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 : DbContextEventOutbox , IOracleDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + public OracleDbContextEventOutbox(IDbContextProvider 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(); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs new file mode 100644 index 0000000000..ca79019e28 --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs new file mode 100644 index 0000000000..e2d7d33761 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs index b7cbaec1a1..7716ae6150 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs +++ b/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<>)); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs new file mode 100644 index 0000000000..5c24d79f88 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface IPostgreSqlDbContextEventInbox : IDbContextEventInbox + where TDbContext : IHasEventInbox + { + + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs new file mode 100644 index 0000000000..7e6bc4bd59 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface IPostgreSqlDbContextEventOutbox : IDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs new file mode 100644 index 0000000000..fedff90dec --- /dev/null +++ b/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 : DbContextEventInbox, IPostgreSqlDbContextEventInbox + where TDbContext : IHasEventInbox + { + public PostgreSqlDbContextEventInbox( + IDbContextProvider dbContextProvider, + IClock clock, + IOptions 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); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs new file mode 100644 index 0000000000..c5e79a0014 --- /dev/null +++ b/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 : DbContextEventOutbox , IPostgreSqlDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + public PostgreSqlDbContextEventOutbox(IDbContextProvider 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); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs new file mode 100644 index 0000000000..f4bb462a1c --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs new file mode 100644 index 0000000000..853ae9ba59 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs index 4e76b89113..55c1a4af42 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs +++ b/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<>)); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs new file mode 100644 index 0000000000..60adf600c7 --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs new file mode 100644 index 0000000000..9022d5c7e6 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs new file mode 100644 index 0000000000..ccc92d4eb1 --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs new file mode 100644 index 0000000000..c1d9949c19 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs index c330beb281..fb32e5a21d 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs @@ -17,7 +17,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore { - public class EfCoreRepository : RepositoryBase, IEfCoreRepository, IAsyncEnumerable + public class EfCoreRepository : RepositoryBase, IEfCoreRepository where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { @@ -381,12 +381,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore return query; } - [Obsolete("This method will be deleted in future versions.")] - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - { - return DbSet.AsAsyncEnumerable().GetAsyncEnumerator(cancellationToken); - } - protected virtual void CheckAndSetId(TEntity entity) { if (entity is IEntity entityWithGuidId) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 588b052140..3ac150fe32 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -61,9 +61,9 @@ namespace Volo.Abp.EntityFrameworkCore public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService(); public IClock Clock => LazyServiceProvider.LazyGetRequiredService(); - + public IDistributedEventBus DistributedEventBus => LazyServiceProvider.LazyGetRequiredService(); - + public ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService(); public ILogger> Logger => LazyServiceProvider.LazyGetService>>(NullLogger>.Instance); @@ -155,10 +155,6 @@ namespace Volo.Abp.EntityFrameworkCore { try { - ApplyAbpConcepts(); - - var eventReport = CreateEventReport(); - var auditLog = AuditingManager?.Current?.Log; List entityChangeList = null; if (auditLog != null) @@ -166,10 +162,14 @@ namespace Volo.Abp.EntityFrameworkCore entityChangeList = EntityHistoryHelper.CreateChangeList(ChangeTracker.Entries().ToList()); } - var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + HandlePropertiesBeforeSave(); - PublishEntityEvents(eventReport); + var eventReport = CreateEventReport(); + var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + + PublishEntityEvents(eventReport); + if (entityChangeList != null) { EntityHistoryHelper.UpdateChangeList(entityChangeList); @@ -285,16 +285,18 @@ namespace Volo.Abp.EntityFrameworkCore } } } - + private void PublishEventsForTrackedEntity(EntityEntry entry) { 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().IsDeleted) @@ -308,27 +310,33 @@ namespace Volo.Abp.EntityFrameworkCore EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity); } } - + 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); + } } } - + protected virtual EntityEventReport CreateEventReport() { var eventReport = new EntityEventReport(); - + foreach (var entry in ChangeTracker.Entries().ToList()) { var generatesDomainEventsEntity = entry.Entity as IGeneratesDomainEvents; @@ -369,24 +377,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 +463,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().IsDeleted) @@ -485,11 +474,19 @@ namespace Volo.Abp.EntityFrameworkCore protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry) { - if (TryCancelDeletionForSoftDelete(entry)) + if (!(entry.Entity is ISoftDelete)) { - UpdateConcurrencyStamp(entry); - SetDeletionAuditProperties(entry); + return; } + + if (IsHardDeleted(entry)) + { + return; + } + + entry.Reload(); + entry.Entity.As().IsDeleted = true; + entry.State = EntityState.Modified; } protected virtual bool IsHardDeleted(EntityEntry entry) @@ -510,7 +507,7 @@ namespace Volo.Abp.EntityFrameworkCore { return; } - + Entry(entity).Property(x => x.ConcurrencyStamp).OriginalValue = entity.ConcurrencyStamp; entity.ConcurrencyStamp = Guid.NewGuid().ToString("N"); } @@ -531,24 +528,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().IsDeleted = true; - return true; - } - protected virtual void CheckAndSetId(EntityEntry entry) { if (entry.Entity is IEntity entityWithGuidId) @@ -638,7 +617,7 @@ namespace Volo.Abp.EntityFrameworkCore !typeof(TEntity).IsDefined(typeof(OwnedAttribute), true) && !mutableEntityType.IsOwned()) { - if (LazyServiceProvider == null || Clock == null || !Clock.SupportsMultipleTimezone) + if (LazyServiceProvider == null || Clock == null) { return; } @@ -650,7 +629,7 @@ namespace Volo.Abp.EntityFrameworkCore (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?)) && property.CanWrite && - !property.IsDefined(typeof(DisableDateTimeNormalizationAttribute), true) + ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(property) == null ).ToList(); dateTimePropertyInfos.ForEach(property => diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs index b5d9eb8436..96c245be2e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs +++ b/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<>)); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs new file mode 100644 index 0000000000..242b601177 --- /dev/null +++ b/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 : IDbContextEventInbox + where TDbContext : IHasEventInbox + { + protected IDbContextProvider DbContextProvider { get; } + protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; } + protected IClock Clock { get; } + + public DbContextEventInbox( + IDbContextProvider dbContextProvider, + IClock clock, + IOptions 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> 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 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); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs new file mode 100644 index 0000000000..0b8909b966 --- /dev/null +++ b/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 : IDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + protected IDbContextProvider DbContextProvider { get; } + + public DbContextEventOutbox( + IDbContextProvider 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> 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); + } + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreInboxConfigExtensions.cs new file mode 100644 index 0000000000..182c7b20ba --- /dev/null +++ b/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(this InboxConfig outboxConfig) + where TDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(IDbContextEventInbox); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EfCoreOutboxConfigExtensions.cs new file mode 100644 index 0000000000..53745477c7 --- /dev/null +++ b/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(this OutboxConfig outboxConfig) + where TDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(IDbContextEventOutbox); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventInboxDbContextModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventInboxDbContextModelBuilderExtensions.cs new file mode 100644 index 0000000000..29c9f0042c --- /dev/null +++ b/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(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); + }); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventOutboxDbContextModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/EventOutboxDbContextModelBuilderExtensions.cs new file mode 100644 index 0000000000..a8236ac43a --- /dev/null +++ b/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(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); + }); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventInbox.cs new file mode 100644 index 0000000000..81b51bc11d --- /dev/null +++ b/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 : IEventInbox + where TDbContext : IHasEventInbox + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IDbContextEventOutbox.cs new file mode 100644 index 0000000000..71d65c7f23 --- /dev/null +++ b/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 : IEventOutbox + where TDbContext : IHasEventOutbox + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventInbox.cs new file mode 100644 index 0000000000..f6836a0c19 --- /dev/null +++ b/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 IncomingEvents { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IHasEventOutbox.cs new file mode 100644 index 0000000000..5e2934d591 --- /dev/null +++ b/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 OutgoingEvents { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs new file mode 100644 index 0000000000..d86e3f36ed --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface ISqlRawDbContextEventInbox : IDbContextEventInbox + where TDbContext : IHasEventInbox + { + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs new file mode 100644 index 0000000000..776cc2f93c --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.EntityFrameworkCore.DistributedEvents +{ + public interface ISqlRawDbContextEventOutbox : IDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IncomingEventRecord.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/IncomingEventRecord.cs new file mode 100644 index 0000000000..a1f971bbaa --- /dev/null +++ b/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, + 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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/OutgoingEventRecord.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/OutgoingEventRecord.cs new file mode 100644 index 0000000000..727216de48 --- /dev/null +++ b/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, + 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 + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs new file mode 100644 index 0000000000..cb764ede17 --- /dev/null +++ b/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 : DbContextEventInbox , ISqlRawDbContextEventInbox + where TDbContext : IHasEventInbox + { + public SqlRawDbContextEventInbox( + IDbContextProvider dbContextProvider, + IClock clock, + IOptions 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); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs new file mode 100644 index 0000000000..8748e6dc09 --- /dev/null +++ b/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 : DbContextEventOutbox , ISqlRawDbContextEventOutbox + where TDbContext : IHasEventOutbox + { + public SqlRawDbContextEventOutbox(IDbContextProvider 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); + } + } +} diff --git a/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xml b/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xsd b/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Boxes/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo.Abp.EventBus.Boxes.csproj b/framework/src/Volo.Abp.EventBus.Boxes/Volo.Abp.EventBus.Boxes.csproj new file mode 100644 index 0000000000..6cbaf62aed --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Boxes/Volo.Abp.EventBus.Boxes.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + Volo.Abp.EventBus.Boxes + Volo.Abp.EventBus.Boxes + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpDistributedEventBusExtensions.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpDistributedEventBusExtensions.cs new file mode 100644 index 0000000000..a943f6faa7 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesModule.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesModule.cs new file mode 100644 index 0000000000..8e5eeb2c60 --- /dev/null +++ b/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(); + context.AddBackgroundWorker(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesOptions.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/AbpEventBusBoxesOptions.cs new file mode 100644 index 0000000000..1d72666c42 --- /dev/null +++ b/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 + { + /// + /// Default: 6 hours + /// + public TimeSpan CleanOldEventTimeIntervalSpan { get; set; } + + /// + /// Default: 1000 + /// + public int InboxWaitingEventMaxCount { get; set; } + + /// + /// Default: 1000 + /// + public int OutboxWaitingEventMaxCount { get; set; } + + /// + /// Period time of and + /// Default: 2 seconds + /// + public TimeSpan PeriodTimeSpan { get; set; } + + /// + /// Default: 15 seconds + /// + public TimeSpan DistributedLockWaitDuration { get; set; } + + /// + /// Default: 2 hours + /// + 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); + } + } +} diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IInboxProcessor.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IInboxProcessor.cs new file mode 100644 index 0000000000..e93ff0cc2e --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IOutboxSender.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/IOutboxSender.cs new file mode 100644 index 0000000000..4a700eb823 --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessManager.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessManager.cs new file mode 100644 index 0000000000..310cad2886 --- /dev/null +++ b/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 Processors { get; } + + public InboxProcessManager( + IOptions options, + IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + Options = options.Value; + Processors = new List(); + } + + public async Task StartAsync(CancellationToken cancellationToken = default) + { + foreach (var inboxConfig in Options.Inboxes.Values) + { + if (inboxConfig.IsProcessingEnabled) + { + var processor = ServiceProvider.GetRequiredService(); + await processor.StartAsync(inboxConfig, cancellationToken); + Processors.Add(processor); + } + } + } + + public async Task StopAsync(CancellationToken cancellationToken = default) + { + foreach (var processor in Processors) + { + await processor.StopAsync(cancellationToken); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessor.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/InboxProcessor.cs new file mode 100644 index 0000000000..e3c71771ad --- /dev/null +++ b/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 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 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.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; + } + } +} diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSender.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSender.cs new file mode 100644 index 0000000000..32545227c3 --- /dev/null +++ b/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 Logger { get; set; } + + protected CancellationTokenSource StoppingTokenSource { get; } + protected CancellationToken StoppingToken { get; } + + public OutboxSender( + IServiceProvider serviceProvider, + AbpAsyncTimer timer, + IDistributedEventBus distributedEventBus, + IDistributedLockProvider distributedLockProvider, + IOptions eventBusBoxesOptions) + { + ServiceProvider = serviceProvider; + Timer = timer; + DistributedEventBus = distributedEventBus; + DistributedLockProvider = distributedLockProvider; + EventBusBoxesOptions = eventBusBoxesOptions.Value; + Timer.Period = EventBusBoxesOptions.PeriodTimeSpan.Milliseconds; + Timer.Elapsed += TimerOnElapsed; + Logger = NullLogger.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); + } + } + } + } +} diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSenderManager.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/OutboxSenderManager.cs new file mode 100644 index 0000000000..6d403bd243 --- /dev/null +++ b/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 Senders { get; } + + public OutboxSenderManager( + IOptions options, + IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + Options = options.Value; + Senders = new List(); + } + + public async Task StartAsync(CancellationToken cancellationToken = default) + { + foreach (var outboxConfig in Options.Outboxes.Values) + { + if (outboxConfig.IsSendingEnabled) + { + var sender = ServiceProvider.GetRequiredService(); + await sender.StartAsync(outboxConfig, cancellationToken); + Senders.Add(sender); + } + } + } + + public async Task StopAsync(CancellationToken cancellationToken = default) + { + foreach (var sender in Senders) + { + await sender.StopAsync(cancellationToken); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/TaskHelper.cs b/framework/src/Volo.Abp.EventBus.Boxes/Volo/Abp/EventBus/Boxes/TaskHelper.cs new file mode 100644 index 0000000000..87abe18da8 --- /dev/null +++ b/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; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs index 0be3a326b6..b747a858d2 100644 --- a/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs +++ b/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) - : base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler) + IOptions 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(IDistributedEventHandler 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(); + 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 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 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 headersArguments) + private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers, Dictionary 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 headersArguments) + { var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName); SetEventMessageHeaders(headers, headersArguments); diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs index f5e3682a7a..ceba890234 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs +++ b/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) - : base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler) + IOptions 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>(); @@ -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(IDistributedEventHandler 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(); + 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 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 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( diff --git a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/IRebusSerializer.cs b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/IRebusSerializer.cs new file mode 100644 index 0000000000..68700b4da5 --- /dev/null +++ b/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(byte[] value); + } +} diff --git a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs index 0206b5fefc..1cb380679f 100644 --- a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs +++ b/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 may not be thread-safe! protected ConcurrentDictionary> HandlerFactories { get; } protected ConcurrentDictionary 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, IOptions 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>(); EventTypes = new ConcurrentDictionary(); @@ -122,9 +135,17 @@ namespace Volo.Abp.EventBus.Rebus Rebus.Unsubscribe(eventType); } - public IDisposable Subscribe(IDistributedEventHandler 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(); + await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig); + if (exceptions.Any()) + { + ThrowOriginalExceptions(eventType, exceptions); + } + } + + protected override byte[] Serialize(object eventData) + { + return Serializer.Serialize(eventData); + } } } diff --git a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventHandlerAdapter.cs b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventHandlerAdapter.cs index 005226b885..61330828b4 100644 --- a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventHandlerAdapter.cs +++ b/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); } } } diff --git a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/Utf8JsonRabbitMqSerializer.cs b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/Utf8JsonRabbitMqSerializer.cs new file mode 100644 index 0000000000..515e616aab --- /dev/null +++ b/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(byte[] value) + { + return _jsonSerializer.Deserialize(Encoding.UTF8.GetString(value)); + } + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj index 261b10d8ae..5de5b0893f 100644 --- a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj +++ b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj @@ -16,6 +16,7 @@ + diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index 329c26c93f..bd86f14703 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/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) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs index 342e7b77bb..ab8fe6823b 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusOptions.cs @@ -6,9 +6,14 @@ namespace Volo.Abp.EventBus.Distributed { public ITypeList Handlers { get; } + public OutboxConfigDictionary Outboxes { get; } + + public InboxConfigDictionary Inboxes { get; } public AbpDistributedEventBusOptions() { Handlers = new TypeList(); + Outboxes = new OutboxConfigDictionary(); + Inboxes = new InboxConfigDictionary(); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs new file mode 100644 index 0000000000..70c5bd5533 --- /dev/null +++ b/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, + IGuidGenerator guidGenerator, + IClock clock + ) : base( + serviceScopeFactory, + currentTenant, + unitOfWorkManager, + errorHandler) + { + GuidGenerator = guidGenerator; + Clock = clock; + AbpDistributedEventBusOptions = abpDistributedEventBusOptions.Value; + } + + public IDisposable Subscribe(IDistributedEventHandler 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 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 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 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); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs index e406f7069b..80af122242 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs +++ b/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 /// Object to handle the event IDisposable Subscribe(IDistributedEventHandler handler) where TEvent : class; + + Task PublishAsync( + TEvent eventData, + bool onUnitOfWorkComplete = true, + bool useOutbox = true) + where TEvent : class; + + Task PublishAsync( + Type eventType, + object eventData, + bool onUnitOfWorkComplete = true, + bool useOutbox = true); } } diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventInbox.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventInbox.cs new file mode 100644 index 0000000000..d072f5d922 --- /dev/null +++ b/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> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default); + + Task MarkAsProcessedAsync(Guid id); + + Task ExistsByMessageIdAsync(string messageId); + + Task DeleteOldEventsAsync(); + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventOutbox.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IEventOutbox.cs new file mode 100644 index 0000000000..cc00ee6654 --- /dev/null +++ b/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> GetWaitingEventsAsync(int maxCount, CancellationToken cancellationToken = default); + + Task DeleteAsync(Guid id); + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/ISupportsEventBoxes.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/ISupportsEventBoxes.cs new file mode 100644 index 0000000000..7a13696bc0 --- /dev/null +++ b/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 + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfig.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfig.cs new file mode 100644 index 0000000000..5f708fdb6f --- /dev/null +++ b/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 EventSelector { get; set; } + + public Func HandlerSelector { get; set; } + + /// + /// Used to enable/disable processing incoming events. + /// Default: true. + /// + public bool IsProcessingEnabled { get; set; } = true; + + public InboxConfig([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfigDictionary.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxConfigDictionary.cs new file mode 100644 index 0000000000..9909181138 --- /dev/null +++ b/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 + { + public void Configure(Action configAction) + { + Configure("Default", configAction); + } + + public void Configure(string outboxName, Action configAction) + { + var outboxConfig = this.GetOrAdd(outboxName, () => new InboxConfig(outboxName)); + configAction(outboxConfig); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IncomingEventInfo.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IncomingEventInfo.cs new file mode 100644 index 0000000000..571c62602b --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs index 3ee3700a0d..d5dd961802 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs +++ b/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 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); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullDistributedEventBus.cs index 60be7a147c..97004fbeac 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullDistributedEventBus.cs +++ b/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 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; + } } } diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfig.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfig.cs new file mode 100644 index 0000000000..be7630ddee --- /dev/null +++ b/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 Selector { get; set; } + + /// + /// Used to enable/disable sending events from outbox to the message broker. + /// Default: true. + /// + public bool IsSendingEnabled { get; set; } = true; + + public OutboxConfig([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfigDictionary.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxConfigDictionary.cs new file mode 100644 index 0000000000..be5b838363 --- /dev/null +++ b/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 + { + public void Configure(Action configAction) + { + Configure("Default", configAction); + } + + public void Configure(string outboxName, Action configAction) + { + var outboxConfig = this.GetOrAdd(outboxName, () => new OutboxConfig(outboxName)); + configAction(outboxConfig); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutgoingEventInfo.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutgoingEventInfo.cs new file mode 100644 index 0000000000..643db882d2 --- /dev/null +++ b/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(); + } + }} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index f29a169cb5..694166b1bc 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -92,13 +92,17 @@ namespace Volo.Abp.EventBus public abstract void UnsubscribeAll(Type eventType); /// - public Task PublishAsync(TEvent eventData, bool onUnitOfWorkComplete = true) where TEvent : class + public Task PublishAsync(TEvent eventData, bool onUnitOfWorkComplete = true) + where TEvent : class { return PublishAsync(typeof(TEvent), eventData, onUnitOfWorkComplete); } /// - 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 exceptions) + protected virtual async Task TriggerHandlersAsync(Type eventType, object eventData, List 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 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 handlers) { @@ -182,7 +199,8 @@ namespace Volo.Abp.EventBus protected abstract IEnumerable GetHandlerFactories(Type eventType); - protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List exceptions) + protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, + object eventData, List 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<>))) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/UnitOfWorkEventPublisher.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/UnitOfWorkEventPublisher.cs index 65e271afa8..ea6a66a699 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/UnitOfWorkEventPublisher.cs +++ b/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 + ); } } } diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs index cf3479de67..3193e15ad1 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs +++ b/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 ) ); } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index a40117e0ff..5f57ef25d0 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using MongoDB.Driver; using MongoDB.Driver.Linq; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; @@ -23,8 +24,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB { public class MongoDbRepository : RepositoryBase, - IMongoDbRepository, - IMongoQueryable + IMongoDbRepository where TMongoDbContext : IAbpMongoDbContext where TEntity : class, IEntity { @@ -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) @@ -720,24 +721,6 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return aggregate; } - - [Obsolete("This method will be removed in future versions.")] - public QueryableExecutionModel GetExecutionModel() - { - return GetMongoQueryable().GetExecutionModel(); - } - - [Obsolete("This method will be removed in future versions.")] - public IAsyncCursor ToCursor(CancellationToken cancellationToken = new CancellationToken()) - { - return GetMongoQueryable().ToCursor(GetCancellationToken(cancellationToken)); - } - - [Obsolete("This method will be removed in future versions.")] - public Task> ToCursorAsync(CancellationToken cancellationToken = new CancellationToken()) - { - return GetMongoQueryable().ToCursorAsync(GetCancellationToken(cancellationToken)); - } } public class MongoDbRepository diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs index 5e7eb84111..93633e3cc8 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs @@ -6,6 +6,8 @@ namespace Volo.Abp.MongoDB { public abstract class AbpMongoDbContext : IAbpMongoDbContext, ITransientDependency { + public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + public IMongoModelSource ModelSource { get; set; } public IMongoClient Client { get; private set; } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbDateTimeSerializer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbDateTimeSerializer.cs new file mode 100644 index 0000000000..c3e9efba5a --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbDateTimeSerializer.cs @@ -0,0 +1,43 @@ +using System; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; + +namespace Volo.Abp.MongoDB +{ + public class AbpMongoDbDateTimeSerializer : DateTimeSerializer + { + protected DateTimeKind DateTimeKind { get; set; } + protected bool DisableDateTimeNormalization{ get; set; } + + public AbpMongoDbDateTimeSerializer(DateTimeKind dateTimeKind , bool disableDateTimeNormalization) + { + DateTimeKind = dateTimeKind; + DisableDateTimeNormalization = disableDateTimeNormalization; + } + + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value) + { + context.Writer.WriteDateTime(DisableDateTimeNormalization + ? ToMillisecondsSinceEpoch(value) + : ToMillisecondsSinceEpoch(DateTime.SpecifyKind(value, DateTimeKind))); + } + + public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var dateTime = new BsonDateTime(context.Reader.ReadDateTime()).ToUniversalTime(); + return DateTime.SpecifyKind(dateTime, DisableDateTimeNormalization ? DateTimeKind.Unspecified : DateTimeKind); + } + + private static long ToMillisecondsSinceEpoch(DateTime dateTime) + { + return (dateTime - BsonConstants.UnixEpoch).Ticks / 10000L; + } + + // For unit testing. + internal void SetDateTimeKind(DateTimeKind dateTimeKind) + { + DateTimeKind = dateTimeKind; + } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs index d32f015a33..7598066579 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs +++ b/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<>) + ); } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbOptions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbOptions.cs new file mode 100644 index 0000000000..21502e0cf8 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbOptions.cs @@ -0,0 +1,18 @@ +using Volo.Abp.Timing; + +namespace Volo.Abp.MongoDB +{ + public class AbpMongoDbOptions + { + /// + /// Serializer the datetime based on in MongoDb. + /// Default: true. + /// + public bool UseAbpClockHandleDateTime { get; set; } + + public AbpMongoDbOptions() + { + UseAbpClockHandleDateTime = true; + } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventInbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventInbox.cs new file mode 100644 index 0000000000..792a85e124 --- /dev/null +++ b/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 IncomingEvents { get; } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventOutbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IHasEventOutbox.cs new file mode 100644 index 0000000000..ab4bc10f35 --- /dev/null +++ b/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 OutgoingEvents { get; } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventInbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventInbox.cs new file mode 100644 index 0000000000..71e0d14562 --- /dev/null +++ b/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 : IEventInbox + where TDbContext : IHasEventInbox + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventOutbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IMongoDbContextEventOutbox.cs new file mode 100644 index 0000000000..ddb9cd19c5 --- /dev/null +++ b/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 : IEventOutbox + where TDbContext : IHasEventOutbox + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IncomingEventRecord.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/IncomingEventRecord.cs new file mode 100644 index 0000000000..5e9d07c33f --- /dev/null +++ b/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, + 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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs new file mode 100644 index 0000000000..3cf5485512 --- /dev/null +++ b/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 : IMongoDbContextEventInbox + where TMongoDbContext : IHasEventInbox + { + protected IMongoDbContextProvider DbContextProvider { get; } + protected AbpEventBusBoxesOptions EventBusBoxesOptions { get; } + protected IClock Clock { get; } + + public MongoDbContextEventInbox( + IMongoDbContextProvider dbContextProvider, + IClock clock, + IOptions 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> 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.Filter.Eq(x => x.Id, id); + var update = Builders.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 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); + } + } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventOutbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventOutbox.cs new file mode 100644 index 0000000000..dad0294d1c --- /dev/null +++ b/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 : IMongoDbContextEventOutbox + where TMongoDbContext : IHasEventOutbox + { + protected IMongoDbContextProvider MongoDbContextProvider { get; } + + public MongoDbContextEventOutbox(IMongoDbContextProvider 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> 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)); + } + } + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbInboxConfigExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbInboxConfigExtensions.cs new file mode 100644 index 0000000000..27be4a078b --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbInboxConfigExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EventBus.Distributed; + +namespace Volo.Abp.MongoDB.DistributedEvents +{ + public static class MongoDbInboxConfigExtensions + { + public static void UseMongoDbContext(this InboxConfig outboxConfig) + where TMongoDbContext : IHasEventInbox + { + outboxConfig.ImplementationType = typeof(IMongoDbContextEventInbox); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbOutboxConfigExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbOutboxConfigExtensions.cs new file mode 100644 index 0000000000..6da1513451 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbOutboxConfigExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EventBus.Distributed; + +namespace Volo.Abp.MongoDB.DistributedEvents +{ + public static class MongoDbOutboxConfigExtensions + { + public static void UseMongoDbContext(this OutboxConfig outboxConfig) + where TMongoDbContext : IHasEventOutbox + { + outboxConfig.ImplementationType = typeof(IMongoDbContextEventOutbox); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/OutgoingEventRecord.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/OutgoingEventRecord.cs new file mode 100644 index 0000000000..c38de0cd91 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/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.MongoDB.DistributedEvents +{ + public class OutgoingEventRecord : + BasicAggregateRoot, + 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 + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs index 6d8706d7ee..03f3b52572 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs @@ -3,9 +3,13 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Reflection; +using Microsoft.Extensions.Options; using MongoDB.Bson; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; -using Volo.Abp.Domain.Entities; +using Volo.Abp.Reflection; +using Volo.Abp.Timing; namespace Volo.Abp.MongoDB { @@ -20,10 +24,12 @@ namespace Volo.Abp.MongoDB _entityModelBuilders = new Dictionary(); } - public MongoDbContextModel Build(AbpMongoDbContext dbContext) + public virtual MongoDbContextModel Build(AbpMongoDbContext dbContext) { lock (SyncObj) { + var useAbpClockHandleDateTime = dbContext.LazyServiceProvider.LazyGetRequiredService>().Value.UseAbpClockHandleDateTime; + var entityModels = _entityModelBuilders .Select(x => x.Value) .Cast() @@ -34,6 +40,33 @@ namespace Volo.Abp.MongoDB foreach (var entityModel in entityModels.Values) { var map = entityModel.As().GetMap(); + + if (useAbpClockHandleDateTime) + { + var dateTimePropertyInfos = entityModel.EntityType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .Where(property => + (property.PropertyType == typeof(DateTime) || + property.PropertyType == typeof(DateTime?)) && + property.CanWrite + ).ToList(); + + dateTimePropertyInfos.ForEach(property => + { + var disableDateTimeNormalization = + entityModel.EntityType.IsDefined(typeof(DisableDateTimeNormalizationAttribute), true) || + ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(property) != null; + + if (property.PropertyType == typeof(DateTime?)) + { + map.MapProperty(property.Name).SetSerializer(new NullableSerializer().WithSerializer(new AbpMongoDbDateTimeSerializer(GetDateTimeKind(dbContext), disableDateTimeNormalization))); + } + else + { + map.MapProperty(property.Name).SetSerializer(new AbpMongoDbDateTimeSerializer(GetDateTimeKind(dbContext), disableDateTimeNormalization)); + } + }); + } + if (!BsonClassMap.IsClassMapRegistered(map.ClassType)) { BsonClassMap.RegisterClassMap(map); @@ -52,6 +85,29 @@ namespace Volo.Abp.MongoDB { var map = new BsonClassMap(baseClass); map.ConfigureAbpConventions(); + + if (useAbpClockHandleDateTime) + { + var dateTimePropertyInfos = baseClass.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .Where(property => + (property.PropertyType == typeof(DateTime) || + property.PropertyType == typeof(DateTime?)) && + property.CanWrite + ).ToList(); + + dateTimePropertyInfos.ForEach(property => + { + if (property.PropertyType == typeof(DateTime?)) + { + map.MapProperty(property.Name).SetSerializer(new NullableSerializer().WithSerializer(new AbpMongoDbDateTimeSerializer(GetDateTimeKind(dbContext), false))); + } + else + { + map.MapProperty(property.Name).SetSerializer(new AbpMongoDbDateTimeSerializer(GetDateTimeKind(dbContext), false)); + } + }); + } + BsonClassMap.RegisterClassMap(map); } } @@ -60,6 +116,11 @@ namespace Volo.Abp.MongoDB } } + private DateTimeKind GetDateTimeKind(AbpMongoDbContext dbContext) + { + return dbContext.LazyServiceProvider.LazyGetRequiredService>().Value.Kind; + } + public virtual void Entity(Action> buildAction = null) { var model = (IMongoEntityModelBuilder)_entityModelBuilders.GetOrAdd( diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs index e7e6efd5de..d95fc7012b 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs @@ -96,7 +96,7 @@ namespace Volo.Abp.Threading try { - Elapsed.InvokeSafely(this, new EventArgs()); + Elapsed.InvokeSafely(this, EventArgs.Empty); } catch(Exception ex) { diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs index 11ae4920cd..cba5646f7a 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs @@ -11,6 +11,8 @@ namespace Volo.Abp.Uow public long EventOrder { get; } + public bool UseOutbox { get; } + /// /// Extra properties can be used if needed. /// @@ -19,11 +21,13 @@ namespace Volo.Abp.Uow public UnitOfWorkEventRecord( Type eventType, object eventData, - long eventOrder) + long eventOrder, + bool useOutbox = true) { EventType = eventType; EventData = eventData; EventOrder = eventOrder; + UseOutbox = useOutbox; } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs index fcce316772..8ae0cfd9cb 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding { public abstract class ModelBindingController_Tests : AspNetCoreMvcTestBase { - protected DateTimeKind DateTimeKind { get; set; } + protected DateTimeKind Kind { get; set; } protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) { @@ -34,7 +34,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding response.StatusCode.ShouldBe(HttpStatusCode.OK); var resultAsString = await response.Content.ReadAsStringAsync(); - resultAsString.ShouldBe(DateTimeKind.ToString().ToLower()); + resultAsString.ShouldBe(Kind.ToString().ToLower()); } [Fact] @@ -45,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding response.StatusCode.ShouldBe(HttpStatusCode.OK); var resultAsString = await response.Content.ReadAsStringAsync(); - resultAsString.ShouldBe(DateTimeKind.ToString().ToLower()); + resultAsString.ShouldBe(Kind.ToString().ToLower()); } [Fact] @@ -89,7 +89,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding var resultAsString = await response.Content.ReadAsStringAsync(); //Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is UTC //https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50?view=aspnetcore-3.1&tabs=visual-studio#datetime-values-are-model-bound-as-utc-times - resultAsString.ShouldBe($"utc_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_utc"); + resultAsString.ShouldBe($"utc_{Kind.ToString().ToLower()}_{Kind.ToString().ToLower()}_utc"); } [Fact] @@ -111,7 +111,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding response.StatusCode.ShouldBe(HttpStatusCode.OK); var resultAsString = await response.Content.ReadAsStringAsync(); - resultAsString.ShouldBe($"local_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_local"); + resultAsString.ShouldBe($"local_{Kind.ToString().ToLower()}_{Kind.ToString().ToLower()}_local"); } } @@ -119,8 +119,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding { protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) { - DateTimeKind = DateTimeKind.Utc; - services.Configure(x => x.Kind = DateTimeKind); + Kind = DateTimeKind.Utc; + services.Configure(x => x.Kind = Kind); base.ConfigureServices(context, services); } @@ -130,8 +130,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding { protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) { - DateTimeKind = DateTimeKind.Local; - services.Configure(x => x.Kind = DateTimeKind); + Kind = DateTimeKind.Local; + services.Configure(x => x.Kind = Kind); base.ConfigureServices(context, services); } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index 4f8faef65e..49713b3c1d 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -13,7 +12,6 @@ using Volo.Abp.Modularity; using Volo.Abp.TestApp; using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.EntityFrameworkCore; -using Volo.Abp.Timing; namespace Volo.Abp.EntityFrameworkCore { @@ -55,8 +53,6 @@ namespace Volo.Abp.EntityFrameworkCore abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); }); }); - - Configure(options => options.Kind = DateTimeKind.Utc); } public override void OnPreApplicationInitialization(ApplicationInitializationContext context) diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs index 9a6c0817f2..93e0de6639 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs @@ -24,28 +24,31 @@ namespace Volo.Abp.EntityFrameworkCore } [Fact] - public void Should_Accept_EfCore_Related_Queries() + public async Task Should_Accept_EfCore_Related_Queries() { - var query = _personRepository.Where(p => p.Age > 0); - - _efCoreAsyncQueryableProvider.CanExecute(query).ShouldBeTrue(); + using ( _ = _unitOfWorkManager.Begin()) + { + var query = (await _personRepository.GetQueryableAsync()).Where(p => p.Age > 0); + + _efCoreAsyncQueryableProvider.CanExecute(query).ShouldBeTrue(); + } } [Fact] public void Should_Not_Accept_Other_Providers() { var query = new[] {1, 2, 3}.AsQueryable().Where(x => x > 0); - + _efCoreAsyncQueryableProvider.CanExecute(query).ShouldBeFalse(); } - + [Fact] public async Task Should_Execute_Queries() { using (var uow = _unitOfWorkManager.Begin()) { - var query = _personRepository.Where(p => p.Age > 0); - + var query = (await _personRepository.GetQueryableAsync()).Where(p => p.Age > 0); + (await _efCoreAsyncQueryableProvider.CountAsync(query) > 0).ShouldBeTrue(); (await _efCoreAsyncQueryableProvider.FirstOrDefaultAsync(query)).ShouldNotBeNull(); (await _efCoreAsyncQueryableProvider.ToListAsync(query)).Count.ShouldBeGreaterThan(0); @@ -54,4 +57,4 @@ namespace Volo.Abp.EntityFrameworkCore } } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Basic_Tests.cs index 3d5d7b656b..ab3217b9f3 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Basic_Tests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Shouldly; +using Volo.Abp.Domain.Repositories; using Volo.Abp.TestApp.Testing; using Xunit; diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Queryable_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Queryable_Tests.cs index a5b8aa0577..8149cdbe32 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Queryable_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Queryable_Tests.cs @@ -26,9 +26,9 @@ namespace Volo.Abp.EntityFrameworkCore.Repositories [Fact] public async Task GetBookList() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - _bookRepository.Any().ShouldBeTrue(); + (await _bookRepository.AnyAsync()).ShouldBeTrue(); return Task.CompletedTask; }); } @@ -36,9 +36,9 @@ namespace Volo.Abp.EntityFrameworkCore.Repositories [Fact] public async Task GetPhoneInSecondDbContextList() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - _phoneInSecondDbContextRepository.Any().ShouldBeTrue(); + (await _phoneInSecondDbContextRepository.AnyAsync()).ShouldBeTrue(); return Task.CompletedTask; }); } @@ -46,9 +46,9 @@ namespace Volo.Abp.EntityFrameworkCore.Repositories [Fact] public async Task EfCore_Include_Extension() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - var person = PersonRepository.Include(p => p.Phones).Single(p => p.Id == TestDataBuilder.UserDouglasId); + var person = await (await PersonRepository.GetDbSetAsync()).Include(p => p.Phones).SingleAsync(p => p.Id == TestDataBuilder.UserDouglasId); person.Name.ShouldBe("Douglas"); person.Phones.Count.ShouldBe(2); return Task.CompletedTask; diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/UnitTestModelCacheKeyFactory.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/UnitTestModelCacheKeyFactory.cs new file mode 100644 index 0000000000..0686bfb614 --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/UnitTestModelCacheKeyFactory.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Volo.Abp.EntityFrameworkCore +{ + /// + /// Avoid unit test caching the configure of the entity. + /// OnModelCreating will be executed multiple times + /// + public class UnitTestModelCacheKeyFactory : IModelCacheKeyFactory + { + public object Create(DbContext context) + { + return context; + } + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpDateTimeValueConverter_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpDateTimeValueConverter_Tests.cs deleted file mode 100644 index c2793b3b67..0000000000 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpDateTimeValueConverter_Tests.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Volo.Abp.TestApp.Testing; - -namespace Volo.Abp.EntityFrameworkCore.ValueConverters -{ - public class AbpDateTimeValueConverter_Tests : AbpDateTimeValueConverter_Tests - { - - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/EFCore_DateTimeKindTests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/EFCore_DateTimeKindTests.cs new file mode 100644 index 0000000000..8a39819a2e --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ValueConverters/EFCore_DateTimeKindTests.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.Testing; +using Volo.Abp.Timing; +using Xunit; + +namespace Volo.Abp.EntityFrameworkCore.ValueConverters +{ + public abstract class EFCore_DateTimeKindTests : DateTimeKind_Tests + { + [Fact] + public async Task DateTime_Kind_Should_Be_Normalized_In_View_Query_Test() + { + var personName = "bob lee"; + await PersonRepository.InsertAsync(new Person(Guid.NewGuid(), personName, 18) + { + Birthday = DateTime.Parse("2020-01-01 00:00:00"), + LastActive = DateTime.Parse("2020-01-01 00:00:00"), + }, true); + + var person = await PersonRepository.GetViewAsync(personName); + + person.ShouldNotBeNull(); + person.CreationTime.Kind.ShouldBe(Kind); + + person.Birthday.ShouldNotBeNull(); + person.Birthday.Value.Kind.ShouldBe(Kind); + person.Birthday.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); + + //LastActive DisableDateTimeNormalization + person.LastActive.ShouldNotBeNull(); + person.LastActive.Value.Kind.ShouldBe(DateTimeKind.Unspecified); + person.LastActive.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); + } + } + + public class DateTimeKindTests : EFCore_DateTimeKindTests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Unspecified; + services.Configure(x => x.Kind = Kind); + } + } + + public class DateTimeKindTests_Local : EFCore_DateTimeKindTests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Local; + services.Configure(x => x.Kind = Kind); + } + } + + public class DateTimeKindTests_Utc : EFCore_DateTimeKindTests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Utc; + services.Configure(x => x.Kind = Kind); + } + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/CityRepository.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/CityRepository.cs index cd85cbcb5a..b3d1663edb 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/CityRepository.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/CityRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.TestApp.Domain; diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index 014c9c42db..f7fd1b046d 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -34,6 +35,12 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.ReplaceService(); + base.OnConfiguring(optionsBuilder); + } + protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Owned(); diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs index 22013d9b63..3daff7049c 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs @@ -24,10 +24,10 @@ namespace Volo.Abp.MongoDB } [Fact] - public void Should_Accept_MongoDb_Related_Queries() + public async Task Should_Accept_MongoDb_Related_Queries() { - var query = _personRepository.Where(p => p.Age > 0); - + var query = (await _personRepository.GetQueryableAsync()).Where(p => p.Age > 0); + _mongoDbAsyncQueryableProvider.CanExecute(query).ShouldBeTrue(); } @@ -35,17 +35,17 @@ namespace Volo.Abp.MongoDB public void Should_Not_Accept_Other_Providers() { var query = new[] {1, 2, 3}.AsQueryable().Where(x => x > 0); - + _mongoDbAsyncQueryableProvider.CanExecute(query).ShouldBeFalse(); } - + [Fact] public async Task Should_Execute_Queries() { using (var uow = _unitOfWorkManager.Begin()) { - var query = _personRepository.Where(p => p.Age > 0).OrderBy(p => p.Name); - + var query = (await _personRepository.GetQueryableAsync()).Where(p => p.Age > 0).OrderBy(p => p.Name); + (await _mongoDbAsyncQueryableProvider.CountAsync(query) > 0).ShouldBeTrue(); (await _mongoDbAsyncQueryableProvider.FirstOrDefaultAsync(query)).ShouldNotBeNull(); (await _mongoDbAsyncQueryableProvider.ToListAsync(query)).Count.ShouldBeGreaterThan(0); @@ -54,4 +54,4 @@ namespace Volo.Abp.MongoDB } } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs index 5335aa11be..48f74ab3a2 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs @@ -27,7 +27,7 @@ namespace Volo.Abp.MongoDB.Repositories [Fact] public async Task CanExecuteAsync() { - _mongoDbAsyncQueryableProvider.CanExecute(_personRepository).ShouldBeTrue(); + _mongoDbAsyncQueryableProvider.CanExecute(await _personRepository.GetQueryableAsync()).ShouldBeTrue(); _mongoDbAsyncQueryableProvider.CanExecute(await _personRepository.WithDetailsAsync()).ShouldBeTrue(); } @@ -36,7 +36,7 @@ namespace Volo.Abp.MongoDB.Repositories { using (var uow = _unitOfWorkManager.Begin()) { - (await _mongoDbAsyncQueryableProvider.FirstOrDefaultAsync(_personRepository.Where(p => p.Name == "Douglas"))).ShouldNotBeNull(); + (await _mongoDbAsyncQueryableProvider.FirstOrDefaultAsync((await _personRepository.GetQueryableAsync()).Where(p => p.Name == "Douglas"))).ShouldNotBeNull(); await uow.CompleteAsync(); } } @@ -46,7 +46,7 @@ namespace Volo.Abp.MongoDB.Repositories { using (var uow = _unitOfWorkManager.Begin()) { - (await _mongoDbAsyncQueryableProvider.AnyAsync(_personRepository, p => p.Name == "Douglas")).ShouldBeTrue(); + (await _mongoDbAsyncQueryableProvider.AnyAsync(await _personRepository.GetQueryableAsync(), p => p.Name == "Douglas")).ShouldBeTrue(); await uow.CompleteAsync(); } } @@ -56,7 +56,7 @@ namespace Volo.Abp.MongoDB.Repositories { using (var uow = _unitOfWorkManager.Begin()) { - (await _mongoDbAsyncQueryableProvider.CountAsync(_personRepository.Where(p => p.Name == "Douglas"))).ShouldBeGreaterThan(0); + (await _mongoDbAsyncQueryableProvider.CountAsync((await _personRepository.GetQueryableAsync()).Where(p => p.Name == "Douglas"))).ShouldBeGreaterThan(0); await uow.CompleteAsync(); } } @@ -66,7 +66,7 @@ namespace Volo.Abp.MongoDB.Repositories { using (var uow = _unitOfWorkManager.Begin()) { - (await _mongoDbAsyncQueryableProvider.LongCountAsync(_personRepository)).ShouldBeGreaterThan(0); + (await _mongoDbAsyncQueryableProvider.LongCountAsync(await _personRepository.GetQueryableAsync())).ShouldBeGreaterThan(0); await uow.CompleteAsync(); } } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs index 09b1551e3d..0be9b43db0 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs @@ -14,21 +14,21 @@ namespace Volo.Abp.MongoDB.Repositories public class Repository_Basic_Tests : Repository_Basic_Tests { [Fact] - public void ToMongoQueryable_Test() + public async Task ToMongoQueryable_Test() { - ((IMongoQueryable) PersonRepository).ShouldNotBeNull(); - PersonRepository.As>().ShouldNotBeNull(); - ((IMongoQueryable) PersonRepository.Where(p => p.Name == "Douglas")).ShouldNotBeNull(); - PersonRepository.Where(p => p.Name == "Douglas").As>().ShouldNotBeNull(); + (await PersonRepository.GetQueryableAsync()).ShouldNotBeNull(); + (await PersonRepository.GetQueryableAsync()).As>().ShouldNotBeNull(); + ((IMongoQueryable)(await PersonRepository.GetQueryableAsync()).Where(p => p.Name == "Douglas")).ShouldNotBeNull(); + (await PersonRepository.GetQueryableAsync()).Where(p => p.Name == "Douglas").As>().ShouldNotBeNull(); } [Fact] public async Task Linq_Queries() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - PersonRepository.FirstOrDefault(p => p.Name == "Douglas").ShouldNotBeNull(); - PersonRepository.Count().ShouldBeGreaterThan(0); + (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "Douglas").ShouldNotBeNull(); + (await PersonRepository.GetQueryableAsync()).Count().ShouldBeGreaterThan(0); return Task.CompletedTask; }); } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Serializer/MongoDB_DateTimeKind_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Serializer/MongoDB_DateTimeKind_Tests.cs new file mode 100644 index 0000000000..39a685380b --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Serializer/MongoDB_DateTimeKind_Tests.cs @@ -0,0 +1,120 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using Shouldly; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.Testing; +using Volo.Abp.Timing; +using Xunit; + +namespace Volo.Abp.MongoDB.Serializer +{ + [Collection(MongoTestCollection.Name)] + public abstract class MongoDB_DateTimeKind_Tests : DateTimeKind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + // MongoDB uses static properties to store the mapping information, + // We must reconfigure it in the new unit test. + foreach (var registeredClassMap in BsonClassMap.GetRegisteredClassMaps()) + { + var frozen = registeredClassMap.GetType().BaseType?.GetField("_frozen", BindingFlags.NonPublic | BindingFlags.Instance); + frozen?.SetValue(registeredClassMap, false); + + foreach (var declaredMemberMap in registeredClassMap.DeclaredMemberMaps) + { + var serializer = declaredMemberMap.GetSerializer(); + switch (serializer) + { + case AbpMongoDbDateTimeSerializer dateTimeSerializer: + dateTimeSerializer.SetDateTimeKind(Kind); + break; + case NullableSerializer nullableSerializer: + { + var lazySerializer = nullableSerializer.GetType() + ?.GetField("_lazySerializer", BindingFlags.NonPublic | BindingFlags.Instance) + ?.GetValue(serializer)?.As>>(); + + if (lazySerializer?.Value is AbpMongoDbDateTimeSerializer dateTimeSerializer) + { + dateTimeSerializer.SetDateTimeKind(Kind); + } + break; + } + } + } + + frozen?.SetValue(registeredClassMap, true); + } + } + } + + public class DateTimeKindTests_Unspecified : MongoDB_DateTimeKind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Unspecified; + services.Configure(x => x.Kind = Kind); + base.AfterAddApplication(services); + } + } + + public class DateTimeKindTests_Local : MongoDB_DateTimeKind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Local; + services.Configure(x => x.Kind = Kind); + base.AfterAddApplication(services); + } + } + + public class DateTimeKindTests_Utc : MongoDB_DateTimeKind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Utc; + services.Configure(x => x.Kind = Kind); + base.AfterAddApplication(services); + } + } + + [Collection(MongoTestCollection.Name)] + public class DisableDateTimeKindTests : TestAppTestBase + { + protected IPersonRepository PersonRepository { get; } + + public DisableDateTimeKindTests() + { + PersonRepository = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(x => x.UseAbpClockHandleDateTime = false); + base.AfterAddApplication(services); + } + + [Fact] + public async Task DateTime_Kind_Should_Be_Normalized_By_MongoDb_Test() + { + var personId = Guid.NewGuid(); + await PersonRepository.InsertAsync(new Person(personId, "bob lee", 18) + { + Birthday = DateTime.Parse("2020-01-01 00:00:00"), + LastActive = DateTime.Parse("2020-01-01 00:00:00"), + }, true); + + var person = await PersonRepository.GetAsync(personId); + + person.ShouldNotBeNull(); + person.CreationTime.Kind.ShouldBe(DateTimeKind.Utc); + + person.Birthday.ShouldNotBeNull(); + person.Birthday.Value.Kind.ShouldBe(DateTimeKind.Utc); + } + } +} diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/PersonRepository.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/PersonRepository.cs new file mode 100644 index 0000000000..3ed46288f1 --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/PersonRepository.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using MongoDB.Driver; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; +using Volo.Abp.TestApp.Domain; + +namespace Volo.Abp.TestApp.MongoDB +{ + public class PersonRepository : MongoDbRepository, IPersonRepository + { + public PersonRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async Task GetViewAsync(string name) + { + var person = await (await (await GetCollectionAsync()).FindAsync(x => x.Name == name)).FirstOrDefaultAsync(); + return new PersonView() + { + Name = person.Name, + CreationTime = person.CreationTime, + Birthday = person.Birthday, + LastActive = person.LastActive + }; + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs index f7e7fde5e2..645d413692 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.TestApp.Application //This is especially used to test the AbstractKeyCrudAppService public class DistrictAppService : AbstractKeyCrudAppService { - public DistrictAppService(IRepository repository) + public DistrictAppService(IRepository repository) : base(repository) { } @@ -23,7 +23,7 @@ namespace Volo.Abp.TestApp.Application protected override async Task GetEntityByIdAsync(DistrictKey id) { return await AsyncExecuter.FirstOrDefaultAsync( - Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name) + (await Repository.GetQueryableAsync()).Where(d => d.CityId == id.CityId && d.Name == id.Name) ); } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/AbpDateTimeValueConverter_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/AbpDateTimeValueConverter_Tests.cs deleted file mode 100644 index e36225e53c..0000000000 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/AbpDateTimeValueConverter_Tests.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Modularity; -using Volo.Abp.TestApp.Domain; -using Volo.Abp.Timing; -using Xunit; - -namespace Volo.Abp.TestApp.Testing -{ - public abstract class AbpDateTimeValueConverter_Tests : TestAppTestBase - where TStartupModule : IAbpModule - { - private readonly IPersonRepository _personRepository; - - protected AbpDateTimeValueConverter_Tests() - { - _personRepository = GetRequiredService(); - } - - [Fact] - public async Task DateTime_Kind_Should_Be_Normalized_To_UTC_Test() - { - var personId = Guid.Parse("4125582e-d100-4c27-aa84-e4de85830dca"); - await _personRepository.InsertAsync(new Person(personId, "bob lee", 18) - { - Birthday = DateTime.Parse("2020-01-01 00:00:00"), - LastActive = DateTime.Parse("2020-01-01 00:00:00"), - }, true); - - var person = await _personRepository.GetAsync(personId); - - person.ShouldNotBeNull(); - person.CreationTime.Kind.ShouldBe(DateTimeKind.Utc); - - person.Birthday.ShouldNotBeNull(); - person.Birthday.Value.Kind.ShouldBe(DateTimeKind.Utc); - person.Birthday.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); - - //LastActive DisableDateTimeNormalization - person.LastActive.ShouldNotBeNull(); - person.LastActive.Value.Kind.ShouldBe(DateTimeKind.Unspecified); - person.LastActive.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); - } - - [Fact] - public async Task DateTime_Kind_Should_Be_Normalized_To_UTC_View_Test() - { - var personName = "bob lee"; - await _personRepository.InsertAsync(new Person(Guid.NewGuid(), personName, 18) - { - Birthday = DateTime.Parse("2020-01-01 00:00:00"), - LastActive = DateTime.Parse("2020-01-01 00:00:00"), - }, true); - - var person = await _personRepository.GetViewAsync(personName); - - person.ShouldNotBeNull(); - person.CreationTime.Kind.ShouldBe(DateTimeKind.Utc); - - person.Birthday.ShouldNotBeNull(); - person.Birthday.Value.Kind.ShouldBe(DateTimeKind.Utc); - person.Birthday.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); - - //LastActive DisableDateTimeNormalization - person.LastActive.ShouldNotBeNull(); - person.LastActive.Value.Kind.ShouldBe(DateTimeKind.Unspecified); - person.LastActive.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DateTimeKind_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DateTimeKind_Tests.cs new file mode 100644 index 0000000000..2e13db34b8 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DateTimeKind_Tests.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Volo.Abp.TestApp.Domain; +using Xunit; + +namespace Volo.Abp.TestApp.Testing +{ + public abstract class DateTimeKind_Tests : TestAppTestBase + where TStartupModule : IAbpModule + { + protected IPersonRepository PersonRepository { get; } + protected DateTimeKind Kind { get; set; } + + protected DateTimeKind_Tests() + { + PersonRepository = GetRequiredService(); + } + + [Fact] + public async Task DateTime_Kind_Should_Be_Normalized_Test() + { + var personId = Guid.NewGuid(); + await PersonRepository.InsertAsync(new Person(personId, "bob lee", 18) + { + Birthday = DateTime.Parse("2020-01-01 00:00:00"), + LastActive = DateTime.Parse("2020-01-01 00:00:00"), + }, true); + + var person = await PersonRepository.GetAsync(personId); + + person.ShouldNotBeNull(); + person.CreationTime.Kind.ShouldBe(Kind); + + person.Birthday.ShouldNotBeNull(); + person.Birthday.Value.Kind.ShouldBe(Kind); + person.Birthday.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); + + //LastActive DisableDateTimeNormalization + person.LastActive.ShouldNotBeNull(); + person.LastActive.Value.Kind.ShouldBe(DateTimeKind.Unspecified); + person.LastActive.Value.ToString("yyy-MM-dd HH:mm:ss").ShouldBe("2020-01-01 00:00:00"); + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs index 04d0dda3c4..27154ba8de 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs @@ -13,7 +13,7 @@ using Xunit; namespace Volo.Abp.TestApp.Testing { - public abstract class DomainEvents_Tests : TestAppTestBase + public abstract class DomainEvents_Tests : TestAppTestBase where TStartupModule : IAbpModule { protected readonly IRepository PersonRepository; @@ -35,7 +35,7 @@ namespace Volo.Abp.TestApp.Testing bool douglesNameChangeHandled = false; bool customEventHandled = false; bool customEvent2Handled = false; - + LocalEventBus.Subscribe>(data => { data.Entity.Name.ShouldBe("TestPerson1"); @@ -46,7 +46,7 @@ namespace Volo.Abp.TestApp.Testing customEvent2Handled.ShouldBeFalse(); return Task.CompletedTask; }); - + LocalEventBus.Subscribe(data => { data.Value.ShouldBe("42"); @@ -57,7 +57,7 @@ namespace Volo.Abp.TestApp.Testing customEvent2Handled.ShouldBeFalse(); return Task.CompletedTask; }); - + LocalEventBus.Subscribe(data => { data.OldName.ShouldBe("Douglas"); @@ -69,7 +69,7 @@ namespace Volo.Abp.TestApp.Testing customEvent2Handled.ShouldBeFalse(); return Task.CompletedTask; }); - + LocalEventBus.Subscribe>(data => { data.Entity.Name.ShouldBe("Douglas-Updated"); @@ -80,7 +80,7 @@ namespace Volo.Abp.TestApp.Testing customEvent2Handled.ShouldBeFalse(); return Task.CompletedTask; }); - + LocalEventBus.Subscribe(data => { data.Value.ShouldBe("44"); @@ -97,13 +97,13 @@ namespace Volo.Abp.TestApp.Testing await PersonRepository.InsertAsync( new Person(Guid.NewGuid(), "TestPerson1", 42) ); - + await LocalEventBus.PublishAsync(new MyCustomEventData { Value = "42" }); var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); douglas.ChangeName("Douglas-Updated"); await PersonRepository.UpdateAsync(douglas); - + await LocalEventBus.PublishAsync(new MyCustomEventData2 { Value = "44" }); }); } @@ -112,7 +112,7 @@ namespace Volo.Abp.TestApp.Testing public virtual async Task Should_Rollback_Uow_If_Event_Handler_Throws_Exception() { (await PersonRepository.FindAsync(x => x.Name == "TestPerson1")).ShouldBeNull(); - + LocalEventBus.Subscribe>(data => { data.Entity.Name.ShouldBe("TestPerson1"); @@ -128,7 +128,7 @@ namespace Volo.Abp.TestApp.Testing ); }); }); - + exception.Message.ShouldBe("Just to rollback the UOW"); (await PersonRepository.FindAsync(x => x.Name == "TestPerson1")).ShouldBeNull(); @@ -162,7 +162,7 @@ namespace Volo.Abp.TestApp.Testing await WithUnitOfWorkAsync(async () => { - var dougles = PersonRepository.Single(b => b.Name == "Douglas"); + var dougles = await PersonRepository.SingleAsync(b => b.Name == "Douglas"); dougles.ChangeName("Douglas-Changed"); await PersonRepository.UpdateAsync(dougles); }); @@ -172,15 +172,15 @@ namespace Volo.Abp.TestApp.Testing isLocalEventTriggered.ShouldBeTrue(); isDistributedEventTriggered.ShouldBeTrue(); } - + private class MyCustomEventData { public string Value { get; set; } } - + private class MyCustomEventData2 { public string Value { get; set; } } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/MultiTenant_Filter_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/MultiTenant_Filter_Tests.cs index da3e33ffea..2a4a012217 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/MultiTenant_Filter_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/MultiTenant_Filter_Tests.cs @@ -14,7 +14,7 @@ using Xunit; namespace Volo.Abp.TestApp.Testing { - public abstract class MultiTenant_Filter_Tests : TestAppTestBase + public abstract class MultiTenant_Filter_Tests : TestAppTestBase where TStartupModule : IAbpModule { private ICurrentTenant _fakeCurrentTenant; @@ -36,13 +36,13 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Should_Get_Person_For_Current_Tenant() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { //TenantId = null _fakeCurrentTenant.Id.Returns((Guid?)null); - var people = _personRepository.ToList(); + var people = await _personRepository.ToListAsync(); people.Count.ShouldBe(1); people.Any(p => p.Name == "Douglas").ShouldBeTrue(); @@ -50,7 +50,7 @@ namespace Volo.Abp.TestApp.Testing _fakeCurrentTenant.Id.Returns(TestDataBuilder.TenantId1); - people = _personRepository.ToList(); + people = await _personRepository.ToListAsync(); people.Count.ShouldBe(2); people.Any(p => p.Name == TestDataBuilder.TenantId1 + "-Person1").ShouldBeTrue(); people.Any(p => p.Name == TestDataBuilder.TenantId1 + "-Person2").ShouldBeTrue(); @@ -59,7 +59,7 @@ namespace Volo.Abp.TestApp.Testing _fakeCurrentTenant.Id.Returns(TestDataBuilder.TenantId2); - people = _personRepository.ToList(); + people = await _personRepository.ToListAsync(); people.Count.ShouldBe(0); return Task.CompletedTask; @@ -69,19 +69,19 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Should_Get_All_People_When_MultiTenant_Filter_Is_Disabled() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { List people; using (_multiTenantFilter.Disable()) { //Filter disabled manually - people = _personRepository.ToList(); + people = await _personRepository.ToListAsync(); people.Count.ShouldBe(3); } //Filter re-enabled automatically - people = _personRepository.ToList(); + people = await _personRepository.ToListAsync(); people.Count.ShouldBe(1); return Task.CompletedTask; diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs index 821a80492b..4347120d36 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs @@ -22,9 +22,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public virtual async Task FirstOrDefault() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - var entity = EntityWithIntPkRepository.FirstOrDefault(e => e.Name == "Entity1"); + var entity = await EntityWithIntPkRepository.FirstOrDefaultAsync(e => e.Name == "Entity1"); entity.ShouldNotBeNull(); entity.Name.ShouldBe("Entity1"); return Task.CompletedTask; diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Queryable_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Queryable_Tests.cs index 3ca511a7ea..c8710bbf2c 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Queryable_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Queryable_Tests.cs @@ -23,9 +23,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Any() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - PersonRepository.Any().ShouldBeTrue(); + (await PersonRepository.AnyAsync()).ShouldBeTrue(); return Task.CompletedTask; }); } @@ -33,9 +33,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Single() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - var person = PersonRepository.Single(p => p.Id == TestDataBuilder.UserDouglasId); + var person = await PersonRepository.SingleAsync(p => p.Id == TestDataBuilder.UserDouglasId); person.Name.ShouldBe("Douglas"); return Task.CompletedTask; }); diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs index c26134b27c..9ce5499d7f 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs @@ -24,9 +24,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task SpecificationWithRepository_Test() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - CityRepository.Count(new CitySpecification().ToExpression()).ShouldBe(1); + (await CityRepository.CountAsync(new CitySpecification().ToExpression())).ShouldBe(1); return Task.CompletedTask; }); } @@ -39,4 +39,4 @@ namespace Volo.Abp.TestApp.Testing return city => city.Name == "Istanbul"; } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs index f3ac8c301f..514f2d2648 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs @@ -10,7 +10,7 @@ using Xunit; namespace Volo.Abp.TestApp.Testing { - public abstract class SoftDelete_Filter_Tests : TestAppTestBase + public abstract class SoftDelete_Filter_Tests : TestAppTestBase where TStartupModule : IAbpModule { protected readonly IRepository PersonRepository; @@ -25,9 +25,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Should_Not_Get_Deleted_Entities_Linq() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - var person = PersonRepository.FirstOrDefault(p => p.Name == "John-Deleted"); + var person = await PersonRepository.FirstOrDefaultAsync(p => p.Name == "John-Deleted"); person.ShouldBeNull(); return Task.CompletedTask; }); @@ -46,9 +46,9 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Should_Not_Get_Deleted_Entities_By_Default_ToList() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { - var people = PersonRepository.ToList(); + var people = await PersonRepository.ToListAsync(); people.Count.ShouldBe(1); people.Any(p => p.Name == "Douglas").ShouldBeTrue(); return Task.CompletedTask; @@ -58,36 +58,36 @@ namespace Volo.Abp.TestApp.Testing [Fact] public async Task Should_Get_Deleted_Entities_When_Filter_Is_Disabled() { - await WithUnitOfWorkAsync(() => + await WithUnitOfWorkAsync(async () => { //Soft delete is enabled by default - var people = PersonRepository.ToList(); + var people = await PersonRepository.ToListAsync(); people.Any(p => !p.IsDeleted).ShouldBeTrue(); people.Any(p => p.IsDeleted).ShouldBeFalse(); using (DataFilter.Disable()) { //Soft delete is disabled - people = PersonRepository.ToList(); + people = await PersonRepository.ToListAsync(); people.Any(p => !p.IsDeleted).ShouldBeTrue(); people.Any(p => p.IsDeleted).ShouldBeTrue(); using (DataFilter.Enable()) { //Soft delete is enabled again - people = PersonRepository.ToList(); + people = await PersonRepository.ToListAsync(); people.Any(p => !p.IsDeleted).ShouldBeTrue(); people.Any(p => p.IsDeleted).ShouldBeFalse(); } //Soft delete is disabled (restored previous state) - people = PersonRepository.ToList(); + people = await PersonRepository.ToListAsync(); people.Any(p => !p.IsDeleted).ShouldBeTrue(); people.Any(p => p.IsDeleted).ShouldBeTrue(); } //Soft delete is enabled (restored previous state) - people = PersonRepository.ToList(); + people = await PersonRepository.ToListAsync(); people.Any(p => !p.IsDeleted).ShouldBeTrue(); people.Any(p => p.IsDeleted).ShouldBeFalse(); diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json index e3701c9b69..b80c6c7f05 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "استخدم خدمة أخرى لتسجيل الدخول", "UserLockedOutMessage": "تم قفل حساب المستخدم بسبب محاولات تسجيل الدخول غير الصالحة. يرجى الانتظار بعض الوقت والمحاولة مرة أخرى.", "InvalidUserNameOrPassword": "اسم مستخدم أو كلمة مرور غير صالحة!", - "LoginIsNotAllowed": "غير مسموح لك بتسجيل الدخول! أنت بحاجة إلى تأكيد بريدك الإلكتروني / رقم هاتفك.", + "LoginIsNotAllowed": "لا يسمح لك بتسجيل الدخول! حسابك غير نشط أو يحتاج إلى تأكيد بريدك الإلكتروني / رقم هاتفك.", "SelfRegistrationDisabledMessage": "تم تعطيل التسجيل الذاتي لهذا التطبيق. يرجى الاتصال بمسؤول التطبيق لتسجيل مستخدم جديد.", "LocalLoginDisabledMessage": "تسجيل الدخول المحلي معطّل لهذا التطبيق.", "Login": "دخول", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json index 98efb29ed6..d170187231 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Použít jinou službu k přihlášení", "UserLockedOutMessage": "Tento účet byl uzamčen z důvodu neúspěšných přihlášení. Přihlaste se prosím později.", "InvalidUserNameOrPassword": "Neplatné uživatelské jméno nebo heslo!", - "LoginIsNotAllowed": "Není vám dovoleno se přihlásit! Musíte první potvrdit email/telefonní číslo.", + "LoginIsNotAllowed": "Nemáte oprávnění se přihlásit! Váš účet je neaktivní nebo potřebuje potvrdit váš e -mail/telefonní číslo.", "SelfRegistrationDisabledMessage": "Vlastní registrace uživatele není povolena. K vytvoření účtu kontaktujte správce.", "LocalLoginDisabledMessage": "Místní přihlášení je pro tuto aplikaci zakázáno.", "Login": "Přihlásit", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json index 2e8b1f4eb4..f2dc47ccbc 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de-DE.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Verwenden Sie einen anderen Dienst, um sich anzumelden", "UserLockedOutMessage": "Das Benutzerkonto wurde aufgrund ungültiger Anmeldeversuche gesperrt. Bitte warten Sie eine Weile und versuchen Sie es erneut.", "InvalidUserNameOrPassword": "Ungültiger Benutzername oder Passwort!", - "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Sie müssen Ihre E-Mail-Adresse / Telefonnummer bestätigen.", + "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Ihr Konto ist inaktiv oder muss Ihre E-Mail-/Telefonnummer bestätigen.", "SelfRegistrationDisabledMessage": "Die Selbstregistrierung ist für diese Anwendung deaktiviert. Bitte wenden Sie sich an den Anwendungsadministrator, um einen neuen Benutzer zu registrieren.", "LocalLoginDisabledMessage": "Die lokale Anmeldung ist für diese Anwendung deaktiviert.", "Login": "Anmelden", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json index 99854771e8..f66217156b 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Einen anderen Dienst zum Anmelden verwenden", "UserLockedOutMessage": "Das Benutzerkonto wurde aufgrund fehlgeschlagener Anmeldeversuche gesperrt. Bitte warten Sie eine Weile und versuchen Sie es erneut.", "InvalidUserNameOrPassword": "Ungültiger Benutzername oder Passwort!", - "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Sie müssen Ihre E-Mail/Telefonnummer bestätigen.", + "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Ihr Konto ist inaktiv oder muss Ihre E-Mail-/Telefonnummer bestätigen.", "SelfRegistrationDisabledMessage": "Die Selbstregistrierung ist für diese Anwendung deaktiviert. Bitte wenden Sie sich an den Anwendungsadministrator, um einen neuen Benutzer zu registrieren.", "LocalLoginDisabledMessage": "Die lokale Anmeldung ist für diese Anwendung deaktiviert.", "Login": "Anmelden", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json index cbc5643a53..7bfcdccec7 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Use another service to log in", "UserLockedOutMessage": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.", "InvalidUserNameOrPassword": "Invalid username or password!", - "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", + "LoginIsNotAllowed": "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.", "SelfRegistrationDisabledMessage": "Self-registration is disabled for this application. Please contact the application administrator to register a new user.", "LocalLoginDisabledMessage": "Local login is disabled for this application.", "Login": "Login", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json index 48b2ebb4ee..246466193e 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Use another service to log in", "UserLockedOutMessage": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.", "InvalidUserNameOrPassword": "Invalid username or password!", - "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", + "LoginIsNotAllowed": "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.", "SelfRegistrationDisabledMessage": "Self-registration is disabled for this application. Please contact the application administrator to register a new user.", "LocalLoginDisabledMessage": "Local login is disabled for this application.", "Login": "Login", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json index 68a38c96de..d8c50231a5 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Usar otro servicio para iniciar sesión", "UserLockedOutMessage": "La cuenta de usuario ha sido bloqueada debido a los intentos de inicio de sesión no válido. Por favor, espere un rato y vuelve a intentarlo.", "InvalidUserNameOrPassword": "No válido nombre de usuario o la contraseña!", - "LoginIsNotAllowed": "No está permitido el inicio de sesión! Usted tendrá que confirmar su correo electrónico\/número de teléfono.", + "LoginIsNotAllowed": "¡No está permitido iniciar sesión! Su cuenta está inactiva o necesita confirmar su correo electrónico / número de teléfono.", "SelfRegistrationDisabledMessage": "El autoregistro de usuario está deshabilitado para esta aplicación. Póngase en contacto con el administrador de la aplicación para registrar un nuevo usuario.", "Login": "Iniciar sesión", "Cancel": "Cancelar", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json index 9c1d8b6b3a..894cb709ed 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Usar otro servicio para iniciar sesión", "UserLockedOutMessage": "La cuenta de usuario ha sido bloqueada debido a los intentos de inicio de sesión no válidos. Por favor, espere unos minutos y vuelve a intentarlo.", "InvalidUserNameOrPassword": "El nombre de usuario o la contraseña no son válidos", - "LoginIsNotAllowed": "No está permitido el inicio de sesión! Usted tendrá que confirmar su correo electrónico o número de teléfono.", + "LoginIsNotAllowed": "¡No está permitido iniciar sesión! Su cuenta está inactiva o necesita confirmar su correo electrónico / número de teléfono.", "SelfRegistrationDisabledMessage": "El autoregistro de usuario está deshabilitado para esta aplicación. Póngase en contacto con el administrador de la aplicación para registrar un nuevo usuario.", "LocalLoginDisabledMessage": "Inicio de sesión local ha sido deshabilitado para esta aplicación", "Login": "Iniciar sesión", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json index 5c938a9480..a4e55494eb 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Käytä toista palvelua kirjautumiseen", "UserLockedOutMessage": "Käyttäjätili on lukittu virheellisten kirjautumisyritysten vuoksi. Odota hetki ja yritä uudelleen.", "InvalidUserNameOrPassword": "Väärä käyttäjänimi tai salasana!", - "LoginIsNotAllowed": "Et saa kirjautua sisään! Sinun on vahvistettava sähköpostiosoitteesi / puhelinnumerosi.", + "LoginIsNotAllowed": "Et saa kirjautua sisään! Tilisi on passiivinen tai sinun on vahvistettava sähköpostiosoitteesi/puhelinnumerosi.", "SelfRegistrationDisabledMessage": "Itserekisteröinti on poistettu käytöstä tälle sovellukselle. Rekisteröi uusi käyttäjä ottamalla yhteyttä sovelluksen järjestelmänvalvojaan.", "LocalLoginDisabledMessage": "Paikallinen sisäänkirjautuminen on poistettu käytöstä tälle sovellukselle.", "Login": "Kirjaudu sisään", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json index 7aed8a8e5f..6c943322cc 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Utilisez un autre service pour vous connecter", "UserLockedOutMessage": "Le compte utilisateur a été verrouillé en raison de tentatives de connexion non valides. Veuillez patienter un instant et réessayer.", "InvalidUserNameOrPassword": "Nom d'utilisateur ou mot de passe invalide!", - "LoginIsNotAllowed": "Vous n'êtes pas autorisé à vous connecter! Vous devez confirmer votre e-mail / numéro de téléphone.", + "LoginIsNotAllowed": "Vous n'êtes pas autorisé à vous connecter ! Votre compte est inactif ou doit confirmer votre e-mail/numéro de téléphone.", "SelfRegistrationDisabledMessage": "L'auto-inscription est désactivée pour cette application. Veuillez contacter l'administrateur de l'application pour enregistrer un nouvel utilisateur.", "LocalLoginDisabledMessage": "La connexion locale est désactivée pour cette application.", "Login": "Connectez-vous", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json index d9a2bccd85..e1605b14e2 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "लॉग इन करने के लिए किसी अन्य सेवा का उपयोग करें", "UserLockedOutMessage": "अमान्य लॉगिन प्रयासों के कारण उपयोगकर्ता खाता बंद कर दिया गया है। कृपया थोड़ी देर प्रतीक्षा करें और पुन: प्रयास करें।", "InvalidUserNameOrPassword": "अमान्य उपयोगकर्ता नाम या पासवर्ड!", - "LoginIsNotAllowed": "आपको लॉगिन करने की अनुमति नहीं है! आपको अपने ईमेल / फोन नंबर की पुष्टि करनी होगी।", + "LoginIsNotAllowed": "आपको लॉगिन करने की अनुमति नहीं है! आपका खाता निष्क्रिय है या आपके ईमेल/फ़ोन नंबर की पुष्टि करने की आवश्यकता है।", "SelfRegistrationDisabledMessage": "इस आवेदन के लिए स्व-पंजीकरण अक्षम है। नया उपयोगकर्ता पंजीकृत करने के लिए कृपया एप्लिकेशन व्यवस्थापक से संपर्क करें।", "LocalLoginDisabledMessage": "इस एप्लिकेशन के लिए स्थानीय लॉगिन अक्षम है।", "Login": "लॉग इन करें", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json index d1d774e7cb..dc95c1b3cb 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Használjon másik szolgáltatást a bejelentkezéshez", "UserLockedOutMessage": "A felhasználói fiókot érvénytelen bejelentkezési kísérletek miatt zároltuk. Kérjük, várjon egy kicsit, és próbálja újra.", "InvalidUserNameOrPassword": "Érvénytelen felhasználónév vagy jelszó!", - "LoginIsNotAllowed": "Ön nem léphet be! Meg kell erősítenie e-mail címét / telefonszámát.", + "LoginIsNotAllowed": "A bejelentkezés nem engedélyezett! Fiókja inaktív, vagy meg kell erősítenie e -mail címét/telefonszámát.", "SelfRegistrationDisabledMessage": "Ennél az alkalmazásnál az önregisztráció le van tiltva. Új felhasználó regisztrálásához vegye fel a kapcsolatot az alkalmazás rendszergazdájával.", "LocalLoginDisabledMessage": "A helyi bejelentkezés le van tiltva ennél az alkalmazásnál.", "Login": "Bejelntkezés", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json index b3a4dc4fef..71d40aabed 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Usa un altro servizio per accedere", "UserLockedOutMessage": "L'account utente è stato bloccato a causa di tentativi di accesso non validi. Attendi qualche istante e riprova.", "InvalidUserNameOrPassword": "Username o password non validi!", - "LoginIsNotAllowed": "Non sei autorizzato ad accedere! Devi confermare la tua email / numero di telefono.", + "LoginIsNotAllowed": "Non sei autorizzato ad accedere! Il tuo account non è attivo o deve confermare la tua email/numero di telefono.", "SelfRegistrationDisabledMessage": "L'auto-registrazione è disabilitata per questa applicazione. Contatta l'amministratore dell'applicazione per registrare un nuovo utente.", "LocalLoginDisabledMessage": "L'accesso locale è disabilitato per questa applicazione.", "Login": "Login", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json index 548d4a4f02..19bc055db9 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Gebruik een andere dienst om in te loggen", "UserLockedOutMessage": "Het gebruikersaccount is geblokkeerd vanwege ongeldige inlogpogingen. Wacht even en probeer het opnieuw.", "InvalidUserNameOrPassword": "Ongeldige gebruikersnaam of wachtwoord!", - "LoginIsNotAllowed": "U mag niet inloggen! U moet uw e-mailadres / telefoonnummer bevestigen.", + "LoginIsNotAllowed": "U mag niet inloggen! Uw account is inactief of moet uw e-mailadres/telefoonnummer bevestigen.", "SelfRegistrationDisabledMessage": "Zelfregistratie is uitgeschakeld voor deze applicatie. Neem contact op met de applicatiebeheerder om een nieuwe gebruiker te registreren.", "LocalLoginDisabledMessage": "Lokale aanmelding is uitgeschakeld voor deze applicatie.", "Login": "Log in", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json index 032ac41cfa..05927f1efa 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Użyj innej usługi do zalogowania się", "UserLockedOutMessage": "Konto użytkownika zostało zablokowane po nieudanych próbach zalogowania. Odczekaj chwilę i spróbuj ponownie.", "InvalidUserNameOrPassword": "Niepoprawna nazwa użytkownika lub hasło!", - "LoginIsNotAllowed": "Nie jesteś uprawniony do logowania! Musisz potwierdzić swój email/numer telefonu.", + "LoginIsNotAllowed": "Nie możesz się zalogować! Twoje konto jest nieaktywne lub wymaga potwierdzenia adresu e-mail/numeru telefonu.", "SelfRegistrationDisabledMessage": "Rejestracja użytkowników jest wyłączona dla tej aplikacji. Skontaktuj się z administratorem aplikacji w celu rejestracji nowego użytkownika.", "Login": "Zaloguj", "Cancel": "Anuluj", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json index 9efbc95faa..f1a08b3d90 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Usar outro serviço para entrar", "UserLockedOutMessage": "Esta conta de usuário está bloqueada devido a muitas tentativas de acesso. Por favor, espero alguns instantes e tente novamente.", "InvalidUserNameOrPassword": "Usuário ou senha estão incorretos!", - "LoginIsNotAllowed": "Você não possui permissão para entrar! Você deve confirmar seu e-mail ou número de telefone.", + "LoginIsNotAllowed": "Você não tem permissão para fazer o login! Sua conta está inativa ou precisa confirmar seu e-mail / número de telefone.", "SelfRegistrationDisabledMessage": "Não é permitido que você crie uma nova conta neste site. Contate um administrador para que ele crie uma conta para você.", "Login": "Entrar", "Cancel": "Cancelar", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json index 909d09f211..71c9fbcdd1 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Folisiţi alt serviciu pentru a vă autentifica", "UserLockedOutMessage": "Contul a fost blocat din cauza incercărilor eşuate de autentificare. Vă rugăm să aşteptaţi şi să încercaţi din nou.", "InvalidUserNameOrPassword": "Nume de utilizator sau parolă invalide!", - "LoginIsNotAllowed": "Nu vă este permis să vă autentificaţi! Trebuie să vă confirmaţi email-ul/numărul de telefon.", + "LoginIsNotAllowed": "Nu ai voie să te autentifici! Contul dvs. este inactiv sau trebuie să vă confirme numărul de e-mail / telefon.", "SelfRegistrationDisabledMessage": "Înregistrarea personală este dezactivată pentru această aplicaţie. Vă rugăm să contactaţi administratorul aplicaţiei pentru a înregistra un nou utilizator.", "LocalLoginDisabledMessage": "Autentificarea locală este dezactivată pentru această aplicaţie.", "Login": "Autentificare", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json index b278f7fbbc..2722c472ef 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Использовать сторонний сервис для входа", "UserLockedOutMessage": "Пользователь заблокирован из-за большого количества попыток входа. Пожалуйста, попробуйте позднее.", "InvalidUserNameOrPassword": "Неправильные имя пользователя и/или пароль!", - "LoginIsNotAllowed": "Вы не можете войти. Вам необходимо подтвердить электронную почту или телефон.", + "LoginIsNotAllowed": "Вам не разрешено входить в систему! Ваша учетная запись неактивна или вам необходимо подтвердить адрес электронной почты / номер телефона.", "SelfRegistrationDisabledMessage": "Самостоятельная регистрация не предусмотрена. Пожалуйста, свяжитесь с администраром для получения доступа.", "LocalLoginDisabledMessage": "Локальный вход отключен.", "Login": "Войти", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json index 43846a225a..8ac5e62980 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Použitie inej služby na prihlásenie", "UserLockedOutMessage": "Používateľské konto bolo uzamknuté z dôvodu neplatných pokusov o prihlásenie. Chvíľu počkajte a skúste to znova.", "InvalidUserNameOrPassword": "Neplatné používateľské meno alebo heslo!", - "LoginIsNotAllowed": "Nemáte povolené prihlásiť sa! Musíte potvrdiť svoj email/telefónne číslo.", + "LoginIsNotAllowed": "Nemáte povolenie sa prihlásiť! Váš účet je neaktívny alebo potrebuje potvrdiť váš e -mail/telefónne číslo.", "SelfRegistrationDisabledMessage": "Samostatná registrácia je pre túto aplikáciu vypnutá. Ak chcete zaregistrovať nového používateľa, obráťte sa na správcu aplikácie.", "LocalLoginDisabledMessage": "Lokálne prihlásenie je pre túto aplikáciu zakázané.", "Login": "Prihlásiť", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json index 7d8457c5a8..6db74f6184 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Uporabi drugo storitev za prijavo", "UserLockedOutMessage": "Uporabniški račun je bil zaklenjen zaradi neveljavnih poskusov prijave. Počakajte nekaj časa in poskusite znova.", "InvalidUserNameOrPassword": "Napačno uporabniško ime ali geslo!", - "LoginIsNotAllowed": "Nimate dovoljenja za prijavo! Potrditi morate e-poštni naslov/telefonsko številko.", + "LoginIsNotAllowed": "Ne smete se prijaviti! Vaš račun je neaktiven ali mora potrditi vaš e -poštni naslov/telefonsko številko.", "SelfRegistrationDisabledMessage": "Možnost lastne registracije uporabnika je onemogočena za to aplikacijo. Kontaktirajte skrbnika aplikacije, da registrirate novega uporabnika.", "LocalLoginDisabledMessage": "Lokalna prijava za to aplikacijo je onemogočena.", "Login": "Prijava", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json index 2dc74572c9..f9b545b021 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Başka bir servisle giriş yap", "UserLockedOutMessage": "Kullanıcı hesabı hatalı giriş denemeleri nedeniyle kilitlenmiştir. Lütfen bir süre bekleyip tekrar deneyin.", "InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!", - "LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.", + "LoginIsNotAllowed": "Giriş yapmanıza izin verilmiyor! Hesabınız etkin değil veya e-postanızı/telefon numaranızı onaylamanız gerekiyor.", "SelfRegistrationDisabledMessage": "Bu uygulama için kullanıcıların kendi kendilerine kaydolmaları engellenmiştir. Yeni bir kullanıcı kaydetmek için lütfen uygulama yöneticisi ile iletişime geçin.", "LocalLoginDisabledMessage": "Bu uygulama için local login devre dışı bırakılmıştır.", "Login": "Giriş yap", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json index 56d436c1cd..9b5c9b5c42 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "Sử dụng dịch vụ khác để đăng nhập", "UserLockedOutMessage": "Tài khoản người dùng đã bị khóa do các nỗ lực đăng nhập không hợp lệ. Xin vui lòng chờ một lúc và thử lại.", "InvalidUserNameOrPassword": "Sai tên tài khoản hoặc mật khẩu!", - "LoginIsNotAllowed": "Bạn không được phép đăng nhập! Bạn cần xác nhận email/số điện thoại của bạn.", + "LoginIsNotAllowed": "Bạn không được phép đăng nhập! Tài khoản của bạn không hoạt động hoặc cần xác nhận email / số điện thoại của bạn.", "SelfRegistrationDisabledMessage": "Tự đăng ký người dùng bị vô hiệu hóa cho ứng dụng này. Liên hệ với quản trị viên ứng dụng để đăng ký người dùng mới.", "Login": "Đăng nhập", "Cancel": "Hủy bỏ", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json index 7f3939c502..ffb6730e22 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "使用另一个服务登录", "UserLockedOutMessage": "登录失败,用户账户已被锁定.请稍后再试.", "InvalidUserNameOrPassword": "用户名或密码错误!", - "LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.", + "LoginIsNotAllowed": "无法登录!你的账号未激活或者需要验证邮箱地址/手机号.", "SelfRegistrationDisabledMessage": "应用程序未开放注册,请联系管理员添加新用户.", "LocalLoginDisabledMessage": "应用程序未开放本地账户登录.", "Login": "登录", diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json index 36e84e1b9c..6e69fc9093 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json @@ -9,7 +9,7 @@ "UseAnotherServiceToLogin": "使用另一個服務登入", "UserLockedOutMessage": "登入失敗,使用者帳號已被鎖定.請稍後再試.", "InvalidUserNameOrPassword": "使用者名稱或密碼錯誤!", - "LoginIsNotAllowed": "無法登入!你需要驗證電子信箱地址/手機號碼.", + "LoginIsNotAllowed": "無法登入!你的賬號未激活或者需要驗證郵箱地址/手機號碼.", "SelfRegistrationDisabledMessage": "應用程式未開放註冊,請聯絡管理員以加入新使用者.", "LocalLoginDisabledMessage": "應用程序未開放本地賬戶登錄.", "Login": "登入", diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs index e2fe874dbd..3b6959b60c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs @@ -26,6 +26,8 @@ namespace Volo.Abp.Identity [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } + public bool IsActive { get; set; } + public bool LockoutEnabled { get; set; } [CanBeNull] diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs index f7291a4e82..f3b03ab8dc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs @@ -23,6 +23,8 @@ namespace Volo.Abp.Identity public bool PhoneNumberConfirmed { get; set; } + public bool IsActive { get; set; } + public bool LockoutEnabled { get; set; } public DateTimeOffset? LockoutEnd { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs index 351059a305..e380ffa0d0 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs @@ -178,7 +178,7 @@ namespace Volo.Abp.Identity user.Name = input.Name; user.Surname = input.Surname; (await UserManager.UpdateAsync(user)).CheckErrors(); - + user.SetIsActive(input.IsActive); if (input.RoleNames != null) { (await UserManager.SetRolesAsync(user, input.RoleNames)).CheckErrors(); diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs index 8de9014cbe..fc88c33363 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs @@ -33,7 +33,7 @@ namespace Volo.Abp.Identity.AspNetCore AbpOptions = options.Value; } - public async override Task PasswordSignInAsync( + public override async Task PasswordSignInAsync( string userName, string password, bool isPersistent, @@ -62,5 +62,16 @@ namespace Volo.Abp.Identity.AspNetCore return await base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure); } + + protected override async Task PreSignInCheck(IdentityUser user) + { + if (!user.IsActive) + { + Logger.LogWarning("User is currently inactive."); + return SignInResult.NotAllowed; + } + + return await base.PreSignInCheck(user); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor index 17d24c1d9b..2f632422c1 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor @@ -110,6 +110,9 @@ + + @L["DisplayName:IsActive"] + @L["DisplayName:LockoutEnabled"] @@ -221,6 +224,9 @@ + + @L["DisplayName:IsActive"] + @L["DisplayName:LockoutEnabled"] @@ -254,4 +260,4 @@ @if ( HasManagePermissionsPermission ) { -} \ No newline at end of file +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json index 68f4318991..642bede8d6 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json @@ -25,6 +25,7 @@ "DisplayName:Email": "Email address", "DisplayName:PhoneNumber": "Phone number", "DisplayName:TwoFactorEnabled": "Two factor verification", + "DisplayName:IsActive": "Active", "DisplayName:LockoutEnabled": "Lock account after failed login attempts", "NewRole": "New role", "RoleName": "Role name", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json index 2ad19c80f4..177e696bab 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json @@ -25,6 +25,7 @@ "DisplayName:Email": "E-posta adresi", "DisplayName:PhoneNumber": "Telefon numarası", "DisplayName:TwoFactorEnabled": "İki aşamalı doğrulama", + "DisplayName:IsActive": "Aktif", "DisplayName:LockoutEnabled": "Başarısız giriş denemeleri sonrası hesabı kilitleme", "NewRole": "Yeni rol", "RoleName": "Rol adı", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json index ec3c4dbbb8..35fae96839 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json @@ -25,6 +25,7 @@ "DisplayName:Email": "邮箱地址", "DisplayName:PhoneNumber": "手机号码", "DisplayName:TwoFactorEnabled": "二次认证", + "DisplayName:IsActive": "启用", "DisplayName:LockoutEnabled": "登录失败,账户被锁定", "NewRole": "新角色", "RoleName": "角色名称", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json index 54298f530d..06f72cd5d0 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json @@ -25,6 +25,7 @@ "DisplayName:Email": "電子信箱地址", "DisplayName:PhoneNumber": "手機號碼", "DisplayName:TwoFactorEnabled": "二次認證", + "DisplayName:IsActive": "啟用", "DisplayName:LockoutEnabled": "登入失敗,帳號被鎖定", "NewRole": "新角色", "RoleName": "角色名稱", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs index f48553681c..befa288fda 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs @@ -82,6 +82,11 @@ namespace Volo.Abp.Identity /// True if the telephone number has been confirmed, otherwise false. public virtual bool PhoneNumberConfirmed { get; protected internal set; } + /// + /// Gets or sets a flag indicating if the user is active. + /// + public virtual bool IsActive { get; protected internal set; } + /// /// Gets or sets a flag indicating if two factor authentication is enabled for this user. /// @@ -155,6 +160,7 @@ namespace Volo.Abp.Identity NormalizedEmail = email.ToUpperInvariant(); ConcurrencyStamp = Guid.NewGuid().ToString(); SecurityStamp = Guid.NewGuid().ToString(); + IsActive = true; Roles = new Collection(); Claims = new Collection(); @@ -338,11 +344,6 @@ namespace Volo.Abp.Identity PhoneNumberConfirmed = confirmed; } - public override string ToString() - { - return $"{base.ToString()}, UserName = {UserName}"; - } - /// /// Normally use to change the phone number /// in the application code. @@ -356,5 +357,15 @@ namespace Volo.Abp.Identity PhoneNumber = phoneNumber; PhoneNumberConfirmed = !phoneNumber.IsNullOrWhiteSpace() && confirmed; } + + public virtual void SetIsActive(bool isActive) + { + IsActive = isActive; + } + + public override string ToString() + { + return $"{base.ToString()}, UserName = {UserName}"; + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml index 1aa5669b0c..2deaf0a9da 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml @@ -27,6 +27,7 @@ + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs index 03bbd859cc..c239ac2e48 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs @@ -79,6 +79,8 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } + public bool IsActive { get; set; } = true; + public bool LockoutEnabled { get; set; } = true; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml index 5bac90712e..b95068cd20 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml @@ -27,6 +27,7 @@ + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs index 29d044f9da..25affb88a3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs @@ -87,6 +87,8 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } + public bool IsActive { get; set; } + public bool LockoutEnabled { get; set; } } diff --git a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs index dc5d961f9a..b1b3368e5b 100644 --- a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs @@ -35,5 +35,15 @@ namespace Volo.Abp.Identity.AspNetCore result.ShouldBe("Failed"); } + + [Fact] + public async Task Should_Not_SignIn_If_User_Not_Active() + { + var result = await GetResponseAsStringAsync( + "api/signin-test/password?userName=bob&password=1q2w3E*" + ); + + result.ShouldBe("NotAllowed"); + } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs index 708deaa68e..a6e0f0c310 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs @@ -12,6 +12,7 @@ namespace Volo.Abp.Identity { private readonly IGuidGenerator _guidGenerator; private readonly IIdentityUserRepository _userRepository; + private readonly IdentityUserManager _userManager; private readonly IIdentityClaimTypeRepository _identityClaimTypeRepository; private readonly IIdentityRoleRepository _roleRepository; private readonly IOrganizationUnitRepository _organizationUnitRepository; @@ -32,6 +33,7 @@ namespace Volo.Abp.Identity public AbpIdentityTestDataBuilder( IGuidGenerator guidGenerator, IIdentityUserRepository userRepository, + IdentityUserManager userManager, IIdentityClaimTypeRepository identityClaimTypeRepository, IIdentityRoleRepository roleRepository, IOrganizationUnitRepository organizationUnitRepository, @@ -44,6 +46,7 @@ namespace Volo.Abp.Identity { _guidGenerator = guidGenerator; _userRepository = userRepository; + _userManager = userManager; _identityClaimTypeRepository = identityClaimTypeRepository; _roleRepository = roleRepository; _lookupNormalizer = lookupNormalizer; @@ -133,6 +136,10 @@ namespace Volo.Abp.Identity neo.AddClaim(_guidGenerator, new Claim("TestClaimType", "43")); neo.AddOrganizationUnit(_ou111.Id); await _userRepository.InsertAsync(neo); + + var bob = new IdentityUser(_testData.UserBobId, "bob", "bob@abp.io"); + bob.SetIsActive(false); + await _userManager.CreateAsync(bob, "1q2w3E*"); } private async Task AddLinkUsers() diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs index 10fcce89af..d2cb0f19b4 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.Identity public Guid UserJohnId { get; } = Guid.NewGuid(); public Guid UserDavidId { get; } = Guid.NewGuid(); public Guid UserNeoId { get; } = Guid.NewGuid(); + public Guid UserBobId { get; } = Guid.NewGuid(); public Guid AgeClaimId { get; } = Guid.NewGuid(); public Guid EducationClaimId { get; } = Guid.NewGuid(); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/FR.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/FR.json index 77ad4b10b5..406094d58a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/FR.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/FR.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId existe déjà: {ClientId}", "UserLockedOut": "Le compte utilisateur a été verrouillé en raison de tentatives de connexion non valides. Veuillez patienter un instant et réessayer.", "InvalidUserNameOrPassword": "Nom d'utilisateur ou mot de passe invalide!", - "LoginIsNotAllowed": "Vous n'êtes pas autorisé à vous connecter! Vous devez confirmer votre adresse e-mail / numéro de téléphone.", + "LoginIsNotAllowed": "Vous n'êtes pas autorisé à vous connecter ! Votre compte est inactif ou doit confirmer votre e-mail/numéro de téléphone.", "InvalidUsername": "Nom d'utilisateur ou mot de passe invalide!", "TheTargetUserIsNotLinkedToYou": "L'utilisateur cible n'est pas lié à vous!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ar.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ar.json index b228280c52..85eb99bc1d 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ar.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ar.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "موجود بالفعل: {ClientId}", "UserLockedOut": "تم قفل حساب المستخدم بسبب محاولات تسجيل الدخول غير الصالحة. يرجى الانتظار بعض الوقت والمحاولة مرة أخرى.", "InvalidUserNameOrPassword": "اسم مستخدم أو كلمة مرور غير صالحة!", - "LoginIsNotAllowed": "غير مسموح لك بتسجيل الدخول! أنت بحاجة إلى تأكيد بريدك الإلكتروني / رقم هاتفك.", + "LoginIsNotAllowed": "لا يسمح لك بتسجيل الدخول! حسابك غير نشط أو يحتاج إلى تأكيد بريدك الإلكتروني / رقم هاتفك.", "InvalidUsername": "اسم المستخدم أو كلمة المرور غير صالحة!" } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json index bee3e74fd9..97f3661f39 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId již existuje: {ClientId}", "UserLockedOut": "Tento uživatelský účet byl zablokován kvůli neplatným pokusům o přihlášení. Chvilku počkejte a zkuste to znovu.", "InvalidUserNameOrPassword": "Neplatné uživatelské jméno či heslo!", - "LoginIsNotAllowed": "Nemáte oprávnění se přihlásit! Musíte potvrdit svůj email/telefonní číslo.", + "LoginIsNotAllowed": "Nemáte oprávnění se přihlásit! Váš účet je neaktivní nebo potřebuje potvrdit váš e -mail/telefonní číslo.", "InvalidUsername": "Neplatné uživatelské jméno či heslo!" } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de-DE.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de-DE.json index bbb835af15..2d419907cc 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de-DE.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de-DE.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId existiert bereits: {ClientId}", "UserLockedOut": "Das Benutzerkonto wurde aufgrund ungültiger Anmeldeversuche gesperrt. Bitte warten Sie eine Weile und versuchen Sie es erneut.", "InvalidUserNameOrPassword": "Ungültiger Benutzername oder Passwort!", - "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Sie müssen Ihre E-Mail-Adresse / Telefonnummer bestätigen.", + "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Ihr Konto ist inaktiv oder muss Ihre E-Mail-/Telefonnummer bestätigen.", "InvalidUsername": "Ungültiger Benutzername oder Passwort!", "TheTargetUserIsNotLinkedToYou": "Der Zielbenutzer ist nicht mit Ihnen verknüpft!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de.json index 68e2cde8b8..8154b4375a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/de.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId bereits vorhanden: {ClientId}", "UserLockedOut": "Das Benutzerkonto wurde aufgrund ungültiger Anmeldeversuche ausgesperrt. Bitte warten Sie eine Weile und versuchen Sie es erneut.", "InvalidUserNameOrPassword": "Ungültiger Benutzername oder Passwort!", - "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Sie müssen Ihre E-Mail/Telefonnummer bestätigen.", + "LoginIsNotAllowed": "Sie dürfen sich nicht anmelden! Ihr Konto ist inaktiv oder muss Ihre E-Mail-/Telefonnummer bestätigen.", "InvalidUsername": "Ungültiger Benutzername oder Passwort!" } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en-GB.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en-GB.json index 7c3c87b884..173379249c 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en-GB.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en-GB.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId already exist: {ClientId}", "UserLockedOut": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.", "InvalidUserNameOrPassword": "Invalid username or password!", - "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", + "LoginIsNotAllowed": "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.", "InvalidUsername": "Invalid username or password!", "TheTargetUserIsNotLinkedToYou": "The target user is not linked to you!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json index 31a8ebad04..e756db2877 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId already exist: {ClientId}", "UserLockedOut": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.", "InvalidUserNameOrPassword": "Invalid username or password!", - "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", + "LoginIsNotAllowed": "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.", "InvalidUsername": "Invalid username or password!", "InvalidAuthenticatorCode": "Invalid authenticator code!", "TheTargetUserIsNotLinkedToYou": "The target user is not linked to you!" diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/es.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/es.json index 7a21c35184..2a2515bd97 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/es.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/es.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId ya existe: {ClientId}", "UserLockedOut": "La cuenta de usuario ha sido bloqueada debido a inicios de sesión no validos. Por favor, espera un momento e intentalo de nuevo.", "InvalidUserNameOrPassword": "Nombre de usuario o contraseña incorrecto!", - "LoginIsNotAllowed": "No puedes iniciar sesión!, necesitas confirmar tu e-mail/ número de teléfono.", + "LoginIsNotAllowed": "¡No está permitido iniciar sesión! Su cuenta está inactiva o necesita confirmar su correo electrónico / número de teléfono.", "InvalidUsername": "Nombre de usuario icorrecto", "TheTargetUserIsNotLinkedToYou": "El usuario de destino no está asociado a usted." } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/fi.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/fi.json index 21687921a9..bfb746e632 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/fi.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/fi.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId on jo olemassa: {ClientId}", "UserLockedOut": "Käyttäjätili on lukittu virheellisten kirjautumisyritysten vuoksi. Odota hetki ja yritä uudelleen.", "InvalidUserNameOrPassword": "Väärä käyttäjänimi tai salasana!", - "LoginIsNotAllowed": "Et saa kirjautua sisään! Sinun on vahvistettava sähköpostiosoitteesi / puhelinnumerosi.", + "LoginIsNotAllowed": "Et saa kirjautua sisään! Tilisi on passiivinen tai sinun on vahvistettava sähköpostiosoitteesi/puhelinnumerosi.", "InvalidUsername": "Väärä käyttäjänimi tai salasana!", "TheTargetUserIsNotLinkedToYou": "Kohdekäyttäjä ei ole linkitetty sinuun!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hi.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hi.json index 2daea29000..cf612a909b 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hi.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hi.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId पहले से मौजूद है: {ClientId}", "UserLockedOut": "अमान्य लॉगिन प्रयासों के कारण उपयोगकर्ता खाता बंद कर दिया गया है। कृपया थोड़ी देर प्रतीक्षा करें और पुन: प्रयास करें।", "InvalidUserNameOrPassword": "अमान्य उपयोगकर्ता नाम या पासवर्ड!", - "LoginIsNotAllowed": "आपको लॉगिन करने की अनुमति नहीं है! आपको अपने ईमेल / फोन नंबर की पुष्टि करनी होगी।", + "LoginIsNotAllowed": "आपको लॉगिन करने की अनुमति नहीं है! आपका खाता निष्क्रिय है या आपके ईमेल/फ़ोन नंबर की पुष्टि करने की आवश्यकता है।", "InvalidUsername": "अमान्य उपयोगकर्ता नाम या पासवर्ड!", "TheTargetUserIsNotLinkedToYou": "लक्ष्य उपयोगकर्ता आपसे जुड़ा नहीं है!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hu.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hu.json index e4109a2ba7..5bd3a2676a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hu.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/hu.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId már létezik: {ClientId}", "UserLockedOut": "A felhasználói fiókot érvénytelen bejelentkezési kísérletek miatt zároltuk. Kérjük, várjon egy kicsit, és próbálja újra.", "InvalidUserNameOrPassword": "Érvénytelen felhasználónév vagy jelszó!", - "LoginIsNotAllowed": "Ön nem léphet be! Meg kell erősítenie e-mail címét / telefonszámát.", + "LoginIsNotAllowed": "A bejelentkezés nem engedélyezett! Fiókja inaktív, vagy meg kell erősítenie e -mail címét/telefonszámát.", "InvalidUsername": "Érvénytelen felhasználónév vagy jelszó!", "TheTargetUserIsNotLinkedToYou": "A célfelhasználó nincs hozzád kapcsolódva!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/it.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/it.json index 2b564f3af7..cde4f688f4 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/it.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/it.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId già esistente: {ClientId}", "UserLockedOut": "L'account utente è stato bloccato a causa di tentativi di accesso non validi. Attendi qualche istante e riprova.", "InvalidUserNameOrPassword": "Username o password non validi!", - "LoginIsNotAllowed": "Non sei autorizzato ad accedere! Devi confermare la tua email/numero di telefono.", + "LoginIsNotAllowed": "Non sei autorizzato ad accedere! Il tuo account non è attivo o deve confermare la tua email/numero di telefono.", "InvalidUsername": "Username o password non validi!", "InvalidAuthenticatorCode": "Codice autenticatore non valido!", "TheTargetUserIsNotLinkedToYou": "L'utente di destinazione non è collegato a te!" diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/nl.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/nl.json index 9b6943c54f..bcce8f1e55 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/nl.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/nl.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId bestaat al: {ClientId}", "UserLockedOut": "Het gebruikersaccount is geblokkeerd vanwege ongeldige inlogpogingen. Wacht even en probeer het opnieuw.", "InvalidUserNameOrPassword": "ongeldige gebruikersnaam of wachtwoord!", - "LoginIsNotAllowed": "U mag niet inloggen! U moet uw e-mailadres / telefoonnummer bevestigen.", + "LoginIsNotAllowed": "U mag niet inloggen! Uw account is inactief of moet uw e-mailadres/telefoonnummer bevestigen.", "InvalidUsername": "Ongeldige gebruikersnaam of wachtwoord!", "TheTargetUserIsNotLinkedToYou": "De beoogde gebruiker is niet aan jou gekoppeld!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ro-RO.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ro-RO.json index 7917e7118c..7841a1df8a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ro-RO.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ro-RO.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "Id-ul de client există deja: {ClientId}", "UserLockedOut": "Contul de utilizator a fost blocat din cauza încercărilor de autentificare eşuate. Vă rugăm să aşteptaţi puţin şi după să încercaţi din nou.", "InvalidUserNameOrPassword": "Nume de utilizator sau parolă invalidă!", - "LoginIsNotAllowed": "Nu vă este permisă autentificarea! Trebuie să vă confirmaţi emailul/numărul de telefon.", + "LoginIsNotAllowed": "Nu ai voie să te autentifici! Contul dvs. este inactiv sau trebuie să vă confirme numărul de e-mail / telefon.", "InvalidUsername": "Nume de utilizator sau parolă invalidă!", "InvalidAuthenticatorCode": "Cod de autentificare invalid!", "TheTargetUserIsNotLinkedToYou": "Utilizatorul ţintă nu este conectat la dumneavoastră!" diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ru.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ru.json index 808962d8e5..5710bc16e3 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ru.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/ru.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "Параметр ClientID уже существует: {ClientId}", "UserLockedOut": "Учетная запись пользователя была заблокирована из-за неудачных попыток входа в систему. Пожалуйста, попробуйте позже.", "InvalidUserNameOrPassword": "Неверное имя пользователя или пароль!", - "LoginIsNotAllowed": "У вас нет разрешения на вход в систему. Необходимо подтвердить свой адрес электронной почты/номер телефона.", + "LoginIsNotAllowed": "Вам не разрешено входить в систему! Ваша учетная запись неактивна или вам необходимо подтвердить адрес электронной почты / номер телефона.", "InvalidUsername": "Неверное имя пользователя или пароль!" } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sk.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sk.json index 744a92ac1d..18435aaae8 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sk.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sk.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId už existuje: {ClientId}", "UserLockedOut": "Používateľské konto bolo uzamknuté z dôvodu viacerých neplatných pokusov o prihlásenie. Chvíľu počkajte a skúste sa prihlásiť znova.", "InvalidUserNameOrPassword": "Nesprávne používateľské meno alebo heslo!", - "LoginIsNotAllowed": "Nemáte povolené prihlásiť sa! Musíte potvrdiť svoj email/telefónne číslo.", + "LoginIsNotAllowed": "Nemáte povolenie sa prihlásiť! Váš účet je neaktívny alebo potrebuje potvrdiť váš e -mail/telefónne číslo.", "InvalidUsername": "Nesprávne používateľské meno alebo heslo!", "TheTargetUserIsNotLinkedToYou": "Cieľový používateľ nie je s vami prepojený!" } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sl.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sl.json index b73f64b00e..c5f2dc73de 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sl.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/sl.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId že obstaja: {ClientId}", "UserLockedOut": "Uporabniški račun je bil blokiran zaradi neveljavnih poskusov prijave. Počakajte nekaj časa in poskusite znova.", "InvalidUserNameOrPassword": "Napačno uporabniško ime ali geslo!", - "LoginIsNotAllowed": "Nimate dovoljenja za prijavo! Potrditi morate svojo e-pošto / telefonsko številko.", + "LoginIsNotAllowed": "Ne smete se prijaviti! Vaš račun je neaktiven ali mora potrditi vaš e -poštni naslov/telefonsko številko.", "InvalidUsername": "Napačno uporabniško ime ali geslo!" } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json index b5c072c7e6..08178cd306 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId already zaten mevcut: {ClientId}", "UserLockedOut": "Kullanıcı hesabı hatalı giriş denemeleri nedeniyle kilitlenmiştir. Lütfen bir süre bekleyip tekrar deneyin.", "InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!", - "LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.", + "LoginIsNotAllowed": "Giriş yapmanıza izin verilmiyor! Hesabınız etkin değil veya e-postanızı/telefon numaranızı onaylamanız gerekiyor.", "InvalidUsername": "Kullanıcı adı ya da şifre geçersiz!", "InvalidAuthenticatorCode": "Geçersiz kimlik doğrulama kodu!", "TheTargetUserIsNotLinkedToYou": "Hedef kullanıcı sizinle bağlantılı değil!" diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json index 7b90b802e7..20bc8b43f0 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json @@ -7,7 +7,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId已经存在: {ClientId}", "UserLockedOut": "登录失败,用户账户已被锁定.请稍后再试.", "InvalidUserNameOrPassword": "用户名或密码错误!", - "LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.", + "LoginIsNotAllowed": "无法登录!你的账号未激活或者需要验证邮箱地址/手机号.", "InvalidUsername": "用户名或密码错误!", "InvalidAuthenticatorCode": "验证码无效!", "TheTargetUserIsNotLinkedToYou": "目标用户未和你有关联!" diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hant.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hant.json index 0ffeae8f81..bf4341055e 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hant.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hant.json @@ -6,7 +6,7 @@ "Volo.IdentityServer:DuplicateClientId": "ClientId已經存在: {ClientId}", "UserLockedOut": "登錄失敗,用戶賬戶已被鎖定.請稍後再試.", "InvalidUserNameOrPassword": "用戶名或密碼錯誤!", - "LoginIsNotAllowed": "無法登錄!妳需要驗證郵箱地址/手機號.", + "LoginIsNotAllowed": "無法登入!你的賬號未激活或者需要驗證郵箱地址/手機號碼.", "InvalidUsername": "用戶名或密碼錯誤!" } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs index 224abc6c10..e09315c69f 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs @@ -40,5 +40,11 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity await base.IsActiveAsync(context); } } + + [UnitOfWork] + public override Task IsUserActiveAsync(IdentityUser user) + { + return Task.FromResult(user.IsActive); + } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs index 5da11fe767..a240732236 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs @@ -69,7 +69,7 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore public virtual async Task GetCountAsync(string filter = null, CancellationToken cancellationToken = default) { - return await this + return await (await GetQueryableAsync()) .WhereIf( !filter.IsNullOrWhiteSpace(), u => diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs index 635d9fc8fd..55ed754509 100644 --- a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.Users.EntityFrameworkCore public async Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default) { - return await this.OrderBy(x => x.Id).FirstOrDefaultAsync(u => u.UserName == userName, GetCancellationToken(cancellationToken)); + return await (await GetDbSetAsync()).OrderBy(x => x.Id).FirstOrDefaultAsync(u => u.UserName == userName, GetCancellationToken(cancellationToken)); } public virtual async Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default) diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index a95a2f4671..5d1e203902 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -91,6 +91,7 @@ $projects = ( "framework/src/Volo.Abp.Ddd.Application", "framework/src/Volo.Abp.Ddd.Application.Contracts", "framework/src/Volo.Abp.Ddd.Domain", + "framework/src/Volo.Abp.DistributedLocking", "framework/src/Volo.Abp.Emailing", "framework/src/Volo.Abp.EntityFrameworkCore", "framework/src/Volo.Abp.EntityFrameworkCore.MySQL", @@ -101,6 +102,7 @@ $projects = ( "framework/src/Volo.Abp.EntityFrameworkCore.SqlServer", "framework/src/Volo.Abp.EventBus.Abstractions", "framework/src/Volo.Abp.EventBus", + "framework/src/Volo.Abp.EventBus.Boxes", "framework/src/Volo.Abp.EventBus.RabbitMQ", "framework/src/Volo.Abp.EventBus.Kafka", "framework/src/Volo.Abp.EventBus.Rebus", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.Designer.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.Designer.cs similarity index 99% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.Designer.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.Designer.cs index 060cb48e8a..fa24bfe18c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.Designer.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.Designer.cs @@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20210615091011_Initial")] + [Migration("20210909052638_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -20,7 +20,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.7") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -638,6 +638,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.cs similarity index 99% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.cs index 8a02b53ba3..8e2d7527d5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210615091011_Initial.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20210909052638_Initial.cs @@ -253,6 +253,7 @@ namespace MyCompanyName.MyProjectName.Migrations IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false), TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs index 56ac0762cc..928c1b96eb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.7") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -636,6 +636,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.Designer.cs index 3188e8f984..8107c2dcbe 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.Designer.cs @@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20210528093547_Initial")] + [Migration("20210909052730_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -20,7 +20,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -580,6 +580,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.cs index c982c31c32..0d9af49008 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210528093547_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20210909052730_Initial.cs @@ -232,6 +232,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false), TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs index 2d2e6dafd5..c394d8c737 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -578,6 +578,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.Designer.cs index 2a8dcb3b75..b9edb3b1a7 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.Designer.cs @@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(IdentityServerHostMigrationsDbContext))] - [Migration("20210528093513_Initial")] + [Migration("20210909052712_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -20,7 +20,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -580,6 +580,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.cs index bf39015d0f..5333a6a34e 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210528093513_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20210909052712_Initial.cs @@ -232,6 +232,7 @@ namespace MyCompanyName.MyProjectName.Migrations IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false), TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs index 35ad1df358..aa156c5d53 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -578,6 +578,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.Designer.cs index 43912d5e1a..993934aedb 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.Designer.cs @@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20210528093522_Initial")] + [Migration("20210909052706_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -20,7 +20,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -580,6 +580,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.cs index b5e7c7492e..b7465b0dcb 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210528093522_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20210909052706_Initial.cs @@ -232,6 +232,7 @@ namespace MyCompanyName.MyProjectName.Migrations IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false), TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs index bc4abaa1af..472749a2a0 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.6") + .HasAnnotation("ProductVersion", "5.0.9") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => @@ -578,6 +578,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IsActive") + .HasColumnType("bit"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/test/DistEvents/DistDemoApp/DistDemoApp.csproj b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj similarity index 66% rename from test/DistEvents/DistDemoApp/DistDemoApp.csproj rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj index 524fda1046..a3e769e45b 100644 --- a/test/DistEvents/DistDemoApp/DistDemoApp.csproj +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj @@ -3,19 +3,13 @@ Exe net6.0 + DistDemoApp - - - - - - - - - + + diff --git a/test/DistEvents/DistDemoApp/DistDemoAppModule.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoAppEfCoreRabbitMqModule.cs similarity index 51% rename from test/DistEvents/DistDemoApp/DistDemoAppModule.cs rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoAppEfCoreRabbitMqModule.cs index f69f9c2380..da2e7a1445 100644 --- a/test/DistEvents/DistDemoApp/DistDemoAppModule.cs +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/DistDemoAppEfCoreRabbitMqModule.cs @@ -1,22 +1,22 @@ using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Autofac; -using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.DistributedEvents; using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.RabbitMq; using Volo.Abp.Modularity; namespace DistDemoApp { [DependsOn( typeof(AbpEntityFrameworkCoreSqlServerModule), - typeof(AbpAutofacModule) + typeof(AbpEventBusRabbitMqModule), + typeof(DistDemoAppSharedModule) )] - public class DistDemoAppModule : AbpModule + public class DistDemoAppEfCoreRabbitMqModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddHostedService(); - context.Services.AddAbpDbContext(options => { options.AddDefaultRepositories(); @@ -27,10 +27,17 @@ namespace DistDemoApp options.UseSqlServer(); }); - Configure(options => + Configure(options => { - options.EtoMappings.Add(); - options.AutoEventSelectors.Add(); + options.Outboxes.Configure(config => + { + config.UseDbContext(); + }); + + options.Inboxes.Configure(config => + { + config.UseDbContext(); + }); }); } } diff --git a/test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.Designer.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.Designer.cs similarity index 57% rename from test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.Designer.cs rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.Designer.cs index 4c2cf9abbb..292cff66f9 100644 --- a/test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.Designer.cs +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.Designer.cs @@ -11,8 +11,8 @@ using Volo.Abp.EntityFrameworkCore; namespace DistDemoApp.Migrations { [DbContext(typeof(TodoDbContext))] - [Migration("20210825112717_Added_Summary_Table")] - partial class Added_Summary_Table + [Migration("20210910152547_Added_Boxes_Initial")] + partial class Added_Boxes_Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -89,6 +89,73 @@ namespace DistDemoApp.Migrations b.ToTable("TodoSummaries"); }); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.IncomingEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("nvarchar(450)"); + + b.Property("Processed") + .HasColumnType("bit"); + + b.Property("ProcessedTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.HasIndex("Processed", "CreationTime"); + + b.ToTable("AbpEventInbox"); + }); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.HasKey("Id"); + + b.ToTable("AbpEventOutbox"); + }); #pragma warning restore 612, 618 } } diff --git a/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.cs new file mode 100644 index 0000000000..9094eaa8c9 --- /dev/null +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/20210910152547_Added_Boxes_Initial.cs @@ -0,0 +1,103 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace DistDemoApp.Migrations +{ + public partial class Added_Boxes_Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpEventInbox", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + MessageId = table.Column(type: "nvarchar(450)", nullable: true), + EventName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EventData = table.Column(type: "varbinary(max)", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + Processed = table.Column(type: "bit", nullable: false), + ProcessedTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEventInbox", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpEventOutbox", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + EventName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EventData = table.Column(type: "varbinary(max)", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEventOutbox", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TodoItems", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Text = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TodoItems", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TodoSummaries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Year = table.Column(type: "int", nullable: false), + Month = table.Column(type: "tinyint", nullable: false), + Day = table.Column(type: "tinyint", nullable: false), + TotalCount = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TodoSummaries", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEventInbox_MessageId", + table: "AbpEventInbox", + column: "MessageId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEventInbox_Processed_CreationTime", + table: "AbpEventInbox", + columns: new[] { "Processed", "CreationTime" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpEventInbox"); + + migrationBuilder.DropTable( + name: "AbpEventOutbox"); + + migrationBuilder.DropTable( + name: "TodoItems"); + + migrationBuilder.DropTable( + name: "TodoSummaries"); + } + } +} diff --git a/test/DistEvents/DistDemoApp/Migrations/TodoDbContextModelSnapshot.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/TodoDbContextModelSnapshot.cs similarity index 58% rename from test/DistEvents/DistDemoApp/Migrations/TodoDbContextModelSnapshot.cs rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/TodoDbContextModelSnapshot.cs index 99f0f92ab7..57e8c14442 100644 --- a/test/DistEvents/DistDemoApp/Migrations/TodoDbContextModelSnapshot.cs +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Migrations/TodoDbContextModelSnapshot.cs @@ -87,6 +87,73 @@ namespace DistDemoApp.Migrations b.ToTable("TodoSummaries"); }); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.IncomingEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MessageId") + .HasColumnType("nvarchar(450)"); + + b.Property("Processed") + .HasColumnType("bit"); + + b.Property("ProcessedTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.HasIndex("Processed", "CreationTime"); + + b.ToTable("AbpEventInbox"); + }); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.HasKey("Id"); + + b.ToTable("AbpEventOutbox"); + }); #pragma warning restore 612, 618 } } diff --git a/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Program.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Program.cs new file mode 100644 index 0000000000..597b29ae22 --- /dev/null +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/Program.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; + +namespace DistDemoApp +{ + public class Program + { + public static async Task Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Async(c => c.File("Logs/logs.txt")) + .WriteTo.Async(c => c.Console()) + .CreateLogger(); + + try + { + Log.Information("Starting console host."); + await CreateHostBuilder(args).RunConsoleAsync(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseAutofac() + .UseSerilog() + .ConfigureAppConfiguration((context, config) => + { + //setup your additional configuration sources + }) + .ConfigureServices((hostContext, services) => + { + services.AddApplication(); + }); + } +} diff --git a/test/DistEvents/DistDemoApp/TodoDbContext.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContext.cs similarity index 63% rename from test/DistEvents/DistDemoApp/TodoDbContext.cs rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContext.cs index 000f18c802..5a1ddd2c3f 100644 --- a/test/DistEvents/DistDemoApp/TodoDbContext.cs +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContext.cs @@ -1,13 +1,16 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Entities; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.DistributedEvents; namespace DistDemoApp { - public class TodoDbContext : AbpDbContext + public class TodoDbContext : AbpDbContext, IHasEventOutbox, IHasEventInbox { public DbSet TodoItems { get; set; } public DbSet TodoSummaries { get; set; } + public DbSet OutgoingEvents { get; set; } + public DbSet IncomingEvents { get; set; } public TodoDbContext(DbContextOptions options) : base(options) @@ -18,6 +21,9 @@ namespace DistDemoApp protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureEventOutbox(); + modelBuilder.ConfigureEventInbox(); modelBuilder.Entity(b => { diff --git a/test/DistEvents/DistDemoApp/TodoDbContextFactory.cs b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContextFactory.cs similarity index 100% rename from test/DistEvents/DistDemoApp/TodoDbContextFactory.cs rename to test/DistEvents/DistDemoApp.EfCoreRabbitMq/TodoDbContextFactory.cs diff --git a/test/DistEvents/DistDemoApp.EfCoreRabbitMq/appsettings.json b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/appsettings.json new file mode 100644 index 0000000000..5359c021b7 --- /dev/null +++ b/test/DistEvents/DistDemoApp.EfCoreRabbitMq/appsettings.json @@ -0,0 +1,19 @@ +{ + "ConnectionStrings": { + "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=DistEventsDemo;Trusted_Connection=True" + }, + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "localhost" + } + }, + "EventBus": { + "ClientName": "DistDemoApp", + "ExchangeName": "DistDemo" + } + }, + "Redis": { + "Configuration": "127.0.0.1" + } +} diff --git a/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj b/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj new file mode 100644 index 0000000000..dbb980b427 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj @@ -0,0 +1,21 @@ + + + + Exe + net6.0 + DistDemoApp + + + + + + + + + + + Always + + + + diff --git a/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoAppMongoDbKafkaModule.cs b/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoAppMongoDbKafkaModule.cs new file mode 100644 index 0000000000..b2e41b6ca7 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbKafka/DistDemoAppMongoDbKafkaModule.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Kafka; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; +using Volo.Abp.MongoDB.DistributedEvents; + +namespace DistDemoApp +{ + [DependsOn( + typeof(AbpMongoDbModule), + typeof(AbpEventBusKafkaModule), + typeof(DistDemoAppSharedModule) + )] + public class DistDemoAppMongoDbKafkaModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(options => + { + options.AddDefaultRepositories(); + }); + + Configure(options => + { + options.Outboxes.Configure(config => + { + config.UseMongoDbContext(); + }); + + options.Inboxes.Configure(config => + { + config.UseMongoDbContext(); + }); + }); + } + } +} diff --git a/test/DistEvents/DistDemoApp/Program.cs b/test/DistEvents/DistDemoApp.MongoDbKafka/Program.cs similarity index 95% rename from test/DistEvents/DistDemoApp/Program.cs rename to test/DistEvents/DistDemoApp.MongoDbKafka/Program.cs index 676a07e357..b048c17389 100644 --- a/test/DistEvents/DistDemoApp/Program.cs +++ b/test/DistEvents/DistDemoApp.MongoDbKafka/Program.cs @@ -51,7 +51,7 @@ namespace DistDemoApp }) .ConfigureServices((hostContext, services) => { - services.AddApplication(); + services.AddApplication(); }); } } diff --git a/test/DistEvents/DistDemoApp.MongoDbKafka/TodoMongoDbContext.cs b/test/DistEvents/DistDemoApp.MongoDbKafka/TodoMongoDbContext.cs new file mode 100644 index 0000000000..a7f1b78f86 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbKafka/TodoMongoDbContext.cs @@ -0,0 +1,26 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; +using Volo.Abp.MongoDB.DistributedEvents; + +namespace DistDemoApp +{ + [ConnectionStringName("Default")] + public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox + { + public IMongoCollection TodoItems => Collection(); + public IMongoCollection TodoSummaries => Collection(); + + public IMongoCollection OutgoingEvents + { + get => Collection(); + set {} + } + public IMongoCollection IncomingEvents + { + get => Collection(); + set {} + } + } + +} diff --git a/test/DistEvents/DistDemoApp.MongoDbKafka/appsettings.json b/test/DistEvents/DistDemoApp.MongoDbKafka/appsettings.json new file mode 100644 index 0000000000..d8c528fe87 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbKafka/appsettings.json @@ -0,0 +1,19 @@ +{ + "ConnectionStrings": { + "Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo" + }, + "Kafka": { + "Connections": { + "Default": { + "BootstrapServers": "localhost:9092" + } + }, + "EventBus": { + "GroupId": "DistDemoApp", + "TopicName": "DistDemoTopic" + } + }, + "Redis": { + "Configuration": "127.0.0.1" + } +} diff --git a/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj b/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj new file mode 100644 index 0000000000..5698855ebb --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj @@ -0,0 +1,21 @@ + + + + Exe + net6.0 + DistDemoApp + + + + + + + + + + + Always + + + + diff --git a/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoAppMongoDbRebusModule.cs b/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoAppMongoDbRebusModule.cs new file mode 100644 index 0000000000..21dab7b9b5 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbRebus/DistDemoAppMongoDbRebusModule.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.DependencyInjection; +using Rebus.Persistence.InMem; +using Rebus.Transport.InMem; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Rebus; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; +using Volo.Abp.MongoDB.DistributedEvents; + +namespace DistDemoApp +{ + [DependsOn( + typeof(AbpMongoDbModule), + typeof(AbpEventBusRebusModule), + typeof(DistDemoAppSharedModule) + )] + public class DistDemoAppMongoDbRebusModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(options => + { + options.InputQueueName = "eventbus"; + options.Configurer = rebusConfigurer => + { + rebusConfigurer.Transport(t => t.UseInMemoryTransport(new InMemNetwork(), "eventbus")); + rebusConfigurer.Subscriptions(s => s.StoreInMemory()); + }; + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(options => + { + options.AddDefaultRepositories(); + }); + + Configure(options => + { + options.Outboxes.Configure(config => + { + config.UseMongoDbContext(); + }); + + options.Inboxes.Configure(config => + { + config.UseMongoDbContext(); + }); + }); + } + } +} diff --git a/test/DistEvents/DistDemoApp.MongoDbRebus/Program.cs b/test/DistEvents/DistDemoApp.MongoDbRebus/Program.cs new file mode 100644 index 0000000000..a79ad1b1bf --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbRebus/Program.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; + +namespace DistDemoApp +{ + public class Program + { + public static async Task Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Async(c => c.File("Logs/logs.txt")) + .WriteTo.Async(c => c.Console()) + .CreateLogger(); + + try + { + Log.Information("Starting console host."); + await CreateHostBuilder(args).RunConsoleAsync(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseAutofac() + .UseSerilog() + .ConfigureAppConfiguration((context, config) => + { + //setup your additional configuration sources + }) + .ConfigureServices((hostContext, services) => + { + services.AddApplication(); + }); + } +} diff --git a/test/DistEvents/DistDemoApp.MongoDbRebus/TodoMongoDbContext.cs b/test/DistEvents/DistDemoApp.MongoDbRebus/TodoMongoDbContext.cs new file mode 100644 index 0000000000..95370bb4d2 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbRebus/TodoMongoDbContext.cs @@ -0,0 +1,19 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; +using Volo.Abp.MongoDB.DistributedEvents; + +namespace DistDemoApp +{ + [ConnectionStringName("Default")] + public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox + { + public IMongoCollection TodoItems => Collection(); + public IMongoCollection TodoSummaries => Collection(); + + public IMongoCollection OutgoingEvents => Collection(); + + public IMongoCollection IncomingEvents => Collection(); + } + +} diff --git a/test/DistEvents/DistDemoApp.MongoDbRebus/appsettings.json b/test/DistEvents/DistDemoApp.MongoDbRebus/appsettings.json new file mode 100644 index 0000000000..d8c528fe87 --- /dev/null +++ b/test/DistEvents/DistDemoApp.MongoDbRebus/appsettings.json @@ -0,0 +1,19 @@ +{ + "ConnectionStrings": { + "Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo" + }, + "Kafka": { + "Connections": { + "Default": { + "BootstrapServers": "localhost:9092" + } + }, + "EventBus": { + "GroupId": "DistDemoApp", + "TopicName": "DistDemoTopic" + } + }, + "Redis": { + "Configuration": "127.0.0.1" + } +} diff --git a/test/DistEvents/DistDemoApp/DemoService.cs b/test/DistEvents/DistDemoApp.Shared/DemoService.cs similarity index 100% rename from test/DistEvents/DistDemoApp/DemoService.cs rename to test/DistEvents/DistDemoApp.Shared/DemoService.cs diff --git a/test/DistEvents/DistDemoApp.Shared/DistDemoApp.Shared.csproj b/test/DistEvents/DistDemoApp.Shared/DistDemoApp.Shared.csproj new file mode 100644 index 0000000000..4f5cb8ad84 --- /dev/null +++ b/test/DistEvents/DistDemoApp.Shared/DistDemoApp.Shared.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + DistDemoApp + + + + + + + + + + + + + + + + + + diff --git a/test/DistEvents/DistDemoApp/MyProjectNameHostedService.cs b/test/DistEvents/DistDemoApp.Shared/DistDemoAppHostedService.cs similarity index 90% rename from test/DistEvents/DistDemoApp/MyProjectNameHostedService.cs rename to test/DistEvents/DistDemoApp.Shared/DistDemoAppHostedService.cs index 7522d9fe9d..ba72d6902a 100644 --- a/test/DistEvents/DistDemoApp/MyProjectNameHostedService.cs +++ b/test/DistEvents/DistDemoApp.Shared/DistDemoAppHostedService.cs @@ -6,13 +6,13 @@ using Volo.Abp; namespace DistDemoApp { - public class MyProjectNameHostedService : IHostedService + public class DistDemoAppHostedService : IHostedService { private readonly IAbpApplicationWithExternalServiceProvider _application; private readonly IServiceProvider _serviceProvider; private readonly DemoService _demoService; - public MyProjectNameHostedService( + public DistDemoAppHostedService( IAbpApplicationWithExternalServiceProvider application, IServiceProvider serviceProvider, DemoService demoService) diff --git a/test/DistEvents/DistDemoApp.Shared/DistDemoAppSharedModule.cs b/test/DistEvents/DistDemoApp.Shared/DistDemoAppSharedModule.cs new file mode 100644 index 0000000000..138b64f567 --- /dev/null +++ b/test/DistEvents/DistDemoApp.Shared/DistDemoAppSharedModule.cs @@ -0,0 +1,39 @@ +using Medallion.Threading; +using Medallion.Threading.Redis; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; +using Volo.Abp.Autofac; +using Volo.Abp.Domain; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus.Boxes; +using Volo.Abp.Modularity; + +namespace DistDemoApp +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpDddDomainModule), + typeof(AbpEventBusBoxesModule) + )] + public class DistDemoAppSharedModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + context.Services.AddHostedService(); + + Configure(options => + { + options.EtoMappings.Add(); + options.AutoEventSelectors.Add(); + }); + + context.Services.AddSingleton(sp => + { + var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + return new RedisDistributedSynchronizationProvider(connection.GetDatabase()); + }); + } + } +} \ No newline at end of file diff --git a/test/DistEvents/DistDemoApp/TodoEventHandler.cs b/test/DistEvents/DistDemoApp.Shared/TodoEventHandler.cs similarity index 96% rename from test/DistEvents/DistDemoApp/TodoEventHandler.cs rename to test/DistEvents/DistDemoApp.Shared/TodoEventHandler.cs index 73c7040732..7a69ceed1e 100644 --- a/test/DistEvents/DistDemoApp/TodoEventHandler.cs +++ b/test/DistEvents/DistDemoApp.Shared/TodoEventHandler.cs @@ -41,8 +41,6 @@ namespace DistDemoApp } Console.WriteLine("Increased total count: " + todoSummary); - - throw new ApplicationException("Thrown to rollback the UOW!"); } public async Task HandleEventAsync(EntityDeletedEto eventData) diff --git a/test/DistEvents/DistDemoApp/TodoItem.cs b/test/DistEvents/DistDemoApp.Shared/TodoItem.cs similarity index 100% rename from test/DistEvents/DistDemoApp/TodoItem.cs rename to test/DistEvents/DistDemoApp.Shared/TodoItem.cs diff --git a/test/DistEvents/DistDemoApp/TodoItemEto.cs b/test/DistEvents/DistDemoApp.Shared/TodoItemEto.cs similarity index 100% rename from test/DistEvents/DistDemoApp/TodoItemEto.cs rename to test/DistEvents/DistDemoApp.Shared/TodoItemEto.cs diff --git a/test/DistEvents/DistDemoApp/TodoItemObjectMapper.cs b/test/DistEvents/DistDemoApp.Shared/TodoItemObjectMapper.cs similarity index 100% rename from test/DistEvents/DistDemoApp/TodoItemObjectMapper.cs rename to test/DistEvents/DistDemoApp.Shared/TodoItemObjectMapper.cs diff --git a/test/DistEvents/DistDemoApp/TodoSummary.cs b/test/DistEvents/DistDemoApp.Shared/TodoSummary.cs similarity index 100% rename from test/DistEvents/DistDemoApp/TodoSummary.cs rename to test/DistEvents/DistDemoApp.Shared/TodoSummary.cs diff --git a/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.Designer.cs b/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.Designer.cs deleted file mode 100644 index 990ef3e967..0000000000 --- a/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.Designer.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -using System; -using DistDemoApp; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Volo.Abp.EntityFrameworkCore; - -namespace DistDemoApp.Migrations -{ - [DbContext(typeof(TodoDbContext))] - [Migration("20210825110134_Initial")] - partial class Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.9") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("DistDemoApp.TodoItem", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasMaxLength(40) - .HasColumnType("nvarchar(40)") - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("ExtraProperties") - .HasColumnType("nvarchar(max)") - .HasColumnName("ExtraProperties"); - - b.Property("Text") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.HasKey("Id"); - - b.ToTable("TodoItems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.cs b/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.cs deleted file mode 100644 index b84d1f433f..0000000000 --- a/test/DistEvents/DistDemoApp/Migrations/20210825110134_Initial.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace DistDemoApp.Migrations -{ - public partial class Initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "TodoItems", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Text = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_TodoItems", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "TodoItems"); - } - } -} diff --git a/test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.cs b/test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.cs deleted file mode 100644 index 7a7f167470..0000000000 --- a/test/DistEvents/DistDemoApp/Migrations/20210825112717_Added_Summary_Table.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace DistDemoApp.Migrations -{ - public partial class Added_Summary_Table : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "TodoSummaries", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Year = table.Column(type: "int", nullable: false), - Month = table.Column(type: "tinyint", nullable: false), - Day = table.Column(type: "tinyint", nullable: false), - TotalCount = table.Column(type: "int", nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_TodoSummaries", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "TodoSummaries"); - } - } -} diff --git a/test/DistEvents/DistDemoApp/appsettings.json b/test/DistEvents/DistDemoApp/appsettings.json deleted file mode 100644 index 6bb7ba54ca..0000000000 --- a/test/DistEvents/DistDemoApp/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=DistEventsDemo;Trusted_Connection=True" - } -} \ No newline at end of file diff --git a/test/DistEvents/DistEventsDemo.sln b/test/DistEvents/DistEventsDemo.sln index de7a36535a..e6c3348a5f 100644 --- a/test/DistEvents/DistEventsDemo.sln +++ b/test/DistEvents/DistEventsDemo.sln @@ -1,6 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp", "DistDemoApp\DistDemoApp.csproj", "{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp.EfCoreRabbitMq", "DistDemoApp.EfCoreRabbitMq\DistDemoApp.EfCoreRabbitMq.csproj", "{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp.MongoDbKafka", "DistDemoApp.MongoDbKafka\DistDemoApp.MongoDbKafka.csproj", "{19762F48-4CDB-4723-A72F-D859C0DC815A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp.Shared", "DistDemoApp.Shared\DistDemoApp.Shared.csproj", "{C515F4E2-0ED3-4561-BC58-FC633B50E2EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp.MongoDbRebus", "DistDemoApp.MongoDbRebus\DistDemoApp.MongoDbRebus.csproj", "{4FB63540-4CC5-4A7B-900B-F5FCD907456E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -12,5 +18,17 @@ Global {10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Release|Any CPU.ActiveCfg = Release|Any CPU {10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Release|Any CPU.Build.0 = Release|Any CPU + {19762F48-4CDB-4723-A72F-D859C0DC815A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19762F48-4CDB-4723-A72F-D859C0DC815A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19762F48-4CDB-4723-A72F-D859C0DC815A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19762F48-4CDB-4723-A72F-D859C0DC815A}.Release|Any CPU.Build.0 = Release|Any CPU + {C515F4E2-0ED3-4561-BC58-FC633B50E2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C515F4E2-0ED3-4561-BC58-FC633B50E2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C515F4E2-0ED3-4561-BC58-FC633B50E2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C515F4E2-0ED3-4561-BC58-FC633B50E2EB}.Release|Any CPU.Build.0 = Release|Any CPU + {4FB63540-4CC5-4A7B-900B-F5FCD907456E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FB63540-4CC5-4A7B-900B-F5FCD907456E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FB63540-4CC5-4A7B-900B-F5FCD907456E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FB63540-4CC5-4A7B-900B-F5FCD907456E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal