Browse Source

Merge branch 'dev' into entityactions

pull/22238/head
liangshiwei 2 years ago
parent
commit
73d9820f0d
  1. 125
      Directory.Packages.props
  2. 7
      README.md
  3. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  4. 13
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  5. 82
      docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md
  6. BIN
      docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/community-talks.png
  7. BIN
      docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/cover-image.png
  8. BIN
      docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/upgrade-abp-packages.png
  9. BIN
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/1.png
  10. BIN
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/2.png
  11. BIN
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/3.png
  12. 183
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md
  13. BIN
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman1.png
  14. BIN
      docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman2.png
  15. 177
      docs/en/Community-Articles/2023-10-03-basta-mainz-2023-what-a-blast-in-germany/post.md
  16. 134
      docs/en/Community-Articles/2024-03-05-URL-Based-Localization/Post.md
  17. BIN
      docs/en/Community-Articles/2024-03-05-URL-Based-Localization/UrlLocalizationSampleProject.zip
  18. BIN
      docs/en/Community-Articles/2024-03-05-URL-Based-Localization/cover.png
  19. BIN
      docs/en/Community-Articles/2024-03-05-URL-Based-Localization/scrshot1.jpg
  20. BIN
      docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca3de1eb496455c4c3341721ab3.jpg
  21. BIN
      docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca5122043881595c75c6ea661de.jpg
  22. BIN
      docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca776481fe9cb89b439988e99e1.jpg
  23. 234
      docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/POST.md
  24. BIN
      docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/index.png
  25. BIN
      docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/order.png
  26. BIN
      docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183abfe065ba8bacdd56ef05d9a4cd.jpg
  27. BIN
      docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183ac7b768a9a7523278ab07847b3c.jpg
  28. BIN
      docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183ac7c4767cabf12d657ac90f4c17.jpg
  29. 28
      docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/post.md
  30. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_122158.webp
  31. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_153010.webp
  32. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_153413.webp
  33. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250305_151043.webp
  34. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4732-2000px.webp
  35. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4770-2000px.webp
  36. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4915-2000px.webp
  37. BIN
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/Screenshot_2025-03-04_at_13.47.28.webp
  38. 43
      docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/post.md
  39. 73
      docs/en/framework/infrastructure/background-jobs/index.md
  40. 74
      docs/en/framework/infrastructure/background-workers/index.md
  41. 34
      docs/en/framework/infrastructure/cancellation-token-provider.md
  42. 18
      docs/en/guides/microservice-mongodb.md
  43. BIN
      docs/en/images/books.png
  44. 17
      docs/en/index.md
  45. 39
      docs/en/kb/can-not-login-with-admin-user.md
  46. 2
      docs/en/release-info/migration-guides/abp-9-0.md
  47. 7
      docs/en/studio/release-notes.md
  48. 2
      docs/en/studio/version-mapping.md
  49. 17
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs
  50. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs
  51. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs
  52. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs
  53. 24
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  54. 19
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs
  55. 5
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobWorkerOptions.cs
  56. 5
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs
  57. 2
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs
  58. 8
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs
  59. 5
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/IBackgroundJobStore.cs
  60. 3
      framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs
  61. 2
      framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs
  62. 23
      framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs
  63. 7
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
  64. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorList.cs
  65. 12
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorListExtensions.cs
  66. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntitySelectorList.cs
  67. 96
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  68. 4
      framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
  69. 12
      framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireOptions.cs
  70. 65
      framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireOptionsConfiguration.cs
  71. 25
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
  72. 10
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs
  73. 18
      latest-versions.json
  74. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json
  75. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json
  76. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/da.json
  77. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json
  78. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/el.json
  79. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json
  80. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
  81. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json
  82. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json
  83. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fa.json
  84. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json
  85. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json
  86. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json
  87. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hr.json
  88. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json
  89. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/is.json
  90. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json
  91. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json
  92. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json
  93. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json
  94. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json
  95. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json
  96. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json
  97. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json
  98. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sv.json
  99. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json
  100. 4
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json

125
Directory.Packages.props

@ -56,71 +56,72 @@
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="Markdig.Signed" Version="0.37.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.33" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.2" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0-preview.7.24406.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.5.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.5.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.5.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.5.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.6.0" />
<PackageVersion Include="Minio" Version="6.0.3" />
<PackageVersion Include="MongoDB.Driver" Version="3.1.0" />
<PackageVersion Include="NEST" Version="7.17.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.11.1" />
<PackageVersion Include="NUglify" Version="1.21.9" />
@ -161,18 +162,18 @@
<PackageVersion Include="Spectre.Console" Version="0.49.1" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.8.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.2" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.1" />
<PackageVersion Include="System.Linq.Queryable" Version="4.3.0" />
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.2" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.0" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.2" />
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1142" />
<PackageVersion Include="TimeZoneConverter" Version="6.1.0" />
<PackageVersion Include="Unidecode.NET" Version="2.1.0" />

7
README.md

@ -1,11 +1,12 @@
# ABP Framework
![build and test](https://img.shields.io/github/actions/workflow/status/abpframework/abp/build-and-test.yml?branch=dev&style=flat-square) 🔹 [![codecov](https://codecov.io/gh/abpframework/abp/branch/dev/graph/badge.svg?token=jUKLCxa6HF)](https://codecov.io/gh/abpframework/abp) 🔹 [![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![NuGet (with prereleases)](https://img.shields.io/nuget/vpre/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![MyGet (nightly builds)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://abp.io/docs/latest/release-info/nightly-builds) 🔹
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](https://github.com/abpframework/abp/blob/dev/CODE_OF_CONDUCT.md) 🔹 [![CLA Signed](https://cla-assistant.io/readme/badge/abpframework/abp)](https://cla-assistant.io/abpframework/abp) 🔹 [![Discord Shield](https://discord.com/api/guilds/951497912645476422/widget.png?style=shield)](https://abp.io/join-discord)
[ABP](https://abp.io/) offers an **opinionated architecture** to build enterprise software solutions with **best practices** on top of the **.NET** and the **ASP.NET Core** platforms. It provides the fundamental infrastructure, production-ready startup templates, pre-built application modules, UI themes, tooling, guides and documentation to implement that architecture properly and **automate the details** and repetitive works as much as possible.
[![ABP Platform](https://github.com/user-attachments/assets/200653c0-0e69-4b47-b76a-3a83460aaab6)](https://abp.io)
[![ABP Platform](https://github.com/user-attachments/assets/c4356ec7-4d0f-4e00-a1d2-fc74ad985fb8)](https://abp.io)
## Getting Started

2
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json

@ -17,6 +17,6 @@
"SelectYourOrganization": "Select your organization",
"PleaseSelectOrganization": "Please select an organization to continue",
"Continue": "Continue",
"CaptchaExplanation": "Calculate the given mathematical expression and enter the correct answer."
"CaptchaExplanation": "Calculate the below mathematical expression and enter the answer."
}
}

13
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -1875,6 +1875,17 @@
"RcStableDifferenceExplanation3": "Use the RC version for testing and early adoption but use the Stable version for production deployment.",
"AddToCalendar": "Add To Calendar",
"AddToGoogleCalendar": "Add To Google Calendar",
"DownloadICSFile": "Download ICS File"
"DownloadICSFile": "Download ICS File",
"AddToAppleCalendar": "Add To Apple Calendar",
"AddToOutlookCalendar": "Add To Outlook Calendar",
"FaqIyzicoPaymentIssuesTitle" : "What should I do if I encounter issues while purchasing the Iyzico payment gateway?",
"FaqIyzicoPaymentIssuesExplanation1": "This website uses two payment gateways to receive payments: Iyzico and 2Checkout. The default gateway is Iyzico. When you check the 'Automatic Renewal' option, it will automatically use the Iyzico payment gateway. If you’re experiencing issues while trying to purchase through the Iyzico payment gateway, it could be due to the following reasons:",
"FaqIyzicoPaymentIssuesExplanation2": "<strong>Card Type</strong>: Iyzico only supports VISA and MasterCard. If you’re using a different card type (e.g., American Express), please switch to the 2Checkout payment gateway.",
"FaqIyzicoPaymentIssuesExplanation3": "<strong>3D Secure</strong>: If your card has 3D Secure enabled, you’ll be redirected to your card provider’s 3D Secure page to complete the verification process. Once done, you’ll return to Iyzico to finalize the payment. If you encounter any issues with 3D Secure, please contact your card provider for assistance. Sometimes there might be problems with the redirection to the 3D Secure provider, in that case you can disable 3D Secure or contact us.",
"FaqIyzicoPaymentIssuesExplanation4": "If you’re unable to complete the payment through Iyzico, you can use our alternative payment gateway, 2Checkout which supports a wider range of payment options, including:",
"FaqIyzicoPaymentIssuesExplanation5": "<strong>Credit Cards</strong>: Visa, MasterCard, American Express, Discover, JCB, Diners Club",
"FaqIyzicoPaymentIssuesExplanation6": "<strong>Digital Wallets</strong>: PayPal, AliPay, WebMoney",
"FaqIyzicoPaymentIssuesExplanation7": "Alternatively, you can send the license amount directly via bank wire transfer. For our bank account details, please visit: <a href='https://volosoft.com/bank-account'>Bank Account Information</a> (use USD currency).",
"FaqIyzicoPaymentIssuesExplanation8": "ABP website doesn't save or process your credit card. We use payment gateways for this and the entire transaction is handled by payment gateways. We have no authority to interfere with the payment process or fix the payment steps. If you have further questions or need additional support, feel free to contact us at <a href='https://abp.io/contact'>abp.io/contact</a> abp.io/contact"
}
}

82
docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md

@ -0,0 +1,82 @@
# ABP.IO Platform 9.1 Final Has Been Released!
We are glad to announce that [ABP](https://abp.io/) 9.1 stable version has been released today.
## What's New With Version 9.1?
All the new features were explained in detail in the [9.1 RC Announcement Post](https://abp.io/community/articles/abp-platform-9.1-rc-has-been-released-wws5l00k), so there is no need to review them again. You can check it out for more details.
## Getting Started with 9.1
### Creating New Solutions
You can check the [Get Started page](https://abp.io/get-started) to see how to get started with ABP. You can either download [ABP Studio](https://abp.io/get-started#abp-studio-tab) (**recommended**, if you prefer a user-friendly GUI application - desktop application) or use the [ABP CLI](https://abp.io/docs/latest/cli) to create new solutions.
By default, ABP Studio uses stable versions to create solutions. Therefore, it will be creating the solution with the latest stable version, which is v9.1 for now, so you don't need to specify the version.
### How to Upgrade an Existing Solution
You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:
### Upgrading via ABP Studio
If you are already using the ABP Studio, you can upgrade it to the latest version to align it with ABP v9.1. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See [the documentation](https://abp.io/docs/latest/studio/installation#upgrading) for more info.
After upgrading the ABP Studio, then you can open your solution in the application, and simply click the **Upgrade ABP Packages** action button to instantly upgrade your solution:
![](upgrade-abp-packages.png)
### Upgrading via ABP CLI
Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.
If you haven't installed it yet, you can run the following command:
```bash
dotnet tool install -g Volo.Abp.Studio.Cli
```
Or to update the existing CLI, you can run the following command:
```bash
dotnet tool update -g Volo.Abp.Studio.Cli
```
After installing/updating the ABP CLI, you can use the [`update` command](https://abp.io/docs/latest/CLI#update) to update all the ABP related NuGet and NPM packages in your solution as follows:
```bash
abp update
```
You can run this command in the root folder of your solution to update all ABP related packages.
## Migration Guides
There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.0: [ABP Version 9.1 Migration Guide](https://abp.io/docs/latest/release-info/migration-guides/abp-9-1)
## Community News
### New ABP Community Articles
As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:
* [URL-Based Localization](https://abp.io/community/articles/urlbased-localization-3ivzinbb) by [Alper Ebiçoğlu](https://twitter.com/alperebicoglu)
* [Building a CRUD API with ABP Framework, ASP.NET Core, and PostgreSQL](https://abp.io/community/articles/building-a-crud-api-with-abp-framework-asp.net-core-and-postgresql-elrj0old) by [Berkan Şaşmaz](https://github.com/berkansasmaz)
* [Encryption and Decryption in ABP Framework](https://abp.io/community/articles/encryption-and-decryption-in-abp-framework-37uqhdwz) by [Liming Ma](https://github.com/maliming)
* [Migrate Your DB from the Web Application - Adding a DB Migration Controller](https://abp.io/community/articles/migrate-your-db-from-the-web-application-adding-a-db-migration-controller-in-abp-framework-x3u3uvk3) by [Alper Ebiçoğlu](https://twitter.com/alperebicoglu)
* [Containerization: Blazor WASM + JWT Web API => Docker](https://abp.io/community/articles/containerization-blazor-wasm-jwt-web-api-docker-i3eirlsf) by [Bart Van Hoey](https://abp.io/community/members/bartvanhoey)
* [Configuring Post-Logout Redirect URI in ABP Based Blazor Applications with OpenIddict](https://abp.io/community/articles/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg) by [Engincan Veske](https://github.com/EngincanV)
Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community.
### ABP Community Talks 2025.2: Real World Problems and Solutions with AI
![](community-talks.png)
In this episode of ABP Community Talks (2025.2), Decision Tree joined us to explore how AI is being leveraged to solve real-world problems, showcasing a practical use case of AI applications.
> You can re-watch the talk from [here](https://www.youtube.com/watch?v=CXpWjxCIY_E).
## About the Next Version
The next feature version will be 9.2. You can follow the [release planning here](https://github.com/abpframework/abp/milestones). Please [submit an issue](https://github.com/abpframework/abp/issues/new) if you have any problems with this version.

BIN
docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/community-talks.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

BIN
docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

BIN
docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/upgrade-abp-packages.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

BIN
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

183
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md

@ -2,17 +2,72 @@
## ITokenExtensionGrant
Create a class that inherits `ITokenExtensionGrant`, and then register it with the framework.
Create a `MyTokenExtensionGrant` class that inherits `ITokenExtensionGrant`, and then register it with the framework.
In the `MyTokenExtensionGrant` class below we try to get the token details, The `ForbidResult` handles the failure case and `SignInResult` returns a new token response, You can pass more parameters to implement business checks.
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//...
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.Configure(openIddictServerOptions =>
{
openIddictServerOptions.GrantTypes.Add(MyTokenExtensionGrant.ExtensionGrantName);
});
});
//...
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
//...
Configure<AbpOpenIddictExtensionGrantsOptions>(options =>
{
options.Grants.Add(MyTokenExtensionGrant.ExtensionGrantName, new MyTokenExtensionGrant());
});
//...
}
```
## Generate a new token response
In the `MyTokenExtensionGrant` class below we have two methods to get a new token using a user token or user API key. You can choose one of them based on your business.
These methods are just examples. Please add more logic to validate input data.
```cs
using System.Collections.Immutable;
using System.Security.Principal;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using OpenIddict.Server;
using OpenIddict.Server.AspNetCore;
using Volo.Abp.Identity;
using Volo.Abp.OpenIddict;
using Volo.Abp.OpenIddict.ExtensionGrantTypes;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
using SignInResult = Microsoft.AspNetCore.Mvc.SignInResult;
namespace OpenIddict.Demo.Server.ExtensionGrants;
public class MyTokenExtensionGrant : ITokenExtensionGrant
{
public const string ExtensionGrantName = "MyTokenExtensionGrant";
public string Name => ExtensionGrantName;
public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
{
// You can get a new token using any of the following methods based on your business.
// They are just examples. You can implement your own logic here.
return await HandleUserAccessTokenAsync(context);
return await HandleUserApiKeyAsync(context);
}
public async Task<IActionResult> HandleUserAccessTokenAsync(ExtensionGrantContext context)
{
var userToken = context.Request.GetParameter("token").ToString();
@ -26,6 +81,9 @@ public class MyTokenExtensionGrant : ITokenExtensionGrant
}!));
}
// We will validate the user token
// The Token is issued by the OpenIddict server, So we can validate it using the introspection endpoint
var transaction = await context.HttpContext.RequestServices.GetRequiredService<IOpenIddictServerFactory>().CreateTransactionAsync();
transaction.EndpointType = OpenIddictServerEndpointType.Introspection;
transaction.Request = new OpenIddictRequest
@ -64,23 +122,92 @@ public class MyTokenExtensionGrant : ITokenExtensionGrant
}));
}
// We have validated the user token and got the user id
var userId = principal.FindUserId();
var userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>();
var user = await userManager.GetByIdAsync(userId.Value);
var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<IdentityUser>>();
var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
claimsPrincipal.SetScopes(principal.GetScopes());
claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));
//abp version < 7.3
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimDestinationsManager>().SetAsync(claimsPrincipal);
// Prepare the scopes
var scopes = GetScopes(context);
claimsPrincipal.SetScopes(scopes);
claimsPrincipal.SetResources(await GetResourcesAsync(context, scopes));
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>().HandleAsync(context.Request, principal);
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
}
//For abp version >= 7.3
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>().HandleAsync(context.Request, claimsPrincipal);
protected async Task<IActionResult> HandleUserApiKeyAsync(ExtensionGrantContext context)
{
var userApiKey = context.Request.GetParameter("user_api_key").ToString();
if (string.IsNullOrEmpty(userApiKey))
{
return new ForbidResult(
new[] {OpenIddictServerAspNetCoreDefaults.AuthenticationScheme},
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
}!));
}
// Here we can validate the user API key and get the user id
if (false) // Add your own logic here
{
// If the user API key is invalid
return new ForbidResult(
new[] {OpenIddictServerAspNetCoreDefaults.AuthenticationScheme},
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
}!));
}
// Add your own logic to get the user by API key
var userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>();
var user = await userManager.FindByNameAsync("admin");
if (user == null)
{
return new ForbidResult(
new[] {OpenIddictServerAspNetCoreDefaults.AuthenticationScheme},
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
}!));
}
// Create a principal for the user
var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<IdentityUser>>();
var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
// Prepare the scopes
var scopes = GetScopes(context);
claimsPrincipal.SetScopes(scopes);
claimsPrincipal.SetResources(await GetResourcesAsync(context, scopes));
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>().HandleAsync(context.Request, claimsPrincipal);
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
}
private ImmutableArray<string> GetScopes(ExtensionGrantContext context)
{
// Prepare the scopes
// The scopes must be defined in the OpenIddict server
// If you want to get the scopes from the request, you have to add `scope` parameter in the request
// scope: AbpAPI profile roles email phone offline_access
//var scopes = context.Request.GetScopes();
// If you want to set the scopes here, you can use the following code
var scopes = new[] { "AbpAPI", "profile", "roles", "email", "phone", "offline_access" }.ToImmutableArray();
return scopes;
}
private async Task<IEnumerable<string>> GetResourcesAsync(ExtensionGrantContext context, ImmutableArray<string> scopes)
{
var resources = new List<string>();
@ -98,38 +225,24 @@ public class MyTokenExtensionGrant : ITokenExtensionGrant
}
```
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//...
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.Configure(openIddictServerOptions =>
{
openIddictServerOptions.GrantTypes.Add(MyTokenExtensionGrant.ExtensionGrantName);
});
});
//...
}
### Get a new token using user access token
public override void ConfigureServices(ServiceConfigurationContext context)
{
//...
Configure<AbpOpenIddictExtensionGrantsOptions>(options =>
{
options.Grants.Add(MyTokenExtensionGrant.ExtensionGrantName, new MyTokenExtensionGrant());
});
//...
}
```
* Get a user token using the `password` grant type.
![Http request 1](1.png)
![Http request 1](postman1.png)
* Use the user token to get a new token using the `HandleUserAccessTokenAsync` method.
![Http request 2](postman2.png)
![Http request 2](2.png)
## Source code
### Get a new token using user API key
* Directly get a new token using the `HandleUserApiKeyAsync` method.
https://github.com/abpframework/abp/commit/3210f138454697647689b4868c8d4b7b3da02d44
![Http request 3](3.png)
## Source code
https://github.com/abpframework/abp/blob/dev/modules/openiddict/app/OpenIddict.Demo.Server/ExtensionGrants/MyTokenExtensionGrant.cs

BIN
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

BIN
docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

177
docs/en/Community-Articles/2023-10-03-basta-mainz-2023-what-a-blast-in-germany/post.md

@ -1,59 +1,118 @@
BASTA! Mainz 2023 has wrapped up, and what an extraordinary journey it has been! We can’t wait to share our impressions, highlights, and the incredible impact it had on the tech community in Germany and beyond.
### A Glance Back at BASTA! Mainz 2023
We just came back from the[ BASTA! Conference 2023](https://basta.net/), which is an incredible .NET event with over 600 in-person attendees and an additional 200+ tuning in virtually from across the globe. The buzz of excitement and anticipation in the air was undeniable, setting the stage for an unforgettable event for [ABP.IO](https://abp.io/).
![53227564247\_b7acafea68\_c.jpg](3a0e074506bcc60cad473655b6fd5da6.jpg)
![53227631612\_eb38958391\_c.jpg](3a0e074555e8d6f62bc3d47c00f86762.jpg)
![53227631792\_a6d87b4abf\_k.jpg](3a0e0745b6418472a66d905053d492e6.jpg)
As one of the proud sponsors, we set up our booth, engaged with attendees, and delved into a wide range of sessions, workshops, and keynotes to absorb the latest software development trends.
### Engaging with Enthusiastic Minds
As the lead developers of ABP Core Team, *[Alper](https://twitter.com/alperebicoglu)* and *[Ismail](https://twitter.com/ismcagdas)* presented the ABP.IO platform modules and features were quite busy enjoying their presenting work with latest version of demos. Alper also gave a great speech on “Building Multi-tenant ASP.NET Core Application & the ABP Framework” at the BASTA! Mainz conference.
![53229004005\_3312b6bfa2\_k.jpg](3a0e074625fddc3097efdc1bef3058d8.jpg)
![20230926\_170335.jpg](3a0e074675731c4a59167a878d4cd04e.jpg)
We introduced **[ABP Framework](https://abp.io/)**, community-driven open-source web application framework, and **[ABP Commercial](https://commercial.abp.io/)**, our enterprise-ready web development platform that is built on top of the open-source ABP Framework to the crowds.
![53227632222\_0a5af1fcc1\_k.jpg](3a0e074703a63129e5713ff51653aaac.jpg)
![53228506206\_24e22498fa\_o.jpg](3a0e07477e980a4c2104300557e8f7fb.jpg)
![53228505931\_b2aea761f9\_k.jpg](3a0e0747c283919bbc5edfdc289db1d8.jpg)
Apart from that, we were thrilled to give away the **[ABP Commercial Licenses](https://commercial.abp.io/pricing)** to the eager attendees on the venue, including the Raffle prize, **Meta Quest 2**, on our last day in BASTA! Conference.
![截屏2023-10-03 11.06.01.png](3a0e0748c8752dfdc2bcb5b9b2ce5908.png)
We are profoundly satisfied with the huge interest and engagement shown by the attendees. The sense of community, collaboration, and shared passion for .NET solutions was palpable throughout the event.
![20230927\_171046.jpg](3a0e07495efa58bb9f1e523d982dbc65.jpg)
![20230928\_150135.jpg](3a0e0749c23a9e3b06d66ed54bed0c7a.jpg)
![20230928\_150533 20.57.50.jpg](3a0e074a268272e630d8422ace32b4c8.jpg)
### A Great Partnership with LIS GmbH
![20230928\_161843.jpg](3a0e074aa3857a696cb941edc46dcb52.jpg)
At BASTA! Mainz 2023, we were particularly thrilled to celebrate our collaboration with ***LIS GmbH***, a leading software solutions provider. This partnership added a unique dimension to the conference, brought us a fresh perspective to the conference, fostering innovation and opening up new avenues for attendees.
### A Big Thank-You from ABP Team
![53228503821\_27860119d9\_k.jpg](3a0e074b0a46662cf39a6796d0f23086.jpg)
Now, a shout-out to BASTA! Mainz 2023. We want to thank to everyone who contributed to the success of this event – attendees, speakers, sponsors, and partners.The conference may have ended, but the knowledge gained, connections formed, and inspiration ignited will continue to shape the tech landscape for years to come.
Until next time!
BASTA! Mainz 2023 has wrapped up, and what an extraordinary journey it has been! We can’t wait to share our impressions, highlights, and the incredible impact it had on the tech community in Germany and beyond.
### A Glance Back at BASTA! Mainz 2023
We just came back from the[ BASTA! Conference 2023](https://basta.net/), which is an incredible .NET event with over 600 in-person attendees and an additional 200+ tuning in virtually from across the globe. The buzz of excitement and anticipation in the air was undeniable, setting the stage for an unforgettable event for [ABP.IO](https://abp.io/).
![53227564247\_b7acafea68\_c.jpg](3a0e074506bcc60cad473655b6fd5da6.jpg)
![53227631612\_eb38958391\_c.jpg](3a0e074555e8d6f62bc3d47c00f86762.jpg)
![53227631792\_a6d87b4abf\_k.jpg](3a0e0745b6418472a66d905053d492e6.jpg)
As one of the proud sponsors, we set up our booth, engaged with attendees, and delved into a wide range of sessions, workshops, and keynotes to absorb the latest software development trends.
### Engaging with Enthusiastic Minds
As the lead developers of ABP Core Team, *[Alper](https://twitter.com/alperebicoglu)* and *[Ismail](https://twitter.com/ismcagdas)* presented the ABP.IO platform modules and features were quite busy enjoying their presenting work with latest version of demos. Alper also gave a great speech on “Building Multi-tenant ASP.NET Core Application & the ABP Framework” at the BASTA! Mainz conference.
![53229004005\_3312b6bfa2\_k.jpg](3a0e074625fddc3097efdc1bef3058d8.jpg)
![20230926\_170335.jpg](3a0e074675731c4a59167a878d4cd04e.jpg)
We introduced **[ABP Framework](https://abp.io/)**, community-driven open-source web application framework, and **[ABP Commercial](https://commercial.abp.io/)**, our enterprise-ready web development platform that is built on top of the open-source ABP Framework to the crowds.
![53227632222\_0a5af1fcc1\_k.jpg](3a0e074703a63129e5713ff51653aaac.jpg)
![53228506206\_24e22498fa\_o.jpg](3a0e07477e980a4c2104300557e8f7fb.jpg)
![53228505931\_b2aea761f9\_k.jpg](3a0e0747c283919bbc5edfdc289db1d8.jpg)
Apart from that, we were thrilled to give away the **[ABP Commercial Licenses](https://commercial.abp.io/pricing)** to the eager attendees on the venue, including the Raffle prize, **Meta Quest 2**, on our last day in BASTA! Conference.
![截屏2023-10-03 11.06.01.png](3a0e0748c8752dfdc2bcb5b9b2ce5908.png)
We are profoundly satisfied with the huge interest and engagement shown by the attendees. The sense of community, collaboration, and shared passion for .NET solutions was palpable throughout the event.
![20230927\_171046.jpg](3a0e07495efa58bb9f1e523d982dbc65.jpg)
![20230928\_150135.jpg](3a0e0749c23a9e3b06d66ed54bed0c7a.jpg)
![20230928\_150533 20.57.50.jpg](3a0e074a268272e630d8422ace32b4c8.jpg)
### A Great Partnership with LIS GmbH
![3a0e074aa3857a696cb941edc46dcb52](https://github.com/user-attachments/assets/ea0f6d32-e49b-4573-997f-9751ef16b303)
At BASTA! Mainz 2023, we were particularly thrilled to celebrate our collaboration with ***LIS GmbH***, a leading software solutions provider. This partnership added a unique dimension to the conference, brought us a fresh perspective to the conference, fostering innovation and opening up new avenues for attendees.
### A Big Thank-You from ABP Team
![53228503821\_27860119d9\_k.jpg](3a0e074b0a46662cf39a6796d0f23086.jpg)
Now, a shout-out to BASTA! Mainz 2023. We want to thank to everyone who contributed to the success of this event – attendees, speakers, sponsors, and partners.The conference may have ended, but the knowledge gained, connections formed, and inspiration ignited will continue to shape the tech landscape for years to come.
Until next time!

134
docs/en/Community-Articles/2024-03-05-URL-Based-Localization/Post.md

@ -0,0 +1,134 @@
# URL-Based Localization
In this article I'll show you how to optimize your ABP website localization with a URL parameter. URL Paths are commonly being used to change the current UI culture. This method makes our website SEO-Friendly as you structuring the URLs for multiple languages. And you can also share the link of your website with a specific language. Let's see implementing multi-language support with URL parameters in an ABP project.
![Turkish and English Localization within URL](scrshot1.jpg)
## Mastering Website Localization: Language Codes in URLs Explained
Enhancing UX with URL-Based website localization is mainly done with ASP.NET Core's routing system. Also we need to automatically redirect the links without language code parameter. Our URL structures for the localization will be as following:
- https://mywebsite.com/en/dashboard (English)
- https://mywebsite.com/tr/dashboard (Turkish)
## Routing
Before starting to explain how to do this, you can see [this commit](https://github.com/salihozkara/MultiLangRoute/commit/09e40cfd751562dec0dab890e54e0c5ca9ee256c) which implements this functionality. The routing module consists of these fundamental classes:
### 1. **MultiLanguageSupportMetaData.cs**
This class is used to add a language parameter to route templates. For example, it changes the `/about` route to `/language/about`.
### 2. **MultiLanguageRedirectRequiredMetaData.cs**
This class is used to redirect users to the correct language version. If a user visits the `/about` page and the current culture is `"tr-TR"`, this class will redirect them to `/tr-TR/about`.
### 3. **UrlNormalizer.cs**
This static class is used to normalize URLs by adding language information and improving performance using caching.
```csharp
public static string NormalizeUrl(EndpointDataSource endpointDataSource, HttpContext httpContext, string url)
{
// Normalize the URL and cache it
return Cache.GetOrAdd(url, (key) =>
{
var absoluteUrl = GetAbsoluteUrl(key);
var multiLanguageRedirectRequiredMetaData =
GetMultiLanguageRedirectRequiredMetaData(endpointDataSource, absoluteUrl);
return multiLanguageRedirectRequiredMetaData?.ReBuildUrl(httpContext, key) ?? key;
});
}
```
### 4. **MyLinkGenerator.cs**
This class extends ASP.NET Core’s `LinkGenerator` class to automatically add language information to all generated links.
### 5. **MyRouteDataRequestCultureProvider.cs**
This class determines the current culture using the language parameter in the URL. For example, it extracts the `"tr-TR"` culture from the `/tr-TR/about` URL.
### 6. **RoutingMiddleware.cs**
This middleware processes HTTP requests and redirects users to the correct language version when necessary.
```csharp
public override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var endpoint = context.GetEndpoint();
if(endpoint is not RouteEndpoint)
{
return next(context);
}
// Redirect if necessary
var redirectMetaData = endpoint.Metadata.GetMetadata<IRedirectMetaData>();
if (redirectMetaData is not null)
{
redirectMetaData.Redirect(context);
return Task.CompletedTask;
}
// ...
}
```
## CultureAnchorTagHelper.cs
This **Tag Helper** processes `<a>` tags in a Razor page and automatically adds language information to URLs if it's missing.
```csharp
[HtmlTargetElement("a", Attributes = "href", TagStructure = TagStructure.NormalOrSelfClosing)]
public class CultureAnchorTagHelper(EndpointDataSource endpointDataSource, IHttpContextAccessor contextAccessor)
: TagHelper, ITransientDependency
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var href = output.Attributes["href"].Value.ToString();
if (href != null)
{
output.Attributes.SetAttribute("href",
UrlNormalizer.NormalizeUrl(endpointDataSource, contextAccessor.HttpContext!, href));
}
}
}
```
**How This Tag Helper Works:**
- Finds all `<a href="...">` tags within your Razor pages.
- Retrieves the `href` attribute of each link.
- Uses the `UrlNormalizer.NormalizeUrl()` method to normalize the URL with the current culture information.
- Replaces the original URL with the normalized one.
For example, if the current culture is `"tr"` and a page contains `<a href="/about">`, this Tag Helper will transform it into `<a href="/tr-TR/about">`.
## Sample Project
Salih Özkara from ABP team created a sample working project which implements URL localization. He used ABP free tier MVC template and MongoDB. You can check out the related commit which implements URL localization:
https://github.com/salihozkara/MultiLangRoute/commit/09e40cfd751562dec0dab890e54e0c5ca9ee256c
And full working demo is available at:
https://github.com/salihozkara/MultiLangRoute
You can download the demo project at:
[UrlLocalizationSampleProject.zip](https://github.com/abpframework/abp/blob/634ff52fb07d0b1281640695dbeffccdc943ca53/docs/en/Community-Articles/2024-03-05-URL-Based-Localization/UrlLocalizationSampleProject.zip)
## Summary
This implementation extends ASP.NET Core’s routing mechanism and utilizes caching for improved performance. It ensures multilingual support by normalizing URLs, redirecting users to the appropriate language version, and automatically handling language-specific links.

BIN
docs/en/Community-Articles/2024-03-05-URL-Based-Localization/UrlLocalizationSampleProject.zip

Binary file not shown.

BIN
docs/en/Community-Articles/2024-03-05-URL-Based-Localization/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

BIN
docs/en/Community-Articles/2024-03-05-URL-Based-Localization/scrshot1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca3de1eb496455c4c3341721ab3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca5122043881595c75c6ea661de.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
docs/en/Community-Articles/2025-02-07-event-recap-ndc-london-2025/3a180ca776481fe9cb89b439988e99e1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

234
docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/POST.md

@ -0,0 +1,234 @@
# Using Outbox/Inbox Pattern for Reliable Event Handling in a Multi-Module Monolithic Application
This article explains how to implement reliable event handling using the `Outbox/Inbox` pattern in a modular monolithic application with multiple databases. We'll use the `ModularCRM` project as an example (how that project was created is explained in [this document](https://abp.io/docs/latest/tutorials/modular-crm)).
## Project Background
`ModularCRM` is a monolithic application that integrates multiple ABP framework open-source modules, including:
- `Account`
- `Identity`
- `Tenant Management`
- `Permission Management`
- `Setting Management`
- And other open-source modules
Besides the ABP framework modules, the project contains three business modules:
- Order module (`Ordering`), using `MongoDB` database
- Product module (`Products`), using `SQL Server` database
- Payment module (`Payment`), using `MongoDB` database
The project configures separate database connection strings for `ModularCRM` and the three business modules in `appsettings.json`:
```json
{
"ConnectionStrings": {
"Default": "Server=localhost,1434;Database=ModularCrm;User Id=sa;Password=1q2w3E***;TrustServerCertificate=true",
"Products": "Server=localhost,1434;Database=ModularCrm_Products;User Id=sa;Password=1q2w3E***;TrustServerCertificate=true",
"Ordering": "mongodb://localhost:27017/ModularCrm_Ordering?replicaSet=rs0",
"Payment": "mongodb://localhost:27017/ModularCrm_Payment?replicaSet=rs0"
}
}
```
## Business Scenario
These modules communicate through the ABP framework's `DistributedEventBus` to implement the following business flow:
> This is a simple example flow. Real business flows are more complex. The sample code is for demonstration purposes.
1. Order module: Publishes `OrderPlacedEto` event when an order is placed
2. Product module: Subscribes to `OrderPlacedEto` event and reduce product stock
3. Payment module: Subscribes to `OrderPlacedEto` event, processes payment, then publishes `PaymentCompletedEto` event
4. Order module: Subscribes to `PaymentCompletedEto` event and updates order status to `Delivered`
When implementing this flow, we need to ensure:
- Transaction consistency between order creation and event publishing
- Transaction consistency when modules process messages
- Reliable message delivery (including persistence, confirmation, and retry mechanisms)
Using the default implementation of the ABP framework's distributed event bus cannot meet these requirements, so we need to add a new mechanism that is also provided by the ABP Framework.
## Outbox/Inbox Pattern Solution
To meet these requirements, we use the `Outbox/Inbox` pattern:
### Outbox Pattern
- Saves distributed events with database operations in the same transaction
- Sends events to distributed message service through background jobs
- Ensures consistency between data updates and event publishing
- Prevents message loss during system failures
### Inbox Pattern
- First saves received distributed events to the database
- Processes events in a transactional way
- Ensures messages are processed only once by saving processed message records
- Maintains processing state for reliable handling
> For how to enable and configure `Outbox/Inbox` in projects and modules, see: https://abp.io/docs/latest/framework/infrastructure/event-bus/distributed#outbox-inbox-for-transactional-events
### Module Configuration
Each module needs to configure separate `Outbox/Inbox`. Since it's a monolithic application, all message processing classes are in the same project, so we need to configure `Outbox/Inbox` for each module with `Selector/EventSelector` to ensure that the module only sends and receives the messages it cares about, avoiding message duplication processing.
**ModularCRM Main Application Configuration**
It will send and receive messages from all ABP framework open-source modules.
```csharp
// This selector will match all abp built-in modules and the current module.
Func<Type, bool> abpModuleSelector = type => type.Namespace != null && (type.Namespace.StartsWith("Volo.") || type.Assembly == typeof(ModularCrmModule).Assembly);
Configure<AbpDistributedEventBusOptions>(options =>
{
options.Inboxes.Configure("ModularCrm", config =>
{
config.UseDbContext<ModularCrmDbContext>();
config.EventSelector = abpModuleSelector;
config.HandlerSelector = abpModuleSelector;
});
options.Outboxes.Configure("ModularCrm", config =>
{
config.UseDbContext<ModularCrmDbContext>();
config.Selector = abpModuleSelector;
});
});
```
**Order Module Configuration**
It only sends `OrderPlacedEto` events and receives `PaymentCompletedEto` events and executes `OrderPaymentCompletedEventHandler`.
```csharp
Configure<AbpDistributedEventBusOptions>(options =>
{
options.Inboxes.Configure(OrderingDbProperties.ConnectionStringName, config =>
{
config.UseMongoDbContext<IOrderingDbContext>();
config.EventSelector = type => type == typeof(PaymentCompletedEto);
config.HandlerSelector = type => type == typeof(OrderPaymentCompletedEventHandler);
});
options.Outboxes.Configure(OrderingDbProperties.ConnectionStringName, config =>
{
config.UseMongoDbContext<IOrderingDbContext>();
config.Selector = type => type == typeof(OrderPlacedEto);
});
});
```
> Here, the `EventSelector` and `HandlerSelector` checks only a single type. If you have multiple events and event handlers, you can check the given type if it is included in an array of types.
**Product Module Configuration**
It only receives `EntityCreatedEto<UserEto>` and `OrderPlacedEto` events and executes `ProductsOrderPlacedEventHandler` and `ProductsUserCreatedEventHandler`. It does not send any events now.
```csharp
Configure<AbpDistributedEventBusOptions>(options =>
{
options.Inboxes.Configure(ProductsDbProperties.ConnectionStringName, config =>
{
config.UseDbContext<IProductsDbContext>();
config.EventSelector = type => type == typeof(EntityCreatedEto<UserEto>) || type == typeof(OrderPlacedEto);
config.HandlerSelector = type => type == typeof(ProductsOrderPlacedEventHandler) || type == typeof(ProductsUserCreatedEventHandler);
});
// Outboxes are not used in this module
options.Outboxes.Configure(ProductsDbProperties.ConnectionStringName, config =>
{
config.UseDbContext<IProductsDbContext>();
config.Selector = type => false;
});
});
```
**Payment Module Configuration**
It only sends `PaymentCompletedEto` events and receives `OrderPlacedEto` events and executes `PaymentOrderPlacedEventHandler`.
```csharp
Configure<AbpDistributedEventBusOptions>(options =>
{
options.Inboxes.Configure(PaymentDbProperties.ConnectionStringName, config =>
{
config.UseMongoDbContext<IPaymentMongoDbContext>();
config.EventSelector = type => type == typeof(OrderPlacedEto);
config.HandlerSelector = type => type == typeof(PaymentOrderPlacedEventHandler);
});
options.Outboxes.Configure(PaymentDbProperties.ConnectionStringName, config =>
{
config.UseMongoDbContext<IPaymentMongoDbContext>();
config.Selector = type => type == typeof(PaymentCompletedEto);
});
});
```
## Running ModularCRM Simulation Business Flow
1. Run the following command in the `ModularCrm` directory:
```
# Start SQL Server and MongoDB databases in Docker
docker-compose up -d
# Restore and install project npm dependencies
abp install-lib
# Migrate databases
dotnet run --project ModularCrm --migrate-database
# Start the application
dotnet run --project ModularCrm
```
2. Navigate to `https://localhost:44303/` to view the application homepage
![index](index.png)
3. Enter a customer name and select a product, then submit an order. After a moment, refresh the page to see the order, product, and payment information.
![order](order.png)
Application logs display the complete processing flow:
```
[Ordering Module] Order created: OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88, ProductId: 0f95689f-4cb6-36f5-68bd-3a18344d32c9, CustomerName: john
[Products Module] OrderPlacedEto event received: OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88, CustomerName: john, ProductId: 0f95689f-4cb6-36f5-68bd-3a18344d32c9
[Products Module] Stock count decreased for ProductId: 0f95689f-4cb6-36f5-68bd-3a18344d32c9
[Payment Module] OrderPlacedEto event received: OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88, CustomerName: john, ProductId: 0f95689f-4cb6-36f5-68bd-3a18344d32c9
[Payment Module] Payment processing completed for OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88
[Ordering Module] PaymentCompletedEto event received: OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88, PaymentId: d0a41ead-ee0f-714c-e254-3a1834504d65, PaymentMethod: CreditCard, PaymentAmount: ModularCrm.Payment.Payment.PaymentCompletedEto
[Ordering Module] Order state updated to Delivered for OrderId: b7ad3f47-0e77-bb81-082f-3a1834503e88
```
In addition, when a new user registers, the product module will also receive the `EntityCreatedEto<UserEto>` event, and we will send an email to the new user, just to demonstrate the `Outbox/Inbox Selector` mechanism.
```
[Products Module] UserCreated event received: UserId: "9a1f2bd0-5b28-210a-9e56-3a18344d310a", UserName: admin
[Products Module] Sending a popular products email to admin@abp.io...
```
## Summary
By introducing the `Outbox/Inbox` pattern, we have achieved:
1. Transactional message sending and receiving
2. Reliable message processing mechanism
3. Modular event processing in a multi-database environment
ModularCRM project not only implements reliable message processing but also demonstrates how to handle multi-database scenarios gracefully in a monolithic application. Project source code: https://github.com/abpframework/abp-samples/tree/master/ModularCrm-OutboxInbox-Pattern
## Reference
- [Outbox/Inbox for transactional events](https://abp.io/docs/latest/framework/infrastructure/event-bus/distributed#outbox-inbox-for-transactional-events)
- [ConnectionStrings](https://abp.io/docs/latest/framework/fundamentals/connection-strings)
- [ABP Studio: Single Layer Solution Template](https://abp.io/docs/latest/solution-templates/single-layer-web-application)

BIN
docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/index.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
docs/en/Community-Articles/2025-02-20-Using-OutboxInbox-Pattern-for-Reliable-Event-Handling-in-a-Multi-Module-Monolithic-Application/order.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183abfe065ba8bacdd56ef05d9a4cd.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

BIN
docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183ac7b768a9a7523278ab07847b3c.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/3a183ac7c4767cabf12d657ac90f4c17.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

28
docs/en/Community-Articles/2025-02-21-catch-us-at-basta-frankfurt-2025/post.md

@ -0,0 +1,28 @@
We are excited to share some fantastic news with our community! We are proud to announce that ABP.IO is going to be at the BASTA! Conference on March 03-07, 2025 in Frankfurt and İsmail Çağdaş from our dev team is going to be a speaker on March 04!
#### **About BASTA!**
For those who don’t know, BASTA! is the leading independent conference for Microsoft technologies in the German-speaking world. For over 20 years, it has been setting standards in the areas of C#, .NET and cloud and web technologies and is considered a must-attend event for Microsoft, cloud, web developers and key players in the software industry. BASTA! is a conference for developers and IT professionals who want to stay up to date with the latest technologies.
#### **ABP at [BASTA! Mainz 2023](https://abp.io/blog/BASTA-Mainz-2023-What-a-Blast-in-Germany)**
![basta 2023.jpg](3a183abfe065ba8bacdd56ef05d9a4cd.jpg)
![2222.jpg](3a183ac7b768a9a7523278ab07847b3c.jpg)
![3333.jpg](3a183ac7c4767cabf12d657ac90f4c17.jpg)
#####
#### **What to Expect at [BASTA! 2025](https://basta.net/frankfurt-en/)**
The most exciting part is İsmail Çağdaş from the ABP developer team will be speaking at the conference on March 04 about the concepts of monoliths and microservices will be briefly explored, along with how modular monoliths bring together the advantages of these two architectures. Using the ABP Framework as an example, the session will highlight its modularity features and demonstrate how it can assist in creating and developing a modular monolith application. Finally, best practices for developing modular monoliths will be discussed, showing how these practices can pave the way for transitioning to a microservice-based architecture when necessary.
If you want to find out more information about İsmail Çağdaş's session, [check here](https://basta.net/microservices-apis/modular-monoliths-architecture-abp/?loc=ffm):
#### **Connect with Us**
We have exciting raffles and surprises planned at our booth and look forward to sharing more information about our solutions with you there.
#### **Join Us Online**
Don't worry if you can't join us in person, our online booth is going to be there for you! The Expo of the online version of BASTA! is open for the main conference days.
Tuesday, March 4, 2025: 9:00 am – approx. 6:00 pm
Wednesday, March 5, 2025: 9:00 am – approx. 6:00 pm
Thursday, March 6, 2025: 9:00 am – approx. 5:45 pm

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_122158.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_153010.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250304_153413.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/20250305_151043.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4732-2000px.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4770-2000px.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/IMG_4915-2000px.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/Screenshot_2025-03-04_at_13.47.28.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

43
docs/en/Community-Articles/2025-03-10-WE-HAD-A-BLAST-AT-BASTA-FRANKFURT-2025/post.md

@ -0,0 +1,43 @@
**WE HAD A BLAST AT BASTA\! FRANKFURT 2025**
![](20250304_122158.webp)
Our team had an amazing time at BASTA\! Frankfurt 2025, held from March 3 to 8 at the Frankfurt am Main Marriott Hotel. As a sponsor for this major conference for asp.net web developers, we were thrilled to connect with so many talented participants.
**Event Highlights**
![](Screenshot_2025-03-04_at_13.47.28.webp)
![](20250304_153010.webp)
![](20250304_153413.webp)
The conference hosted many talented developers as speakers. One of them was İsmail Çağdaş who’s a lead developer from ABP team who talked about the concepts of monoliths and microservices. He explained how modular monoliths combine the strengths of these two architectures, using the ABP Framework as an example to showcase its modularity features and demonstrate how to build and develop a modular monolith application.
**ABP’S Presence**
![](IMG_4732-2000px.webp)
![](IMG_4770-2000px.webp)
At our booth we displayed the latest features of the ABP Framework and gathered valuable feedback from especially dotnet developers. So many attendees showed interest in ABP which was very exciting. It was great engaging with so many participants who wanted to learn more about how ABP provides the infrastructure and tools to create business solutions.
We're also very grateful to our booth neighbor, Xceed and it was a lot of fun connecting with them during the conference. For those who don’t know, Xceed provides comprehensive UI components that allow developers to focus on innovation and their business requirements.
**Networking and Community Engagement**
We organized two raffles during the event, where attendees had the chance to win 2 great prizes. One attendee won a LEGO set and another won an Amazon Kindle. We were happy to see many people attending our raffles, it definitely made this event more fun. Congratulations to the winners and thanks to those who participated\!
![](IMG_4915-2000px.webp)
![](20250305_151043.webp)
**Looking Ahead**
BASTA\! Frankfurt 2025 strengthened our commitment to the developer community. We want to continue our support for asp.net core developers, helping them create asp.net applications and optimize their workflows for web applications.
**Gratitude and Future Events**
Thank you to the organizers, speakers, and attendees for making BASTA\! Frankfurt 2025 such a fantastic experience. We look forward to future events and continued contributions to the net framework developers.
We look forward to sharing more updates with you soon. We hope to see you at our next event\!

73
docs/en/framework/infrastructure/background-jobs/index.md

@ -236,6 +236,79 @@ Background Jobs module implements `IBackgroundJobStore` using various data acces
> Background Jobs module is already installed to the startup templates by default and it works based on your ORM/data access choice.
### Using the Same Storage for Background Jobs & Workers
If multiple applications share the same storage for background jobs and workers (`Default, Hangfire, RabbitMQ, and Quartz`), you should configure the provider options to use the application name for isolation.
#### Default Background Job/Workers
Set `ApplicationName` property in `AbpBackgroundJobWorkerOptions` to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpBackgroundJobWorkerOptions>(options =>
{
options.ApplicationName = context.Services.GetApplicationName()!;
});
}
````
#### Hangfire Background Job/Workers
Set `DefaultQueuePrefix` property in `AbpHangfireOptions` to your application's name:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpHangfireOptions>(options =>
{
options.DefaultQueuePrefix = context.Services.GetApplicationName()!;
});
}
````
#### Quartz Background Job/Workers
Set the `quartz.scheduler.instanceName` property to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<AbpQuartzOptions>(options =>
{
options.Properties = new NameValueCollection
{
["quartz.scheduler.instanceName"] = context.Services.GetApplicationName(),
["quartz.jobStore.dataSource"] = "BackgroundJobsDemoApp",
["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
["quartz.jobStore.tablePrefix"] = "QRTZ_",
["quartz.serializer.type"] = "json",
["quartz.dataSource.BackgroundJobsDemoApp.connectionString"] = configuration.GetConnectionString("Default"),
["quartz.dataSource.BackgroundJobsDemoApp.provider"] = "SqlServer",
["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz",
};
});
}
````
#### RabbitMQ Background Job
Set `DefaultQueueNamePrefix` and `DefaultDelayedQueueNamePrefix` properties in `AbpRabbitMqBackgroundJobOptions` to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpRabbitMqBackgroundJobOptions>(options =>
{
options.DefaultQueueNamePrefix = context.Services.GetApplicationName()!.EndsWith('.') + options.DefaultQueueNamePrefix;
options.DefaultDelayedQueueNamePrefix = context.Services.GetApplicationName()!.EndsWith('.') + options.DefaultDelayedQueueNamePrefix;
});
}
````
### Clustered Deployment
The default background job manager is compatible with [clustered environments](../../../deployment/clustered-environment.md) (where multiple instances of your application run concurrently). It uses a [distributed lock](../distributed-locking.md) to ensure that the jobs are executed only in a single application instance at a time.

74
docs/en/framework/infrastructure/background-workers/index.md

@ -132,6 +132,80 @@ If that's a problem for your workers, you have the following options:
* Stop the background workers (set `AbpBackgroundWorkerOptions.IsEnabled` to `false`) in all application instances except one of them, so only the single instance runs the workers.
* Stop the background workers (set `AbpBackgroundWorkerOptions.IsEnabled` to `false`) in all application instances and create a dedicated application (maybe a console application running in its own container or a Windows Service running in the background) to execute all the background tasks. This can be a good option if your background workers consume high system resources (CPU, RAM or Disk), so you can deploy that background application to a dedicated server and your background tasks don't affect your application's performance.
### Using the Same Storage for Background Jobs & Workers
If multiple applications share the same storage for background jobs and workers (`Default, Hangfire, RabbitMQ, and Quartz`), you should configure the provider options to use the application name for isolation.
#### Default Background Job/Workers
Set `ApplicationName` property in `AbpBackgroundJobWorkerOptions` to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpBackgroundJobWorkerOptions>(options =>
{
options.ApplicationName = context.Services.GetApplicationName()!;
});
}
````
#### Hangfire Background Job/Workers
Set `DefaultQueuePrefix` property in `AbpHangfireOptions` to your application's name:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpHangfireOptions>(options =>
{
options.DefaultQueuePrefix = context.Services.GetApplicationName()!;
});
}
````
#### Quartz Background Job/Workers
Set `quartz.scheduler.instanceName` property to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<AbpQuartzOptions>(options =>
{
options.Properties = new NameValueCollection
{
["quartz.scheduler.instanceName"] = context.Services.GetApplicationName(),
["quartz.jobStore.dataSource"] = "BackgroundJobsDemoApp",
["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
["quartz.jobStore.tablePrefix"] = "QRTZ_",
["quartz.serializer.type"] = "json",
["quartz.dataSource.BackgroundJobsDemoApp.connectionString"] = configuration.GetConnectionString("Default"),
["quartz.dataSource.BackgroundJobsDemoApp.provider"] = "SqlServer",
["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz",
};
});
}
````
#### RabbitMQ Background Job
Set `DefaultQueueNamePrefix` and `DefaultDelayedQueueNamePrefix` properties in `AbpRabbitMqBackgroundJobOptions` to your application's name:
````csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpRabbitMqBackgroundJobOptions>(options =>
{
options.DefaultQueueNamePrefix = context.Services.GetApplicationName()!.EndsWith('.') + options.DefaultQueueNamePrefix;
options.DefaultDelayedQueueNamePrefix = context.Services.GetApplicationName()!.EndsWith('.') + options.DefaultDelayedQueueNamePrefix;
});
}
````
## Integrations
Background worker system is extensible and you can change the default background worker manager with your own implementation or on of the pre-built integrations.

34
docs/en/framework/infrastructure/cancellation-token-provider.md

@ -1,10 +1,14 @@
# Cancellation Token Provider
A `CancellationToken` enables cooperative cancellation between threads, thread pool work items, or `Task` objects. To handle the possible cancellation of the operation, ABP provides `ICancellationTokenProvider` to obtain the `CancellationToken` itself from the source.
A [`CancellationToken`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) enables cooperative cancellation between threads, thread pool work items, or `Task` objects. To handle the possible cancellation of the operation, ABP provides `ICancellationTokenProvider` to obtain the `CancellationToken` itself from the source.
> To get more information about `CancellationToken`, see [Microsoft Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken).
## When To Use Manual Cancellation Tokens
## ICancellationTokenProvider
**ABP automates cancellation token usage wherever possible**. For example, in ASP.NET Core applications, ABP automatically obtains the `CancellationToken` from the `HttpContext.RequestAborted` and uses it in database queries and other cancellable places. So, most of the times, you don't need to deal with `CancellationToken` objects to pass them between methods.
> You do not need to use the `ICancellationTokenProvider` unless you want to add cancellation support in your own logic or to pass a cancellation token to a method outside of the ABP framework.
## ICancellationTokenProvider Service
`ICancellationTokenProvider` is an abstraction to provide `CancellationToken` for different scenarios.
@ -39,33 +43,27 @@ namespace MyProject
}
```
## Built-in providers
## Built-in Providers
- `NullCancellationTokenProvider`
- `HttpContextCancellationTokenProvider`: The **default provider** for ASP.NET Core applications. It simply provides a `CancellationToken` that is source of the web request from the `HttpContext.RequestAborted`.
The `NullCancellationTokenProvider` is a built in provider and it supply always `CancellationToken.None`.
- `NullCancellationTokenProvider`: A built in provider and it supply always `CancellationToken.None`. It is used if no other providers can be used.
- `HttpContextCancellationTokenProvider`
The `HttpContextCancellationTokenProvider` is a built in default provider for ABP Web applications. It simply provides a `CancellationToken` that is source of the web request from the `HttpContext`.
## Implementing a Custom Cancellation Token Provider
## Implementing the ICancellationTokenProvider
You can easily create your CancellationTokenProvider by creating a class that implements the `ICancellationTokenProvider` interface, as shown below:
You can easily create your `ICancellationTokenProvider` implementation by creating a class that implements the `ICancellationTokenProvider` interface, as shown below:
```csharp
using System.Threading;
namespace AbpDemo
{
public class MyCancellationTokenProvider : ICancellationTokenProvider
public class MyCancellationTokenProvider
: ICancellationTokenProvider,
ITransientDependency // Can also be singleton or scoped
{
public CancellationToken Token { get; }
private MyCancellationTokenProvider()
{
}
public CancellationToken Token { get; } // TODO: Return a cancellation token
}
}
```

18
docs/en/guides/microservice-mongodb.md

@ -1,12 +1,12 @@
# Change Microservice project to use MongoDB Provider
This guide explains how to change Microservice project to use MongoDB as the database provider.
This guide explains how to change the Microservice project to use MongoDB as the database provider.
## Create a new Microservice project
Use the ABP Suite to create a new Microservice project, in this guide we use `BookStore` as project name.
> You can use [ABP Studio](../studio/index.md) to create a new Microservice project. In this guide, we assume that you have already created a microservice solution and used `BookStore` as your project name.
You will get the solution as shown below:
Currently, your microservice solution should be similar to that in the figure below:
![microservce-mongodb-project](../images/microservice-mongodb.png)
@ -16,9 +16,9 @@ You need to replace all EF Core references with MongoDB and update module depend
Example:
* Remove `Volo.Abp.EntityFrameworkCore.SqlServer` Nuget package reference.
* Remove `Microsoft.EntityFrameworkCore.Tools` Nuget package reference
* Remove `AbpEntityFrameworkCoreSqlServerModule` depend.
* Remove `Volo.Abp.EntityFrameworkCore.SqlServer` NuGet package reference. (instead use `Volo.Abp.MongoDB` NuGet package and also add the relevant depends on statement)
* Remove `Microsoft.EntityFrameworkCore.Tools` NuGet package reference.
* Remove `AbpEntityFrameworkCoreSqlServerModule` dependency.
* Replace `Volo.Abp.PermissionManagement.EntityFrameworkCore` with `Volo.Abp.PermissionManagement.MongoDB`.
* Update `AbpPermissionManagementEntityFrameworkCoreModule` to `AbpPermissionManagementMongoDbModule`.
@ -43,9 +43,9 @@ Here we use `BookStore.ProductService` project as an example:
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>(b =>
{
b.CollectionName = ProductServiceDbProperties.DbTablePrefix + "Products";
});
{
b.CollectionName = ProductServiceDbProperties.DbTablePrefix + "Products";
});
}
}
```

BIN
docs/en/images/books.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

17
docs/en/index.md

@ -93,21 +93,12 @@ ABP provides a UI theming system where you can use [pre-built themes](ui-themes)
## Books
### Mastering ABP Framework
You can check the following books to expertise the ABP platform:
<img width="302" height="372" src="images/abp-book.png" alt="Mastering ABP - Halil İbrahim Kalkan" />
![books](images/books.png)
*Mastering ABP Framework* is an ultimate guide to get started and expertise with the ABP Framework. It is authored by Halil İbrahim Kalkan, the creator and the lead developer of the ABP Framework.
**[Click here to see the details and order the book](https://abp.io/books/mastering-abp-framework)**.
### Free E-Book: Implementing Domain Driven Design
<img width="441" height="331" src="images/implementing-domain-driven-design-book.png" alt="Implementing Domain Driven Design">
A **practical guide** for implementing Domain Driven Design (DDD). While the implementation details are **based on the ABP** infrastructure, the basic concepts, principles and models can be applied to any solution, even if it is not a .NET solution.
**[Click here to get your free copy](https://abp.io/books/implementing-domain-driven-design?ref=doc).**
* [See the details and **order** the **Mastering ABP Framework** book](https://abp.io/books/mastering-abp-framework)
* [**Download** your free copy of the **Implementing Domain Driven Design** book](https://abp.io/books/implementing-domain-driven-design?ref=doc)
## Samples

39
docs/en/kb/can-not-login-with-admin-user.md

@ -1,5 +1,36 @@
# KB#0003: Can not login with the admin user
# KB#0003: Cannot login with the admin user
* Try username `admin` and Password `1q2w3E*`.
* Try to migrate database. If you have a `DbMigrator` application in your solution, use it. It will seed initial data and create the admin user for you.
* If not works, read the README.MD file in your solution, or check the [Getting Started](https://abp.io/docs/latest/get-started) document.
## Use the Correct Username and Password
You may have entered the wrong password. The username is `admin`, and the password is `1q2w3E*`. Note that the password is case-sensitive.
## Forgot to Seed Initial Data
You may need to add migrations and update the database using the EF Core CLI. If your solution includes a `DbMigrator` application, you must run the `DbMigrator` application to seed the initial data.
If your project does not include a `DbMigrator` application, there might be a `migrate-database.ps1` script available. You can use it to migrate and seed the initial data.
> The no-layer application typically support a `--migrate-database` option for migrating and seeding initial data.
> Example:
> ```bash
> dotnet run --migrate-database
> ```
## Tenant Admin User
If you cannot log in as a tenant admin user, ensure the tenant database is created and seeded, Use the password that was set during tenant creation.
> The tenant seeding process is handled by the template project. If it is not completed, please check the `Logs` file for any error logs.
## Check the `AbpUsers` Table
If you have performed migration and seeded the initial data, check the `AbpUsers` table in the database. Ensure that the user record exists. If your tenant has a separate database, check the tenant database as well.
Passwords are stored in hashed format, not plain text. If you suspect the password is incorrect, you can delete the user record and re-seed the initial data using the `DbMigrator` application or the `migrate-database.ps1` script.
## Other Issues
If the issue persists, refer to the `README.MD` file in your solution or consult the [Getting Started](https://abp.io/docs/latest/get-started) documentation.
Feel free to create an issue in the [ABP GitHub repository](https://github.com/abpframework/abp/issues/new/choose) or contact [ABP Commercial Support](https://abp.io/support/questions/New) for assistance.

2
docs/en/release-info/migration-guides/abp-9-0.md

@ -2,7 +2,7 @@
This document is a guide for upgrading ABP v8.x solutions to ABP v9.0. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application.
> ABP has upgraded to .NET 9.0, so if you plan to use ABP 9.0, you’ll need to migrate your solutions to .NET 9.0. You can refer to the [Migrate from ASP.NET Core 8.0 to 9.0](https://learn.microsoft.com/en-us/aspnet/core/migration/80-90) documentation for guidance. However, ABP’s NuGet packages are compatible with both .NET 8 and .NET 9, allowing developers to continue using .NET 8 while still enjoying the latest features and improvements of the ABP Framework without upgrading their SDK.
> ABP has upgraded to .NET 9.0, so if you plan to use ABP 9.0, you’ll need to migrate your solutions to .NET 9.0. You can refer to the [Migrate from ASP.NET Core 8.0 to 9.0](https://learn.microsoft.com/en-us/aspnet/core/migration/80-90) documentation for guidance.
## Open-Source (Framework)

7
docs/en/studio/release-notes.md

@ -2,6 +2,13 @@
This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version.
## 0.9.23 (2025-02-04)
* Fixed **Open with Terminal** option not working on macOS.
* Fixed dynamic port assignment for Abp Suite if the default port is unavailable.
* Added suite templates package to module templates.
* Added warning message in CLI if the connection string is broken.
## 0.9.22 (2025-01-22)
* Allowed to display multiple installation notes.

2
docs/en/studio/version-mapping.md

@ -4,7 +4,7 @@ This document provides a general overview of the relationship between various ve
| **ABP Studio Version** | **ABP Version of Startup Template** |
|------------------------|---------------------------|
| 0.9.22 | 9.0.4 |
| 0.9.22 - 0.9.23 | 9.0.4 |
| 0.9.20 - 0.9.21 | 9.0.3 |
| 0.9.17 to 0.9.19 | 9.0.2 |
| 0.9.15 - 0.9.16 | 9.0.1 |

17
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs

@ -17,17 +17,14 @@ public class ComponentsComponentsBundleContributor : IBundleContributor
public void AddStyles(BundleContext context)
{
if (!context.InteractiveAuto)
context.BundleDefinitions.Insert(0, new BundleDefinition
{
context.BundleDefinitions.Insert(0, new BundleDefinition
{
Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/bootstrap/css/bootstrap.min.css"
});
context.BundleDefinitions.Insert(1, new BundleDefinition
{
Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/fontawesome/css/all.css"
});
}
Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/bootstrap/css/bootstrap.min.css"
});
context.BundleDefinitions.Insert(1, new BundleDefinition
{
Source = "_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/fontawesome/css/all.css"
});
context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/css/abp.css");
context.Add("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/flag-icon/css/flag-icon.css");

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs

@ -46,9 +46,9 @@ public abstract class
public bool AddMarginBottomClass { get; set; } = true;
protected AbpDatePickerBaseTagHelper(AbpDatePickerBaseTagHelperService<TTagHelper> service, IOptions<AbpDatePickerOptions> options) : base(service)
protected AbpDatePickerBaseTagHelper(AbpDatePickerBaseTagHelperService<TTagHelper> service, IOptionsFactory<AbpDatePickerOptions> optionsFactory) : base(service)
{
_abpDatePickerOptionsImplementation = options.Value;
_abpDatePickerOptionsImplementation = optionsFactory.Create(string.Empty);
}
public void SetDatePickerOptions(IAbpDatePickerOptions options)

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs

@ -9,7 +9,10 @@ public class AbpDatePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDatePickerTa
{
public ModelExpression? AspFor { get; set; }
public AbpDatePickerTagHelper(AbpDatePickerTagHelperService service, IOptions<AbpDatePickerOptions> options) : base(service, options)
public AbpDatePickerTagHelper(
AbpDatePickerTagHelperService service,
IOptionsFactory<AbpDatePickerOptions> optionsFactory)
: base(service, optionsFactory)
{
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs

@ -11,8 +11,10 @@ public class AbpDateRangePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDateRan
public ModelExpression? AspForEnd { get; set; }
public AbpDateRangePickerTagHelper(AbpDateRangePickerTagHelperService tagHelperService, IOptions<AbpDatePickerOptions> options) :
base(tagHelperService, options)
public AbpDateRangePickerTagHelper(
AbpDateRangePickerTagHelperService service,
IOptionsFactory<AbpDatePickerOptions> optionsFactory)
: base(service, optionsFactory)
{
}
}

24
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Hosting;
@ -9,8 +10,10 @@ using Microsoft.AspNetCore.StaticAssets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Security;
@ -184,6 +187,27 @@ public static class AbpApplicationBuilderExtensions
// https://github.com/dotnet/aspnetcore/issues/59673
app.UseStaticFiles();
if (!staticAssetsManifestPath.IsNullOrWhiteSpace())
{
app.ApplicationServices.GetRequiredService<ILogger<AbpAspNetCoreModule>>().LogWarning(
$"The staticAssetsManifestPath({staticAssetsManifestPath}) parameter your provided in MapAbpStaticAssets method is ignored in development mode.");
}
var blazorClientProjectPaths = new[]
{
Path.Combine(AppContext.BaseDirectory, $"{environment.ApplicationName}.Client.staticwebassets.endpoints.json"),
Path.Combine(AppContext.BaseDirectory, $"{environment.ApplicationName.RemovePostFix(".Host")}.Blazor.staticwebassets.endpoints.json"),
};
var blazorClientStaticAssetsManifest = blazorClientProjectPaths.FirstOrDefault(File.Exists);
if (blazorClientStaticAssetsManifest != null)
{
// We have a blazor client project and we need to map the static assets from the client project.
var blazorHostStaticAssetsManifest = Path.Combine(AppContext.BaseDirectory, $"{environment.ApplicationName}.staticwebassets.endpoints.json");
File.Copy(blazorClientStaticAssetsManifest, blazorHostStaticAssetsManifest, true);
return endpoints.MapStaticAssets(blazorHostStaticAssetsManifest);
}
// Volo.Abp.AspNetCore.staticwebassets.endpoints.json is an empty file. Just compatible with the return type of MapAbpStaticAssets.
var tempStaticAssetsManifestPath = Path.Combine(AppContext.BaseDirectory, "Volo.Abp.AspNetCore.staticwebassets.endpoints.json");
if (!File.Exists(tempStaticAssetsManifestPath))

19
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs

@ -5,17 +5,20 @@ using Hangfire;
using Hangfire.States;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Hangfire;
namespace Volo.Abp.BackgroundJobs.Hangfire;
[Dependency(ReplaceServices = true)]
public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
protected AbpBackgroundJobOptions Options { get; }
protected IOptions<AbpBackgroundJobOptions> BackgroundJobOptions { get; }
protected IOptions<AbpHangfireOptions> HangfireOptions { get; }
public HangfireBackgroundJobManager(IOptions<AbpBackgroundJobOptions> options)
public HangfireBackgroundJobManager(IOptions<AbpBackgroundJobOptions> backgroundJobOptions, IOptions<AbpHangfireOptions> hangfireOptions)
{
Options = options.Value;
BackgroundJobOptions = backgroundJobOptions;
HangfireOptions = hangfireOptions;
}
public virtual Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
@ -33,13 +36,7 @@ public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDep
protected virtual string GetQueueName(Type argsType)
{
var queueName = EnqueuedState.DefaultQueue;
var queueAttribute = Options.GetJob(argsType).JobType.GetCustomAttribute<QueueAttribute>();
if (queueAttribute != null)
{
queueName = queueAttribute.Queue;
}
return queueName;
var queueAttribute = BackgroundJobOptions.Value.GetJob(argsType).JobType.GetCustomAttribute<QueueAttribute>();
return queueAttribute != null ? HangfireOptions.Value.DefaultQueuePrefix + queueAttribute.Queue : HangfireOptions.Value.DefaultQueue;
}
}

5
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobWorkerOptions.cs

@ -2,6 +2,11 @@
public class AbpBackgroundJobWorkerOptions
{
/// <summary>
/// Application name.
/// </summary>
public string? ApplicationName { get; set; }
/// <summary>
/// Interval between polling jobs from <see cref="IBackgroundJobStore"/>.
/// Default value: 5000 (5 seconds).

5
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs

@ -9,6 +9,11 @@ public class BackgroundJobInfo
{
public Guid Id { get; set; }
/// <summary>
/// Application name.
/// </summary>
public virtual string? ApplicationName { get; set; }
/// <summary>
/// Name of the job.
/// </summary>

2
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs

@ -43,7 +43,7 @@ public class BackgroundJobWorker : AsyncPeriodicBackgroundWorkerBase, IBackgroun
{
var store = workerContext.ServiceProvider.GetRequiredService<IBackgroundJobStore>();
var waitingJobs = await store.GetWaitingJobsAsync(WorkerOptions.MaxJobFetchCount);
var waitingJobs = await store.GetWaitingJobsAsync(WorkerOptions.ApplicationName, WorkerOptions.MaxJobFetchCount);
if (!waitingJobs.Any())
{

8
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs

@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Timing;
@ -16,16 +18,19 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe
protected IBackgroundJobSerializer Serializer { get; }
protected IGuidGenerator GuidGenerator { get; }
protected IBackgroundJobStore Store { get; }
protected IOptions<AbpBackgroundJobWorkerOptions> BackgroundJobWorkerOptions { get; }
public DefaultBackgroundJobManager(
IClock clock,
IBackgroundJobSerializer serializer,
IBackgroundJobStore store,
IGuidGenerator guidGenerator)
IGuidGenerator guidGenerator,
IOptions<AbpBackgroundJobWorkerOptions> backgroundJobWorkerOptions)
{
Clock = clock;
Serializer = serializer;
GuidGenerator = guidGenerator;
BackgroundJobWorkerOptions = backgroundJobWorkerOptions;
Store = store;
}
@ -41,6 +46,7 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe
var jobInfo = new BackgroundJobInfo
{
Id = GuidGenerator.Create(),
ApplicationName = BackgroundJobWorkerOptions.Value.ApplicationName,
JobName = jobName,
JobArgs = Serializer.Serialize(args),
Priority = priority,

5
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/IBackgroundJobStore.cs

@ -24,12 +24,13 @@ public interface IBackgroundJobStore
/// <summary>
/// Gets waiting jobs. It should get jobs based on these:
/// Conditions: !IsAbandoned And NextTryTime &lt;= Clock.Now.
/// Conditions: ApplicationName is applicationName And !IsAbandoned And NextTryTime &lt;= Clock.Now.
/// Order by: Priority DESC, TryCount ASC, NextTryTime ASC.
/// Maximum result: <paramref name="maxResultCount"/>.
/// </summary>
/// <param name="applicationName">Application name.</param>
/// <param name="maxResultCount">Maximum result count.</param>
Task<List<BackgroundJobInfo>> GetWaitingJobsAsync(int maxResultCount);
Task<List<BackgroundJobInfo>> GetWaitingJobsAsync(string? applicationName, int maxResultCount);
/// <summary>
/// Deletes a job.

3
framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs

@ -35,9 +35,10 @@ public class InMemoryBackgroundJobStore : IBackgroundJobStore, ISingletonDepende
return Task.CompletedTask;
}
public virtual Task<List<BackgroundJobInfo>> GetWaitingJobsAsync(int maxResultCount)
public virtual Task<List<BackgroundJobInfo>> GetWaitingJobsAsync(string? applicationName, int maxResultCount)
{
var waitingJobs = _jobs.Values
.Where(t => t.ApplicationName == applicationName)
.Where(t => !t.IsAbandoned && t.NextTryTime <= Clock.Now)
.OrderByDescending(t => t.Priority)
.ThenBy(t => t.TryCount)

2
framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs

@ -13,7 +13,7 @@ public abstract class HangfireBackgroundWorkerBase : BackgroundWorkerBase, IHang
public TimeZoneInfo? TimeZone { get; set; } = TimeZoneInfo.Utc;
public string Queue { get; set; } = EnqueuedState.DefaultQueue;
public string Queue { get; set; } = default!;
public abstract Task DoWorkAsync(CancellationToken cancellationToken = default);
}

23
framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs

@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using Hangfire;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Hangfire;
@ -30,15 +31,19 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
public async override Task AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken = default)
{
var abpHangfireOptions = ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
var defaultQueuePrefix = abpHangfireOptions.DefaultQueuePrefix;
var defaultQueue = abpHangfireOptions.DefaultQueue;
switch (worker)
{
case IHangfireBackgroundWorker hangfireBackgroundWorker:
{
var unProxyWorker = ProxyHelper.UnProxy(hangfireBackgroundWorker);
RecurringJob.AddOrUpdate(
hangfireBackgroundWorker.RecurringJobId,
hangfireBackgroundWorker.Queue,
hangfireBackgroundWorker.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + hangfireBackgroundWorker.Queue,
() => ((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(cancellationToken),
hangfireBackgroundWorker.CronExpression,
new RecurringJobOptions
@ -57,24 +62,24 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
{
return;
}
var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(ProxyHelper.GetUnProxiedType(worker));
var workerAdapter = (Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker)!;
if (workerAdapter.RecurringJobId.IsNullOrWhiteSpace())
{
RecurringJob.AddOrUpdate(
RecurringJob.AddOrUpdate(
() => workerAdapter.DoWorkAsync(cancellationToken),
GetCron(period.Value),
workerAdapter.TimeZone ,
workerAdapter.Queue);
workerAdapter.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + workerAdapter.Queue);
}
else
{
RecurringJob.AddOrUpdate(
workerAdapter.RecurringJobId,
workerAdapter.Queue,
workerAdapter.Queue.IsNullOrWhiteSpace() ? defaultQueue : defaultQueuePrefix + workerAdapter.Queue,
() => workerAdapter.DoWorkAsync(cancellationToken),
GetCron(period.Value),
new RecurringJobOptions
@ -82,7 +87,7 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
TimeZone = workerAdapter.TimeZone
});
}
break;
}
@ -121,4 +126,4 @@ public class HangfireBackgroundWorkerManager : BackgroundWorkerManager, ISinglet
return cron;
}
}
}

7
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs

@ -7,4 +7,11 @@ public class AbpEntityChangeOptions
/// Publish the EntityUpdatedEvent when any navigation property changes.
/// </summary>
public bool PublishEntityUpdatedEventWhenNavigationChanges { get; set; } = true;
public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; }
public AbpEntityChangeOptions()
{
IgnoredNavigationEntitySelectors = new EntitySelectorList();
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorList.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events;
internal class EntitySelectorList : List<NamedTypeSelector>, IEntitySelectorList
{
}

12
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorListExtensions.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.Domain.Entities.Events;
public static class EntitySelectorListExtensions
{
public static IEntitySelectorList Add(this IEntitySelectorList selectors, string name, Func<Type, bool> predicate)
{
selectors.Add(new NamedTypeSelector(name, predicate));
return selectors;
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntitySelectorList.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events;
public interface IEntitySelectorList : IList<NamedTypeSelector>
{
}

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

@ -202,46 +202,11 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
public async override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
try
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
if (entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()))
{
// Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed.
break;
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}
await PublishEventsForChangedEntityOnSaveChangeAsync();
var auditLog = AuditingManager?.Current?.Log;
List<EntityChangeInfo>? entityChangeList = null;
@ -296,6 +261,55 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
}
protected virtual Task PublishEventsForChangedEntityOnSaveChangeAsync()
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
var ignoredEntity = EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.Any(selector => selector.Predicate(entityEntry.Entity.GetType()));
var onlyForeignKeyModifiedEntity = entityEntry.State == EntityState.Modified && entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey());
if ((entityEntry.State == EntityState.Unchanged && ignoredEntity) || onlyForeignKeyModifiedEntity && ignoredEntity)
{
continue;
}
if (entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()))
{
// Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed.
break;
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}
return Task.CompletedTask;
}
protected virtual void PublishEntityEvents(EntityEventReport changeReport)
{
foreach (var localEvent in changeReport.DomainEvents)
@ -428,7 +442,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
}
}
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges &&
EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(entry.Entity.GetType())) &&
AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
{
ApplyAbpConceptsForModifiedEntity(entry, true);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
@ -464,7 +480,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries().Where(x => x.State == EntityState.Unchanged))
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()
.Where(x => x.State == EntityState.Unchanged)
.Where(x=> EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType()))))
{
UpdateConcurrencyStamp(entry);
}

4
framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs

@ -1,5 +1,6 @@
using Hangfire;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization;
using Volo.Abp.Modularity;
@ -24,7 +25,10 @@ public class AbpHangfireModule : AbpModule
var options = serviceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
return new AbpHangfireBackgroundJobServer(options.BackgroundJobServerFactory.Invoke(serviceProvider));
});
context.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<AbpHangfireOptions>, AbpHangfireOptionsConfiguration>());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
_backgroundJobServer = context.ServiceProvider.GetRequiredService<AbpHangfireBackgroundJobServer>();

12
framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireOptions.cs

@ -12,6 +12,18 @@ namespace Volo.Abp.Hangfire;
public class AbpHangfireOptions
{
/// <summary>
/// This value is used to add prefix to all of the queues. Default is empty.
/// </summary>
public string DefaultQueuePrefix { get; set; } = string.Empty;
/// <summary>
/// Hangfire queue name max length, default is 50.
/// </summary>
public int MaxQueueNameLength { get; set; } = 50;
public string DefaultQueue { get; set; } = EnqueuedState.DefaultQueue;
public BackgroundJobServerOptions? ServerOptions { get; set; }
public IEnumerable<IBackgroundProcess>? AdditionalProcesses { get; set; }

65
framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireOptionsConfiguration.cs

@ -0,0 +1,65 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Hangfire;
using Hangfire.States;
using Microsoft.Extensions.Options;
namespace Volo.Abp.Hangfire;
public class AbpHangfireOptionsConfiguration : IPostConfigureOptions<AbpHangfireOptions>
{
public void PostConfigure(string? name, AbpHangfireOptions options)
{
if (options.DefaultQueuePrefix.IsNullOrWhiteSpace())
{
return;;
}
// The Queue name argument must consist of lowercase letters, digits, underscore, and dash characters only.
var queuesPrefix = Regex.Replace(options.DefaultQueuePrefix.ToLower().Replace(".", "_"), "[^a-z0-9_-]", "");
if (queuesPrefix.IsNullOrWhiteSpace())
{
throw new AbpException($"The QueuesPrefix({options.DefaultQueuePrefix}) is not valid, it must consist of lowercase letters, digits, underscore, and dash characters only.");
}
options.DefaultQueuePrefix = queuesPrefix.EnsureEndsWith('_');
if (options.ServerOptions == null)
{
var queue = $"{options.DefaultQueuePrefix}{EnqueuedState.DefaultQueue}";
if (queue.Length > options.MaxQueueNameLength)
{
throw new AbpException($"The maximum length of the Hangfire queue name({queue}) is {options.MaxQueueNameLength}, Please configure the AbpHangfireOptions.DefaultQueuePrefix manually.");
}
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = new[] { queue }
};
options.DefaultQueue = queue;
}
else
{
var queues = options.ServerOptions.Queues;
for (var i = 0; i < queues.Length; i++)
{
var queue = $"{options.DefaultQueuePrefix}{queues[i]}";
if (queue.Length > options.MaxQueueNameLength)
{
throw new AbpException($"The maximum length of the Hangfire queue name({queue}) is {options.MaxQueueNameLength}, Please configure the AbpHangfireOptions.DefaultQueuePrefix manually.");
}
queues[i] = queue;
}
var defaultQueue = queues.FirstOrDefault(q => q.EndsWith(EnqueuedState.DefaultQueue));
if (defaultQueue.IsNullOrWhiteSpace())
{
defaultQueue = queues.FirstOrDefault();
if (defaultQueue.IsNullOrWhiteSpace())
{
throw new AbpException("There is no queue defined in the Hangfire configuration!");
}
}
options.DefaultQueue = defaultQueue;
}
}
}

25
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs

@ -18,8 +18,31 @@ public class DomainEvents_Tests : DomainEvents_Tests<AbpEntityFrameworkCoreTestM
{
}
public class AbpEntityChangeOptions_DomainEvents_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
public class AbpEntityChangeOptions_DomainEvents_PublishEntityUpdatedEventWhenNavigationChanges_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = false;
});
base.AfterAddApplication(services);
}
}
public class AbpEntityChangeOptions_DomainEvents_IgnoreEntityChangeSelectorList_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = true;
options.IgnoredNavigationEntitySelectors.Add("DisableAllEntity", _ => true);
});
base.AfterAddApplication(services);
}
}
public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase

10
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

@ -259,16 +259,6 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
LocalEventBus = GetRequiredService<ILocalEventBus>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = false;
});
base.AfterAddApplication(services);
}
[Fact]
public async Task Should_Not_Trigger_Domain_Events_For_Aggregate_Root_When_Navigation_Changes_Tests()
{

18
latest-versions.json

@ -1,4 +1,22 @@
[
{
"version": "9.1.0",
"releaseDate": "",
"type": "stable",
"message": "",
"leptonx": {
"version": "4.1.0"
}
},
{
"version": "9.0.6",
"releaseDate": "",
"type": "stable",
"message": "",
"leptonx": {
"version": "4.0.8"
}
},
{
"version": "9.0.5",
"releaseDate": "",

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json

@ -68,6 +68,8 @@
"AccessDenied": "تم الرفض!",
"AccessDeniedMessage": "ليس لديك حق الوصول إلى هذا المورد.",
"OrRegisterWith": "أو التسجيل بـ:",
"RegisterUsingYourProviderAccount": "قم بالتسجيل باستخدام حسابك في {0}"
"RegisterUsingYourProviderAccount": "قم بالتسجيل باستخدام حسابك في {0}",
"RequireMigrateSeedTitle": "تعذر العثور على المستخدم المسؤول",
"RequireMigrateSeedMessage": "يرجى التأكد من تنفيذ seed قاعدة البيانات. راجع <a target=\"_blank\" href=\"https://abp.io/kb/0003\">الوثائق</a> للحلول الممكنة."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json

@ -68,6 +68,8 @@
"AccessDenied": "Přístup odepřen!",
"AccessDeniedMessage": "K tomuto zdroji nemáte přístup.",
"OrRegisterWith": "Nebo se registrujte pomocí:",
"RegisterUsingYourProviderAccount": "Registrovat pomocí vašeho účtu {0}"
"RegisterUsingYourProviderAccount": "Registrovat pomocí vašeho účtu {0}",
"RequireMigrateSeedTitle": "Nelze najít uživatele správce",
"RequireMigrateSeedMessage": "Ujistěte se, že je spuštěn seed databáze. Podívejte se na <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentaci</a> pro možná řešení."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/da.json

@ -65,6 +65,8 @@
"AccessDenied": "Adgang nægtet!",
"AccessDeniedMessage": "Du har ikke adgang til denne ressource.",
"OrRegisterWith": "Eller registrér med:",
"RegisterUsingYourProviderAccount": "Registrér med din {0} konto"
"RegisterUsingYourProviderAccount": "Registrér med din {0} konto",
"RequireMigrateSeedTitle": "Kunne ikke finde administratorbrugeren",
"RequireMigrateSeedMessage": "Sørg for, at databasens seed bliver udført. Se <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentationen</a> for mulige løsninger."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/de.json

@ -68,6 +68,8 @@
"AccessDenied": "Zugriff abgelehnt!",
"AccessDeniedMessage": "Sie haben keinen Zugriff auf diese Ressource.",
"OrRegisterWith": "Oder registrieren Sie sich mit:",
"RegisterUsingYourProviderAccount": "Registrieren Sie sich mit Ihrem {0} Benutzerkonto"
"RegisterUsingYourProviderAccount": "Registrieren Sie sich mit Ihrem {0} Benutzerkonto",
"RequireMigrateSeedTitle": "Der Administrator-Benutzer konnte nicht gefunden werden",
"RequireMigrateSeedMessage": "Bitte stellen Sie sicher, dass die Datenbank-Seed ausgeführt wird. Siehe <a target=\"_blank\" href=\"https://abp.io/kb/0003\">Dokumentation</a> für mögliche Lösungen."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/el.json

@ -66,6 +66,8 @@
"AccessDenied": "Δεν επιτρέπεται η πρόσβαση!",
"AccessDeniedMessage": "Δεν έχετε πρόσβαση σε αυτόν τον πόρο.",
"OrRegisterWith": "Ή εγγραφείτε με:",
"RegisterUsingYourProviderAccount": "Εγγραφείτε χρησιμοποιώντας τον λογαριασμό σας {0}"
"RegisterUsingYourProviderAccount": "Εγγραφείτε χρησιμοποιώντας τον λογαριασμό σας {0}",
"RequireMigrateSeedTitle": "Δεν βρέθηκε ο χρήστης διαχειριστή",
"RequireMigrateSeedMessage": "Βεβαιωθείτε ότι εκτελείται το seed της βάσης δεδομένων. Δείτε την <a target=\"_blank\" href=\"https://abp.io/kb/0003\">τεκμηρίωση</a> για πιθανές λύσεις."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en-GB.json

@ -63,6 +63,8 @@
"PasswordResetInfoInEmail": "We received an account recovery request! If you initiated this request, click the following link to reset your password.",
"ResetMyPassword": "Reset my password",
"OrRegisterWith": "Or register with",
"RegisterUsingYourProviderAccount": "Register using your {0} account"
"RegisterUsingYourProviderAccount": "Register using your {0} account",
"RequireMigrateSeedTitle": "Could not find the admin user",
"RequireMigrateSeedMessage": "Please ensure that the database seed is executed. See <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentation</a> for possible solutions."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json

@ -68,6 +68,8 @@
"AccessDenied": "Access denied!",
"AccessDeniedMessage": "You do not have access to this resource.",
"OrRegisterWith": "Or register with",
"RegisterUsingYourProviderAccount": "Register using your {0} account"
"RegisterUsingYourProviderAccount": "Register using your {0} account",
"RequireMigrateSeedTitle": "Could not find the admin user",
"RequireMigrateSeedMessage": "Please ensure that the database seed is executed. See <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentation</a> for possible solutions."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es-mx.json

@ -36,6 +36,8 @@
"Manage": "Administrar",
"MyAccount": "Mi cuenta",
"OrRegisterWith": "O registrarse con",
"RegisterUsingYourProviderAccount": "Registrarse con su cuenta de {0} "
"RegisterUsingYourProviderAccount": "Registrarse con su cuenta de {0} ",
"RequireMigrateSeedTitle": "No se pudo encontrar el usuario administrador",
"RequireMigrateSeedMessage": "Asegúrese de que se ejecute la semilla de la base de datos. Consulte la <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentación</a> para posibles soluciones."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/es.json

@ -68,6 +68,8 @@
"AccessDenied": "¡Acceso denegado!",
"AccessDeniedMessage": "No tienes acceso a este recurso.",
"OrRegisterWith": "O registrarse con:",
"RegisterUsingYourProviderAccount": "Registrarse con su cuenta de {0} "
"RegisterUsingYourProviderAccount": "Registrarse con su cuenta de {0} ",
"RequireMigrateSeedTitle": "No se pudo encontrar el usuario administrador",
"RequireMigrateSeedMessage": "Asegúrese de que se ejecute la semilla de la base de datos. Consulte la <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentación</a> para posibles soluciones."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fa.json

@ -66,6 +66,8 @@
"AccessDenied": "دسترسی ممنوع!",
"AccessDeniedMessage": "شما به این منبع دسترسی ندارید.",
"OrRegisterWith": "یا ثبت نام کنید با:",
"RegisterUsingYourProviderAccount": "با استفاده از حساب {0} خود ثبت نام کنید"
"RegisterUsingYourProviderAccount": "با استفاده از حساب {0} خود ثبت نام کنید",
"RequireMigrateSeedTitle": "کاربر مدیر پیدا نشد",
"RequireMigrateSeedMessage": "لطفاً اطمینان حاصل کنید که seed پایگاه داده اجرا شده است. برای راه‌حل‌های ممکن <a target=\"_blank\" href=\"https://abp.io/kb/0003\">مستندات</a> را ببینید."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fi.json

@ -68,6 +68,8 @@
"AccessDenied": "Pääsy evätty!",
"AccessDeniedMessage": "Sinulla ei ole pääsyä tähän resurssiin.",
"OrRegisterWith": "Tai rekisteröidy:",
"RegisterUsingYourProviderAccount": "Rekisteröidy {0} -tililläsi"
"RegisterUsingYourProviderAccount": "Rekisteröidy {0} -tililläsi",
"RequireMigrateSeedTitle": "Järjestelmänvalvojaa ei löydy",
"RequireMigrateSeedMessage": "Varmista, että tietokannan siemennys on suoritettu. Katso <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentaatio</a> mahdollisia ratkaisuja varten."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/fr.json

@ -68,6 +68,8 @@
"AccessDenied": "Accès refusé!",
"AccessDeniedMessage": "Vous n'avez pas accès à cette ressource.",
"OrRegisterWith": "Or register with",
"RegisterUsingYourProviderAccount": "Register using your {0} account"
"RegisterUsingYourProviderAccount": "Register using your {0} account",
"RequireMigrateSeedTitle": "Impossible de trouver l'utilisateur administrateur",
"RequireMigrateSeedMessage": "Veuillez vous assurer que l'initialisation de la base de données est exécutée. Consultez la <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentation</a> pour les solutions possibles."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hi.json

@ -68,6 +68,8 @@
"AccessDenied": "पहुंच अस्वीकृत!",
"AccessDeniedMessage": "आपके पास इस संसाधन तक पहुँच नहीं है।",
"OrRegisterWith": "या इसके साथ पंजीकरण करें:",
"RegisterUsingYourProviderAccount": "अपने {0} खाते का उपयोग करके पंजीकरण करें"
"RegisterUsingYourProviderAccount": "अपने {0} खाते का उपयोग करके पंजीकरण करें",
"RequireMigrateSeedTitle": "व्यवस्थापक उपयोगकर्ता नहीं मिला",
"RequireMigrateSeedMessage": "कृपया सुनिश्चित करें कि डेटाबेस सीड निष्पादित किया गया है। संभावित समाधानों के लिए <a target=\"_blank\" href=\"https://abp.io/kb/0003\">दस्तावेज़ीकरण</a> देखें।"
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hr.json

@ -68,6 +68,8 @@
"AccessDenied": "Pristup odbijen!",
"AccessDeniedMessage": "Nemate pristup ovom resursu.",
"OrRegisterWith": "Ili se registrirajte sa:",
"RegisterUsingYourProviderAccount": "Registrirajte se koristeći svoj {0} račun"
"RegisterUsingYourProviderAccount": "Registrirajte se koristeći svoj {0} račun",
"RequireMigrateSeedTitle": "Nije moguće pronaći administratorskog korisnika",
"RequireMigrateSeedMessage": "Molimo provjerite je li izvršeno početno punjenje baze podataka. Pogledajte <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentaciju</a> za moguća rješenja."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/hu.json

@ -68,6 +68,8 @@
"AccessDenied": "Hozzáférés megtagadva!",
"AccessDeniedMessage": "Nincs hozzáférése ehhez az erőforráshoz.",
"OrRegisterWith": "Vagy regisztráljon:",
"RegisterUsingYourProviderAccount": "Regisztráljon a(z) {0} fiókjával"
"RegisterUsingYourProviderAccount": "Regisztráljon a(z) {0} fiókjával",
"RequireMigrateSeedTitle": "Nem található az adminisztrátor felhasználó",
"RequireMigrateSeedMessage": "Kérjük, győződjön meg arról, hogy az adatbázis seed végrehajtásra került. Tekintse meg a <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentációt</a> a lehetséges megoldásokért."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/is.json

@ -68,6 +68,8 @@
"AccessDenied": "Aðgangi hafnað!",
"AccessDeniedMessage": "Þú hefur ekki aðgang að þessari auðlind.",
"OrRegisterWith": "Eða skráðu þig með:",
"RegisterUsingYourProviderAccount": "Skráðu þig með {0} aðganginum þínum"
"RegisterUsingYourProviderAccount": "Skráðu þig með {0} aðganginum þínum",
"RequireMigrateSeedTitle": "Ekki tókst að finna stjórnandann",
"RequireMigrateSeedMessage": "Vinsamlegast gakktu úr skugga um að gagnagrunnsfræið sé keyrt. Sjá <a target=\"_blank\" href=\"https://abp.io/kb/0003\">fylgiskjöl</a> fyrir mögulegar lausnir."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/it.json

@ -68,6 +68,8 @@
"AccessDenied": "Accesso negato!",
"AccessDeniedMessage": "Non hai accesso a questa risorsa.",
"OrRegisterWith": "Oppure registrati con:",
"RegisterUsingYourProviderAccount": "Registrati utilizzando il tuo account {0}"
"RegisterUsingYourProviderAccount": "Registrati utilizzando il tuo account {0}",
"RequireMigrateSeedTitle": "Impossibile trovare l'utente amministratore",
"RequireMigrateSeedMessage": "Assicurati che il seed del database sia eseguito. Consulta la <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentazione</a> per possibili soluzioni."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/nl.json

@ -68,6 +68,8 @@
"AccessDenied": "Toegang geweigerd!",
"AccessDeniedMessage": "U heeft geen toegang tot deze bron.",
"OrRegisterWith": "Of registreer met:",
"RegisterUsingYourProviderAccount": "Registreer met uw {0} -account"
"RegisterUsingYourProviderAccount": "Registreer met uw {0} -account",
"RequireMigrateSeedTitle": "Kan de beheerder niet vinden",
"RequireMigrateSeedMessage": "Zorg ervoor dat het database seed wordt uitgevoerd. Zie <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentatie</a> voor mogelijke oplossingen."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json

@ -68,6 +68,8 @@
"AccessDenied": "Brak dostępu!",
"AccessDeniedMessage": "Nie masz dostępu do tego zasobu.",
"OrRegisterWith": "Lub zarejestruj się za pomocą:",
"RegisterUsingYourProviderAccount": "Zarejestruj się za pomocą konta {0}"
"RegisterUsingYourProviderAccount": "Zarejestruj się za pomocą konta {0}",
"RequireMigrateSeedTitle": "Nie można znaleźć użytkownika administratora",
"RequireMigrateSeedMessage": "Upewnij się, że wykonano seed bazy danych. Zobacz <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentację</a>, aby poznać możliwe rozwiązania."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pt-BR.json

@ -68,6 +68,8 @@
"AccessDenied": "Acesso negado!",
"AccessDeniedMessage": "Você não tem acesso a este recurso.",
"OrRegisterWith": "Ou registre-se com:",
"RegisterUsingYourProviderAccount": "Registre-se utilizando sua conta {0}"
"RegisterUsingYourProviderAccount": "Registre-se utilizando sua conta {0}",
"RequireMigrateSeedTitle": "Não foi possível encontrar o usuário administrador",
"RequireMigrateSeedMessage": "Certifique-se de que a semeadura do banco de dados seja executada. Consulte a <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentação</a> para possíveis soluções."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ro-RO.json

@ -68,6 +68,8 @@
"AccessDenied": "Acces interzis!",
"AccessDeniedMessage": "Nu aveţi acces la această resursă.",
"OrRegisterWith": "Sau înregistraţi-vă cu:",
"RegisterUsingYourProviderAccount": "Înregistraţi-vă folosindu-vă contul {0}"
"RegisterUsingYourProviderAccount": "Înregistraţi-vă folosindu-vă contul {0}",
"RequireMigrateSeedTitle": "Nu s-a putut găsi utilizatorul admin",
"RequireMigrateSeedMessage": "Vă rugăm să vă asiguraţi că seed-ul bazei de date este executat. Consultaţi <a target=\"_blank\" href=\"https://abp.io/kb/0003\">documentaţia</a> pentru soluţii posibile."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ru.json

@ -68,6 +68,8 @@
"AccessDenied": "В доступе отказано!",
"AccessDeniedMessage": "У вас нет доступа к этому ресурсу.",
"OrRegisterWith": "Или зарегистрируйтесь с помощью:",
"RegisterUsingYourProviderAccount": "Зарегистрируйтесь, используя свой {0} аккаунт"
"RegisterUsingYourProviderAccount": "Зарегистрируйтесь, используя свой {0} аккаунт",
"RequireMigrateSeedTitle": "Не удалось найти пользователя-администратора",
"RequireMigrateSeedMessage": "Пожалуйста, убедитесь, что выполнено начальное заполнение базы данных. Смотрите <a target=\"_blank\" href=\"https://abp.io/kb/0003\">документацию</a> для возможных решений."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sk.json

@ -68,6 +68,8 @@
"AccessDenied": "Prístup zamietnutý!",
"AccessDeniedMessage": "K tomuto zdroju nemáte prístup.",
"OrRegisterWith": "Alebo sa zaregistrujte pomocou:",
"RegisterUsingYourProviderAccount": "Zaregistrujte sa pomocou svojho {0} účtu"
"RegisterUsingYourProviderAccount": "Zaregistrujte sa pomocou svojho {0} účtu",
"RequireMigrateSeedTitle": "Nepodarilo sa nájsť používateľa správcu",
"RequireMigrateSeedMessage": "Uistite sa, že je spustené seed databázy. Pozrite si <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentáciu</a> pre možné riešenia."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json

@ -68,6 +68,8 @@
"AccessDenied": "Dostop zavrnjen!",
"AccessDeniedMessage": "Nimate dostopa do tega vira.",
"OrRegisterWith": "Ali pa se registrirajte z:",
"RegisterUsingYourProviderAccount": "Registrirajte se z uporabo vašega {0} računa"
"RegisterUsingYourProviderAccount": "Registrirajte se z uporabo vašega {0} računa",
"RequireMigrateSeedTitle": "Skrbniškega uporabnika ni mogoče najti",
"RequireMigrateSeedMessage": "Prepričajte se, da je seed baze podatkov izveden. Za možne rešitve glejte <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentacijo</a>."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sv.json

@ -68,6 +68,8 @@
"AccessDenied": "Åtkomst nekad!",
"AccessDeniedMessage": "Du har inte tillgång till denna resurs.",
"OrRegisterWith": "Eller registrera dig med",
"RegisterUsingYourProviderAccount": "Registrera dig med ditt {0} konto"
"RegisterUsingYourProviderAccount": "Registrera dig med ditt {0} konto",
"RequireMigrateSeedTitle": "Kunde inte hitta administratörsanvändaren",
"RequireMigrateSeedMessage": "Se till att databasens seed körs. Se <a target=\"_blank\" href=\"https://abp.io/kb/0003\">dokumentationen</a> för möjliga lösningar."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json

@ -68,6 +68,8 @@
"AccessDenied": "Erişim reddedildi!",
"AccessDeniedMessage": "Bu kaynağa erişiminiz yok.",
"OrRegisterWith": "Veya bunlarla kayıt ol:",
"RegisterUsingYourProviderAccount": "{0} hesabınızla kayıt olun."
"RegisterUsingYourProviderAccount": "{0} hesabınızla kayıt olun.",
"RequireMigrateSeedTitle": "Yönetici kullanıcısı bulunamadı",
"RequireMigrateSeedMessage": "Lütfen veritabanı tohumunun yürütüldüğünden emin olun. Olası çözümler için <a target=\"_blank\" href=\"https://abp.io/kb/0003\">belgelere</a> bakın."
}
}

4
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/vi.json

@ -68,6 +68,8 @@
"AccessDenied": "Quyền truy cập bị từ chối!",
"AccessDeniedMessage": "Bạn không có quyền truy cập vào tài nguyên này.",
"OrRegisterWith": "Hoặc đăng ký bằng:",
"RegisterUsingYourProviderAccount": "Đăng ký bằng tài khoản {0} của bạn"
"RegisterUsingYourProviderAccount": "Đăng ký bằng tài khoản {0} của bạn",
"RequireMigrateSeedTitle": "Không thể tìm thấy người dùng quản trị",
"RequireMigrateSeedMessage": "Vui lòng đảm bảo rằng seed cơ sở dữ liệu được thực thi. Xem <a target=\"_blank\" href=\"https://abp.io/kb/0003\">tài liệu</a> để biết các giải pháp có thể."
}
}

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

Loading…
Cancel
Save