Browse Source

Merge branch 'dev' into docs-pdf

pull/22430/head
liangshiwei 1 year ago
parent
commit
b9ad33165a
  1. 4
      .github/workflows/angular.yml
  2. 264
      Directory.Packages.props
  3. 29
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  4. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  5. 44
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md
  6. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png
  7. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png
  8. BIN
      docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png
  9. 170
      docs/en/Community-Articles/2025-04-20-IDX10204/POST.md
  10. BIN
      docs/en/Community-Articles/2025-04-20-IDX10204/header.png
  11. BIN
      docs/en/Community-Articles/2025-04-20-IDX10204/payload.png
  12. 262
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md
  13. BIN
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png
  14. BIN
      docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png
  15. 1
      docs/en/cli/index.md
  16. 2
      docs/en/framework/api-development/static-csharp-clients.md
  17. 2
      docs/en/framework/api-development/swagger.md
  18. 2
      docs/en/framework/architecture/modularity/basics.md
  19. 11
      docs/en/framework/architecture/modularity/extending/module-entity-extensions.md
  20. 12
      docs/en/framework/data/entity-framework-core/index.md
  21. 9
      docs/en/framework/data/entity-framework-core/postgresql.md
  22. 2
      docs/en/framework/fundamentals/caching.md
  23. 51
      docs/en/framework/fundamentals/configuration.md
  24. 2
      docs/en/framework/fundamentals/fluent-validation.md
  25. 11
      docs/en/framework/fundamentals/localization.md
  26. 20
      docs/en/framework/infrastructure/background-jobs/hangfire.md
  27. 2
      docs/en/framework/infrastructure/background-jobs/quartz.md
  28. 20
      docs/en/framework/infrastructure/background-workers/hangfire.md
  29. 2
      docs/en/framework/infrastructure/background-workers/quartz.md
  30. 2
      docs/en/framework/infrastructure/current-user.md
  31. 8
      docs/en/framework/infrastructure/image-manipulation.md
  32. 2
      docs/en/framework/infrastructure/sms-sending.md
  33. 8
      docs/en/framework/infrastructure/string-encryption.md
  34. 2
      docs/en/framework/infrastructure/text-templating/razor.md
  35. 2
      docs/en/framework/infrastructure/text-templating/scriban.md
  36. 2
      docs/en/framework/real-time/signalr.md
  37. 11
      docs/en/framework/ui/maui/index.md
  38. 5
      docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md
  39. 2
      docs/en/get-started/empty-aspnet-core-application.md
  40. BIN
      docs/en/images/samples-cms-kit.png
  41. BIN
      docs/en/images/samples-easycrm.png
  42. BIN
      docs/en/images/samples-eshoponabp.png
  43. BIN
      docs/en/images/samples-eventhub.png
  44. 14
      docs/en/modules/cms-kit-pro/url-forwarding.md
  45. 8
      docs/en/modules/docs.md
  46. 27
      docs/en/modules/gdpr.md
  47. 2
      docs/en/modules/language-management.md
  48. 2
      docs/en/modules/virtual-file-explorer.md
  49. 14
      docs/en/release-info/migration-guides/abp-8-2.md
  50. 10
      docs/en/release-info/release-notes.md
  51. 8
      docs/en/samples/index.md
  52. 2
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md
  53. 28
      docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md
  54. 4
      docs/en/tutorials/microservice/part-06.md
  55. 2
      docs/en/tutorials/modular-crm/part-03.md
  56. 2
      docs/en/tutorials/modular-crm/part-06.md
  57. 6
      docs/en/tutorials/todo/layered/index.md
  58. 8
      docs/en/tutorials/todo/single-layer/index.md
  59. 37
      docs/en/ui-themes/lepton-x/blazor.md
  60. BIN
      docs/en/ui-themes/lepton-x/images/account-layout-background-style.png
  61. BIN
      docs/en/ui-themes/lepton-x/images/leptonx-mvc-customizing-footer.png
  62. 32
      docs/en/ui-themes/lepton-x/mvc.md
  63. 3
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs
  64. 8
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs
  65. 2
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs
  66. 5
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs
  67. 15
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs
  68. 8
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs
  69. 4
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs
  70. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css
  71. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css
  72. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss
  73. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs
  74. 29
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  75. 2
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  76. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
  77. 7
      framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  78. 9
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs
  79. 12
      framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs
  80. 8
      framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor
  81. 2
      framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs
  82. 2
      framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs
  83. 3
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs
  84. 8
      framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs
  85. 2
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  86. 15
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs
  87. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
  88. 11
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs
  89. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs
  90. 25
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs
  91. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs
  92. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs
  93. 43
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  94. 47
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs
  95. 2
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs
  96. 5
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs
  97. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs
  98. 17
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs
  99. 12
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs
  100. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs

4
.github/workflows/angular.yml

@ -27,12 +27,12 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/cache@v2 - uses: actions/cache@v4
with: with:
path: 'npm/ng-packs/node_modules' path: 'npm/ng-packs/node_modules'
key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }}
- uses: actions/cache@v2 - uses: actions/cache@v4
with: with:
path: 'templates/app/angular/node_modules' path: 'templates/app/angular/node_modules'
key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }}

264
Directory.Packages.props

@ -3,190 +3,190 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.0.0" /> <PackageVersion Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.0" />
<PackageVersion Include="aliyun-net-sdk-sts" Version="3.1.2" /> <PackageVersion Include="aliyun-net-sdk-sts" Version="3.1.2" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" /> <PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageVersion Include="AsyncKeyedLock" Version="7.1.3" /> <PackageVersion Include="AsyncKeyedLock" Version="7.1.6" />
<PackageVersion Include="Autofac" Version="8.1.0" /> <PackageVersion Include="Autofac" Version="8.2.1" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" /> <PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" /> <PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
<PackageVersion Include="AutoMapper" Version="13.0.1" /> <PackageVersion Include="AutoMapper" Version="14.0.0" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" /> <PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" /> <PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="AWSSDK.S3" Version="3.7.410.9" /> <PackageVersion Include="AWSSDK.S3" Version="4.0.0" />
<PackageVersion Include="AWSSDK.SecurityToken" Version="3.7.401.16" /> <PackageVersion Include="AWSSDK.SecurityToken" Version="4.0.0" />
<PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" /> <PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.1" /> <PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.19.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.22.1" /> <PackageVersion Include="Azure.Storage.Blobs" Version="12.24.0" />
<PackageVersion Include="Blazorise" Version="1.7.3" /> <PackageVersion Include="Blazorise" Version="1.7.6" />
<PackageVersion Include="Blazorise.Components" Version="1.7.3" /> <PackageVersion Include="Blazorise.Components" Version="1.7.6" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.3" /> <PackageVersion Include="Blazorise.DataGrid" Version="1.7.6" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.3" /> <PackageVersion Include="Blazorise.Snackbar" Version="1.7.6" />
<PackageVersion Include="Castle.Core" Version="5.1.1" /> <PackageVersion Include="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" /> <PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" /> <PackageVersion Include="CommonMark.NET" Version="0.15.1" />
<PackageVersion Include="Confluent.Kafka" Version="2.5.3" /> <PackageVersion Include="Confluent.Kafka" Version="2.10.0" />
<PackageVersion Include="Dapper" Version="2.1.44" /> <PackageVersion Include="Dapper" Version="2.1.66" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.14.0" /> <PackageVersion Include="Dapr.AspNetCore" Version="1.15.4" />
<PackageVersion Include="Dapr.Client" Version="1.14.0" /> <PackageVersion Include="Dapr.Client" Version="1.15.4" />
<PackageVersion Include="DeviceDetector.NET" Version="6.3.3" /> <PackageVersion Include="DeviceDetector.NET" Version="6.4.2" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.190.9" /> <PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.235.9" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.7" /> <PackageVersion Include="DistributedLock.Core" Version="1.0.8" />
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3" /> <PackageVersion Include="DistributedLock.Redis" Version="1.0.3" />
<PackageVersion Include="DeepL.net" Version="1.10.0" /> <PackageVersion Include="DeepL.net" Version="1.15.0" />
<PackageVersion Include="EphemeralMongo.Core" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.linux-x64" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.osx-x64" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.win-x64" Version="1.1.3" />
<PackageVersion Include="itext.bouncy-castle-adapter" Version="9.1.0" /> <PackageVersion Include="itext.bouncy-castle-adapter" Version="9.1.0" />
<PackageVersion Include="itext7" Version="9.1.0" /> <PackageVersion Include="itext7" Version="9.1.0" />
<PackageVersion Include="itext7.pdfhtml" Version="6.1.0" /> <PackageVersion Include="itext7.pdfhtml" Version="6.1.0" />
<PackageVersion Include="EphemeralMongo.Core" Version="2.0.0" />
<PackageVersion Include="EphemeralMongo6.runtime.linux-x64" Version="2.0.0" />
<PackageVersion Include="EphemeralMongo6.runtime.osx-x64" Version="1.1.3" />
<PackageVersion Include="EphemeralMongo6.runtime.win-x64" Version="2.0.0" />
<PackageVersion Include="MongoSandbox.Core" Version="1.0.1" /> <PackageVersion Include="MongoSandbox.Core" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.linux-x64" Version="1.0.1" /> <PackageVersion Include="MongoSandbox6.runtime.linux-x64" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.osx-x64" Version="1.0.1" /> <PackageVersion Include="MongoSandbox6.runtime.osx-x64" Version="1.0.1" />
<PackageVersion Include="MongoSandbox6.runtime.win-x64" Version="1.0.1" /> <PackageVersion Include="MongoSandbox6.runtime.win-x64" Version="1.0.1" />
<PackageVersion Include="FluentValidation" Version="11.10.0" /> <PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.10.0" /> <PackageVersion Include="Google.Cloud.Storage.V1" Version="4.13.0" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.18" /> <PackageVersion Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.18" /> <PackageVersion Include="Hangfire.SqlServer" Version="1.8.18" />
<PackageVersion Include="HtmlSanitizer" Version="8.1.870" /> <PackageVersion Include="HtmlSanitizer" Version="9.0.884" />
<PackageVersion Include="IdentityModel" Version="7.0.0" /> <PackageVersion Include="IdentityModel" Version="7.0.0" />
<PackageVersion Include="IdentityServer4" Version="4.1.2" /> <PackageVersion Include="IdentityServer4" Version="4.1.2" />
<PackageVersion Include="IdentityServer4.AspNetIdentity" Version="4.1.2" /> <PackageVersion Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" /> <PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="LdapForNet" Version="2.7.15" /> <PackageVersion Include="LdapForNet" Version="2.7.15" />
<PackageVersion Include="LibGit2Sharp" Version="0.30.0" /> <PackageVersion Include="LibGit2Sharp" Version="0.31.0" />
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" /> <PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" />
<PackageVersion Include="MailKit" Version="4.8.0" /> <PackageVersion Include="MailKit" Version="4.12.0" />
<PackageVersion Include="Markdig.Signed" Version="0.40.0" /> <PackageVersion Include="Markdig.Signed" Version="0.41.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.30" /> <PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.60" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.30" /> <PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.60" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" /> <PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.2" /> <PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.4" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.2" /> <PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" /> <PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.2" /> <PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" /> <PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0-preview.7.24406.2" /> <PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.4" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.2" /> <PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" /> <PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" /> <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.6.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.6.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
<PackageVersion Include="Minio" Version="6.0.3" /> <PackageVersion Include="Minio" Version="6.0.4" />
<PackageVersion Include="MongoDB.Driver" Version="3.1.0" /> <PackageVersion Include="MongoDB.Driver" Version="3.3.0" />
<PackageVersion Include="NEST" Version="7.17.5" /> <PackageVersion Include="NEST" Version="7.17.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" /> <PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="NSubstitute" Version="5.1.0" /> <PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.11.1" /> <PackageVersion Include="NuGet.Versioning" Version="6.13.2" />
<PackageVersion Include="NUglify" Version="1.21.9" /> <PackageVersion Include="NUglify" Version="1.21.15" />
<PackageVersion Include="Nullable" Version="1.3.1" /> <PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Octokit" Version="13.0.1" /> <PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="6.1.1" /> <PackageVersion Include="OpenIddict.Abstractions" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Core" Version="6.1.1" /> <PackageVersion Include="OpenIddict.Core" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="6.1.1" /> <PackageVersion Include="OpenIddict.Server.AspNetCore" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="6.1.1" /> <PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="6.2.1" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="6.1.1" /> <PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="6.2.1" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.60" /> <PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.80" />
<PackageVersion Include="Polly" Version="8.4.2" /> <PackageVersion Include="Polly" Version="8.5.2" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" /> <PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" /> <PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
<PackageVersion Include="Quartz" Version="3.13.0" /> <PackageVersion Include="Quartz" Version="3.14.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.13.0" /> <PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.14.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.13.0" /> <PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.14.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.13.0" /> <PackageVersion Include="Quartz.Serialization.Json" Version="3.14.0" />
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" /> <PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
<PackageVersion Include="Rebus" Version="8.6.0" /> <PackageVersion Include="Rebus" Version="8.8.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.2.0" /> <PackageVersion Include="Rebus.ServiceProvider" Version="10.3.0" />
<PackageVersion Include="Scriban" Version="5.10.0" /> <PackageVersion Include="Scriban" Version="6.2.1" />
<PackageVersion Include="Serilog" Version="4.0.2" /> <PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.2" /> <PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" /> <PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="8.0.0" /> <PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.1" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" /> <PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Shouldly" Version="4.2.1" /> <PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" /> <PackageVersion Include="SixLabors.ImageSharp" Version="3.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.6" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" /> <PackageVersion Include="SkiaSharp" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.8" /> <PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.8" />
<PackageVersion Include="Slugify.Core" Version="4.0.1" /> <PackageVersion Include="Slugify.Core" Version="4.0.1" />
<PackageVersion Include="Spectre.Console" Version="0.49.1" /> <PackageVersion Include="Spectre.Console" Version="0.50.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" /> <PackageVersion Include="StackExchange.Redis" Version="2.8.31" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.8.1" /> <PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.2" /> <PackageVersion Include="System.Collections.Immutable" Version="9.0.4" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" /> <PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.1" /> <PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.2" />
<PackageVersion Include="System.Linq.Queryable" Version="4.3.0" /> <PackageVersion Include="System.Linq.Queryable" Version="4.3.0" />
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" /> <PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.2" /> <PackageVersion Include="System.Security.Permissions" Version="9.0.4" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" /> <PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" /> <PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.2" /> <PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" />
<PackageVersion Include="System.Text.Json" Version="9.0.2" /> <PackageVersion Include="System.Text.Json" Version="9.0.4" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.0" /> <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1142" /> <PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1231" />
<PackageVersion Include="TimeZoneConverter" Version="7.0.0" /> <PackageVersion Include="TimeZoneConverter" Version="7.0.0" />
<PackageVersion Include="Unidecode.NET" Version="2.1.0" /> <PackageVersion Include="Unidecode.NET" Version="2.1.0" />
<PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.2" /> <PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" /> <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" /> <PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" /> <PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.8.2" /> <PackageVersion Include="Fody" Version="6.9.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

29
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -690,6 +690,33 @@
"Permission:Campaigns": "Campaigns", "Permission:Campaigns": "Campaigns",
"Permission:Licenses": "License Settings", "Permission:Licenses": "License Settings",
"BlockUserPolicy": "User Block Policies", "BlockUserPolicy": "User Block Policies",
"Permission:BlockUserPolicy": "User Block Policy" "Permission:BlockUserPolicy": "User Block Policy",
"ManageImages" : "Manage Image",
"ModuleMainImage": "Module Main Image",
"ModuleImageInfo1": "Accepted file types : <strong> JPEG, JPG, PNG, SVG </strong>",
"ModuleImageInfo2": "Max file size : <strong> 1 MB </strong>",
"ModuleImageInfo3": "Image Proportion : <strong> 1:1 </strong>",
"ModuleImageInfo4": "<a href='/images/example-module-img.png' download> Download a sample cover image </a>",
"AreYouSureToDeleteImage": "Are you sure you want to delete this image?",
"CompactPercentage": "Compact Percentage",
"CompactPercentageDescription": "Percentage of cache entries to remove during compaction (1-100)",
"Settings": "Settings",
"MaxSizeOfInMemoryCache": "Max Size Of In Memory Cache",
"MaxSizeOfInMemoryCacheInfo": "Gets or sets the maximum size of the memory cache.",
"SizeOfTriggerAutoCompact": "Size Of Trigger Auto Compact",
"SizeOfTriggerAutoCompactInfo": "Gets or sets the size threshold that triggers auto-compaction. When the cache size exceeds this value, auto-compaction will be performed",
"AutoCompactPercentage": "Auto Compact Percentage",
"AutoCompactPercentageInfo": "Gets or sets the percentage of items to remove when auto-compaction is triggered (1-100)",
"ClearCaches": "This clears all NuGet content cache",
"CacheLatestVersions": "Cache Latest Versions",
"VersionCount": "Version Count",
"VersionCountInfo": "Gets or sets the number of versions to cache",
"IncludeLeptonX": "Include LeptonX",
"IncludeLeptonXInfo": "Gets or sets whether to include LeptonX in the cache",
"IncludePrerelease": "Include Prerelease",
"IncludePrereleaseInfo": "Gets or sets whether to include prerelease versions in the cache",
"CacheVersionCount": "Cache Version Count",
"CacheVersionCountInfo": "Gets or sets the number of versions to cache",
"CacheLatestVersionsInfo": "This caches the latest versions of the NuGet packages"
} }
} }

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

@ -1888,7 +1888,7 @@
"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).", "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>.", "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>.",
"BiographyContainsUrlValidationMessage": "Biography cannot contain URL.", "BiographyContainsUrlValidationMessage": "Biography cannot contain URL.",
"CreatePostSEOTitleInfo": "The SEO Title is what appears in search engine results as the clickable headline. By default, it matches the article’s title, but you can customize it here for better search visibility. Keep it clear, compelling, and include target keywords—ideally near the beginning. Aim for around 60 characters to avoid truncation in search results.", "CreatePostSEOTitleInfo": "SEO URL is a clean, readable, keyword-rich URL that helps both users and search engines understand what this post is about. Keep it short with 60 characters. SEO titles over 60 characters will be truncated. Use hyphens (-) to separate words (not underscores). Include target keywords near the start. Lowercase only. No stop words unless needed (e.g: \"and\", \"or\", \"the\").",
"SEOTitle": "SEO Title" "SEOTitle": "SEO Title"
} }
} }

44
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md

@ -25,6 +25,7 @@ Here is a brief list of titles explained in the next sections:
* Docs Module: Added "Alternative Words" to Filter Items * Docs Module: Added "Alternative Words" to Filter Items
* Introducing the Bunny BLOB Storage Provider * Introducing the Bunny BLOB Storage Provider
* Upgraded `MongoDB.Driver` to v3.1.0 * Upgraded `MongoDB.Driver` to v3.1.0
* Using Timezone Settings to Display Datetime
* Identity Pro Module: Require Email Verification to Register * Identity Pro Module: Require Email Verification to Register
* Switching users during OAuth login * Switching users during OAuth login
@ -114,6 +115,49 @@ The upgrade provides:
We have prepared a [migration guide](https://abp.io/docs/9.2/release-info/migration-guides/MongoDB-Driver-2-to-3) for this upgrade. Please refer to it to learn more about the changes and how to migrate your application. We have prepared a [migration guide](https://abp.io/docs/9.2/release-info/migration-guides/MongoDB-Driver-2-to-3) for this upgrade. Please refer to it to learn more about the changes and how to migrate your application.
### Using Timezone Settings to Display Datetime
A significant enhancement in ABP v9.2 is the ability to use timezone settings to display `DateTime` values according to the user's or application's configured timezone. Introduced in `v9.2.0-rc.2`, this feature addresses the common challenge of ensuring users see accurate time information, regardless of their geographical location.
Previously, `DateTime` values were often shown in the server's timezone or in UTC, which could cause confusion for users in different timezones. With this new feature, ABP applications can now respect the configured timezone and automatically convert and display datetime values accordingly.
**Before setting the timezone:**
Consider a scenario where you have a list of books, and the `CreationTime` property is displayed without any timezone consideration. It may appear in the server's default timezone:
![before.png](before.png)
*(Screenshot of a Books page showing the CreationTime in a default timezone.)*
**Setting the timezone:**
To set the timezone, start by configuring the `AbpClockOptions` in your module's `ConfigureServices` method:
```csharp
Configure<AbpClockOptions>(options =>
{
options.Kind = DateTimeKind.Utc;
});
```
> By setting the `Kind` property of `AbpClockOptions` to `DateTimeKind.Utc`, ABP will normalize all datetime values. Times stored in the database and returned to the frontend will be in UTC. Additionally, the `SupportsMultipleTimezone` property of the `IClock` service will be **true**, and you’ll be able to configure the timezone from the UI under the _Settings_ page.
After setting the `Kind` property, you can run your application and configure the timezone from the UI under the _Settings_ page. For example, set the timezone to "Asia/Tokyo (+09:00)":
![configure-timezone.png](configure-timezone.png)
*(Screenshot showing the configuration setting for Abp.Timing.TimeZone, set to "Asia/Tokyo (+09:00)".)*
> ABP provides the `Abp.Timing.TimeZone` setting, which allows you to configure the desired timezone at the application, tenant, or user level and this setting can be configured in the UI under the _Settings_ page, inside of the _Time Zone_ tab.
**After setting the timezone:**
Once the timezone is configured, ABP automatically handles the conversion and display of `DateTime` values. The `CreationTime` on the _Books_ page will now be shown in the **"Asia/Tokyo"** timezone.
![after.png](after.png)
*(Screenshot of the _Books_ page after setting the timezone, showing the `CreationTime` adjusted to the "Asia/Tokyo" timezone.)*
This feature utilizes the `IClock` service, which provides methods like `ConvertToUserTime` and `ConvertToUtc` to facilitate timezone conversions. By configuring the `Abp.Timing.TimeZone` setting, developers can ensure a consistent and user-friendly experience across applications with a global user base.
> For a more detailed guide on implementing and using this feature, refer to the article: [Developing a Multi-Timezone Application Using the ABP Framework](https://abp.io/community/articles/developing-a-multitimezone-application-using-the-abp-framework-zk7fnrdq). It offers step-by-step instructions and examples for handling multi-timezone scenarios in ABP applications.
### Identity Pro Module: Require Email Verification to Register ### Identity Pro Module: Require Email Verification to Register
[ABP Identity Pro module](https://abp.io/docs/9.2/modules/identity-pro) has been enhanced with a new feature that allows administrators to require email verification during the registration process. This security improvement ensures that users must verify their email addresses before their registration is considered complete. Enabling this feature is especially important for applications that want to prevent spam registrations. [ABP Identity Pro module](https://abp.io/docs/9.2/modules/identity-pro) has been enhanced with a new feature that allows administrators to require email verification during the registration process. This security improvement ensures that users must verify their email addresses before their registration is considered complete. Enabling this feature is especially important for applications that want to prevent spam registrations.

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

170
docs/en/Community-Articles/2025-04-20-IDX10204/POST.md

@ -0,0 +1,170 @@
# Common Errors in JWT Bearer Authentication
When implementing JWT Bearer authentication in an ABP(tiered) application, you might occasionally encounter errors starting with `IDX`. These errors are related to JWT Bearer Token validation and this article will help you understand and resolve them.
## Enable JWT Bearer authentication
Your API project usually contains the following code, which enables JWT Bearer authentication and makes it as the default authentication scheme.
We simply configure the JWT's `Authority` and `Audience` properties, and it will work fine.
```csharp
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:44301/"; //configuration["AuthServer:Authority"];
options.Audience = "MyProjectName";
});
```
> `AddJwtBearer` and `AddAbpJwtBearer` will do the same thing, but `AddAbpJwtBearer` is recommended.
## JWT authentication process
Let's take a look at how the above code works.
A JWT Token usually consists of three parts: `Header`, `Payload`, and `Signature`.
- `Header`: Contains the type and signing algorithm of the token
- `Payload`: Contains the claims of the token, including `sub`, `aud`, `exp`, `iat`, `iss`, `jti`, `preferred_username`, `given_name`, `role`, `email`, etc.
- `Signature`: The cryptographic signature of the token used to verify its authenticity
Here is an example of a JWT Token issued by `AuthServer(OpenIddict)`:
The `Header` part:
![JWT Header](header.png)
The `Payload` part:
![JWT Payload](payload.png)
### TokenValidationParameters
In the `JwtBearerOptions`, there is a `TokenValidationParameters` property, which is used to validate the JWT Token.
The default implementation for JWT Token validation is `JsonWebTokenHandler`, which comes from the [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/) package.
We didn't set the `TokenValidationParameters` property in the code above, so the default values below will be used:
```csharp
//...
TokenValidationParameters.ValidateAudience = true
TokenValidationParameters.ValidAudience = "MyProjectName"
TokenValidationParameters.ValidAudiences = null
TokenValidationParameters.ValidateIssuer = true
TokenValidationParameters.ValidIssuer = null
TokenValidationParameters.ValidIssuers = null
//...
```
### JWT Bearer Token Validation Process
During JWT Bearer authentication, API website will get the token from the HTTP request and validate it.
The `JsonWebTokenHandler` will get the `OpenID Connect` metadata from the `AuthServer`, it will be used in the validation process, the current metadata request address is: https://localhost:44301/.well-known/openid-configuration , it is a fixed address calculated from the `Authority` property.
First, the token's Signature is verified using the public key obtained from `OpenID Connect` metadata(https://localhost:44301/.well-known/jwks).
Then, the payload is validated. The payload is a JSON object containing essential information such as the `token type`, `expiration time`, `issuer`, and `audience` etc.
Most of the validation problems we may encounter are payload validation failures, for example:
#### Lifetime
If the token in your request has expired, the validation will fail. You will see the exception information like `IDX10230` in the log.
#### Audience
The `ValidAudience` of `TokenValidationParameters` is `MyProjectName`, the `aud` in the payload of the token is also `MyProjectName`, if the token does not contain `aud` or the `aud` does not match, the validation will fail. You may see the exception information like `IDX10206`, `IDX10277` or `IDX10208`.
> If the `ValidateAudience` of `TokenValidationParameters` is `false`, then the `aud` will not be validated.
#### Issuer
The default value of `TokenValidationParameters.ValidateIssuer` is `true`, it requires the token's payload to contain the `issuer` field, and it must match one of `TokenValidationParameters.ValidIssuer` or `TokenValidationParameters.ValidIssuers`.
> The default value of `ValidIssuer` or `ValidIssuers` is `null`, it will use the `issuer` from the `OpenID Connect` metadata as the default value.
1. If the token's payload does not contain the `issuer` field, you may see the error `IDX10211`.
2. If the API website cannot get the `OpenID Connect` metadata from AuthServer website, the validation will fail. You may see the error `IDX10204`, the full exception message is: `IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.`
3. If the `issuer` does not match, the validation will fail. You may see the error `IDX10205` in the log.
> If the `ValidateIssuer` of `TokenValidationParameters` is `false`, then the `issuer` will not be validated.
> Please note that `OpenIddict` will use the current HTTP request information as the value of `issuer`. If the AuthServer website is deployed behind a reverse proxy or similar deployment configurations, the `issuer` in the token may not be the value you expect. In this case, please specify it manually.
```csharp
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
serverBuilder.SetIssuer("https://localhost:44301/");
});
```
## Troubleshooting
To troubleshoot any `IDX` errors during JWT authentication, you can enable detailed logging by configuring the `identitymodel` logs as follows:
```csharp
using System.Diagnostics.Tracing;
using Microsoft.IdentityModel.Logging;
public class Program
{
public async static Task<int> Main(string[] args)
{
IdentityModelEventSource.ShowPII = true;
IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose;
var wilsonTextLogger = newTextWriterEventListener("Logs/identitymodel.txt");
wilsonTextLogger.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Verbose);
//...
}
}
```
Additionally, you can enable `OpenIddict`'s `Verbose` logs for more detailed debugging information:
```csharp
var loggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.MinimumLevel.Override("OpenIddict", LogEventLevel.Verbose)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
```
## Summary
For JWT authentication, you need to pay attention to the following key points:
1. Ensure your API website can communicate with the AuthServer properly
2. Verify that the `aud` claim in your token matches the expected audience
3. Confirm that the `issuer` claim in your token is valid and matches the configuration
You can customize the `JwtBearerOptions`'s `TokenValidationParameters` to modify the validation rules to meet your actual needs.
For example, if your `issuer` needs to support multiple subdomains, you can use the [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator) library to customize the validation.
```csharp
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://abp.io";
options.Audience = "abp_io";
options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
options.TokenValidationParameters.ValidIssuers = new[]
{
"https://{0}.abp.io"
};
});
```
## References
- [Configure JWT bearer authentication in ASP.NET Core]([https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-auth?view=aspnetcore-8.0](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication))
- [OpenIddict](https://github.com/openiddict/openiddict-core)
- [IdentityModel](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet)
- [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator)

BIN
docs/en/Community-Articles/2025-04-20-IDX10204/header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/Community-Articles/2025-04-20-IDX10204/payload.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

262
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md

@ -0,0 +1,262 @@
# Using Microsoft AI Extensions Library and OpenAI to Summarize User Comments
Either you are building an e-commerce application or a simple blog, **user comments** (about your products or blog posts) **can grow rapidly**, making it harder for users to get the gist of discussions at a glance. AI is a pretty good tool to solve the problem. By using AI, you can **summarize all the user comments** and show a single paragraph to your users, so they can easily understand the overall thought of users about the product or the blog post.
In this tutorial, we’ll walk through a real-life implementation of using AI to summarize multiple user comments in an application. I will implement the solution based on ABP's **[CMS Kit](https://abp.io/docs/latest/modules/cms-kit)** library, as it already features a **[commenting system](https://abp.io/docs/latest/modules/cms-kit/comments)** and a [demo application](https://cms-kit-demo.abpdemo.com/) that displays user comments on **[gallery images](https://cms-kit-demo.abpdemo.com/image-gallery)** (it has not a comment summary feature yet, we will implement it in this tutorial).
## A Screenshot
Here, an example screenshot from the application with the comment summary feature:
![comment-example](comment-example.png)
## Cloning the Repository
If you want to follow the development, you can clone the [CMS Kit Demo repository](https://github.com/abpframework/cms-kit-demo) to your computer and make it running by following the instructions on the [README file](https://github.com/abpframework/cms-kit-demo?tab=readme-ov-file#cms-kit-demo).
I suggest to you to play a little with [the application](https://cms-kit-demo.abpdemo.com/) (create a new user for yourself, add some comments to the images in the gallery), so you understand how it works.
## Preparing the Solution for AI
I will use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to use the AI features. It is an abstraction library that can work with multiple AI models and tools. I will use an OpenAI model in the demo.
The first step is to add the [Microsoft.Extensions.AI.OpenAI](http://nuget.org/packages/Microsoft.Extensions.AI.OpenAI) NuGet package to the project:
````bash
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease
````
>The Microsoft AI Extensions Library was in preview at the time when I wrote this article. If it has a stable release now, you can remove the `--prerelease` parameter for the preceding command.
We will store the OpenAI key and model name in user secrets. So, locate the root path of the CMS Kit project (`src\CmsKitDemo` folder) and execute the following commands in order in a command-line terminal:
````bash
dotnet user-secrets init
dotnet user-secrets set OpenAIKey <your-openai-key>
dotnet user-secrets set ModelName <your-openai-model-name>
````
For this example, you need to have an [OpenAI API Key](https://platform.openai.com/). That's all. Now, we are ready to use the AI.
## Implementing the AI Summarization
Let's start from the most important point of this article: Comment summarization. I will create a class named `AiCommentSummarizer` to implement the summarization work. Here, the full content of that class:
````csharp
using System.Text;
using Microsoft.Extensions.AI;
using OpenAI;
using Volo.Abp.DependencyInjection;
namespace CmsKitDemo.Utils;
public class AiCommentSummarizer : ITransientDependency
{
private readonly IConfiguration _configuration;
public AiCommentSummarizer(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<string> SummarizeAsync(string[] commentTexts)
{
// Get the model and key from the configuration
var aiModel = _configuration["ModelName"];
var apiKey = _configuration["OpenAIKey"];
if (aiModel.IsNullOrEmpty() || apiKey.IsNullOrEmpty())
{
return "";
}
// Create the IChatClient
var client = new OpenAIClient(apiKey)
.GetChatClient(aiModel)
.AsIChatClient();
// Create a prompt (input for AI)
var promptBuilder = new StringBuilder();
promptBuilder.AppendLine(
@"There are comments from different users of our website about an image.
We want to summarize the comments into a single comment.
Return a single comment with a maximum of 512 characters. Comments are separated by a newline character and given below."
);
promptBuilder.AppendLine();
foreach (var commentText in commentTexts)
{
promptBuilder.AppendLine("User comment:");
promptBuilder.AppendLine(commentText);
promptBuilder.AppendLine();
}
// Submit the prompt and get the response
var response = await client.GetResponseAsync(
promptBuilder.ToString(),
new ChatOptions { MaxOutputTokens = 1024 }
);
return response.Text;
}
}
````
That class is pretty simple and already decorated with comments:
* First, we are getting the API Key and an OpenAI model name from user secrets. I used `gpt-4.1` as the model name, but you can use another available model.
* Then we are obtaining an `IChatClient` reference for OpenAI. `IChatClient` interface is an abstraction that is provided by the [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) library, so we can implement rest of the code independently from OpenAI.
* Then we continue by building a proper prompt (input) for the AI operation.
* And finally we are using the AI to generate a response (the summary).
At this point, all the AI-related work has already been done. The rest of this article explains how to integrate that summarization feature with the [CMS Kit Demo application](https://cms-kit-demo.abpdemo.com/).
## Adding a CommentsSummary Property to the GalleryImage Entity
The `GalleryImage` entity is used to represent an image on [the image gallery](https://cms-kit-demo.abpdemo.com/image-gallery). I add a `CommentsSummary` property to that entity:
````csharp
public class GalleryImage : CreationAuditedAggregateRoot<Guid>
{
public string Description { get; set; }
public Guid CoverImageMediaId { get; set; }
public string CommentsSummary { get; set; } // The new property is here
//...
}
````
Since the CMS Kit Demo application uses Entity Framework Core, I need to add a new database schema migration and update the database:
````bash
dotnet ef migrations add Added_Summary_To_GalleryImage
dotnet ef database update
````
## Updating the Summary
Great, we have a `GalleryImage.CommentsSummary` property now. But, how will it be updated when a users adds or removes a comment for an image? To implement that;
* We will listen all the change events for user comments (when a user adds, removes or updates a comment).
* Whenever a comment is changed, we will find the related gallery image, retrieve all the user comments for this image, use the `AiCommentSummarizer` class to summarize all the comments.
* Finally, we wil set the `GalleryImage.CommentsSummary` property with the generated summary text.
Here, the implementation:
````csharp
using CmsKitDemo.Entities;
using CmsKitDemo.Utils;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;
using Volo.CmsKit.Comments;
namespace CmsKitDemo.EventHandlers;
public class GalleryImageCommentListener :
ILocalEventHandler<EntityChangedEventData<Comment>>,
ITransientDependency
{
private readonly IRepository<GalleryImage, Guid> _galleryImageRepository;
private readonly IRepository<Comment, Guid> _commentRepository;
private readonly AiCommentSummarizer _aiCommentSummarizer;
public GalleryImageCommentListener(
IRepository<GalleryImage, Guid> galleryImageRepository,
IRepository<Comment, Guid> commentRepository,
AiCommentSummarizer aiCommentSummarizer)
{
_galleryImageRepository = galleryImageRepository;
_commentRepository = commentRepository;
_aiCommentSummarizer = aiCommentSummarizer;
}
public async Task HandleEventAsync(EntityChangedEventData<Comment> eventData)
{
var comment = eventData.Entity;
//Here, we only interest in comments related to image gallery items
if (comment.EntityType != CmsKitDemoConsts.ImageGalleryEntityType)
{
return;
}
if (!Guid.TryParse(comment.EntityId, out var galleryImageId))
{
return;
}
// Get the related image from database
var galleryImage = await _galleryImageRepository.FindAsync(galleryImageId);
if (galleryImage == null)
{
return;
}
// Get all the comments related to the image
var queryable = await _commentRepository.GetQueryableAsync();
var allCommentTexts = await queryable
.Where(c => c.EntityType == CmsKitDemoConsts.ImageGalleryEntityType &&
c.EntityId == comment.EntityId)
.Select(c => c.Text)
.ToArrayAsync();
// Update the summary of comments related to the image
if (allCommentTexts.Length <= 0)
{
galleryImage.CommentsSummary = "";
}
else
{
galleryImage.CommentsSummary =
await _aiCommentSummarizer.SummarizeAsync(allCommentTexts);
}
// Update the image in database
await _galleryImageRepository.UpdateAsync(galleryImage);
}
}
````
Let's explain that class:
* `GalleryImageCommentListener` implements the `ILocalEventHandler<EntityChangedEventData<Comment>>` interface. In this way, it can handle an event whenever a `Comment` [entity](https://abp.io/docs/latest/framework/architecture/domain-driven-design/entities) is changed (created, updated or deleted). We are using ABP's [local event bus](https://abp.io/docs/latest/framework/infrastructure/event-bus/local) and its [pre-defined events](https://abp.io/docs/latest/framework/infrastructure/event-bus/local#pre-built-events).
* `HandleEventAsync` is called by the ABP Framework whenever a new `Comment` is created, or an existing `Comment` is deleted or updated.
* ABP's `Comment` entity is reusable and it can be associated with any kind of objects (blog posts, images, etc). So, first we are checking if this comment is related to an image gallery item.
* Then we are getting the related `GalleryImage` entity from the database.
* And getting all comments (including the new one) from the database for this image.
* Finally, using the `AiCommentSummarizer` class to generate the summary and set the `CommentsSummary` property.
## Show the Summary Card on the UI
Everything is ready on the backend. Now, we can show the summary text on the user interface. To do, that, I added `CommentsSummary` property also to the `GalleryImageDto` class and used it on the `/Pages/Gallery/Detail.cshtml` view:
````csharp
@if (!Model.Image.CommentsSummary.IsNullOrEmpty())
{
<div class="card mt-3">
<div class="card-body">
<h6 class="card-title">Summary of the User Comments</h6>
<p class="mb-auto">@Model.Image.CommentsSummary</p>
</div>
</div>
}
````
That section renders the following card on the user interface:
![summary-card](summary-card.png)
## Conclusion
In this article, I demonstrated how to use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to work with OpenAI for summarization of multiple user comments. I reused the [ABP's CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo) to show it in a more real world example.
## Source Code
* [Source code of the CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo)
* [All the changes made for this article (as a pull request)](https://github.com/abpframework/cms-kit-demo/pull/18)

BIN
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

1
docs/en/cli/index.md

@ -342,6 +342,7 @@ Note that this command can upgrade your solution from a previous version, and al
* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. * `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default.
* `--check-all`: Check the new version of each package separately. Default is `false`. * `--check-all`: Check the new version of each package separately. Default is `false`.
* `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used. * `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used.
* * `--leptonx-version` or `-lv`: Specifies the LeptonX version to use for update. If not specified, latest version or the version that is compatible with `--version` argument is used.
### clean ### clean

2
docs/en/framework/api-development/static-csharp-clients.md

@ -47,7 +47,7 @@ Implement this class in your service application. You can use [auto API controll
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project: First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project:
```` ````
Install-Package Volo.Abp.Http.Client dotnet add package Volo.Abp.Http.Client
```` ````
Then add `AbpHttpClientModule` dependency to your module: Then add `AbpHttpClientModule` dependency to your module:

2
docs/en/framework/api-development/swagger.md

@ -29,7 +29,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project: 1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project:
`Install-Package Volo.Abp.Swashbuckle` `dotnet add package Volo.Abp.Swashbuckle`
2. Add the `AbpSwashbuckleModule` to the dependency list of your module: 2. Add the `AbpSwashbuckleModule` to the dependency list of your module:

2
docs/en/framework/architecture/modularity/basics.md

@ -1,7 +1,5 @@
# Modularity # Modularity
## Introduction
ABP was designed to support to build fully modular applications and systems where every module may have entities, services, database integration, APIs, UI components and so on; ABP was designed to support to build fully modular applications and systems where every module may have entities, services, database integration, APIs, UI components and so on;
* This document introduces the basics of the module system. * This document introduces the basics of the module system.

11
docs/en/framework/architecture/modularity/extending/module-entity-extensions.md

@ -151,6 +151,17 @@ property =>
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option. > Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option.
### DataTypeAttribute
`DataTypeAttribute` is used to specify the type of the property. It is used to determine how to render the property on the user interface:
```csharp
property =>
{
property.Attributes.Add(new DataTypeAttribute(DataType.Date));
}
```
### Validation ### Validation
Entity extension system allows you to define validation for extension properties in a few ways. Entity extension system allows you to define validation for extension properties in a few ways.

12
docs/en/framework/data/entity-framework-core/index.md

@ -755,7 +755,17 @@ public static class QADbContextModelCreatingExtensions
> The `Object Extension` feature need the `Change Tracking`, which means you can't use the read-only repositories for the entities that have `extension properties(MapEfCoreProperty)`, Please see the [Repositories documentation](../../architecture/domain-driven-design/repositories.md) to learn the change tracking behavior. > The `Object Extension` feature need the `Change Tracking`, which means you can't use the read-only repositories for the entities that have `extension properties(MapEfCoreProperty)`, Please see the [Repositories documentation](../../architecture/domain-driven-design/repositories.md) to learn the change tracking behavior.
See the "*ConfigureByConvention Method*" section above for more information. See the **ConfigureByConvention Method** section above for more information.
### Accessing Extra Properties(Shadow Properties)
Extra properties stored in separate fields in the database are known as **Shadow Properties**. These properties are not defined in the entity class, but are part of the EF Core model and can be referenced in LINQ queries using the EF.Property static method
```csharp
var query = (await GetQueryableAsync()).Where(x => EF.Property<string>(x, "Title") == "MyTitle");
```
See the [EF Core Shadow and Indexer Properties document](https://learn.microsoft.com/en-us/ef/core/modeling/shadow-properties) for more information.
## Advanced Topics ## Advanced Topics

9
docs/en/framework/data/entity-framework-core/postgresql.md

@ -21,6 +21,15 @@ Find `UseSqlServer()` call in *YourProjectName*DbContextFactory.cs inside the `.
> Depending on your solution structure, you may find more `UseSqlServer()` calls that needs to be changed. > Depending on your solution structure, you may find more `UseSqlServer()` calls that needs to be changed.
## EnableLegacyTimestampBehavior
Please enable the `Npgsql.EnableLegacyTimestampBehavior` in `PreConfigureServices` method of your EF Core module and `DbContextFactory(IDesignTimeDbContextFactory)` class.
`AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);`
See https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
## Change the Connection Strings ## Change the Connection Strings
PostgreSql connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com](https://www.connectionstrings.com/postgresql/) for details of PostgreSql connection string options. PostgreSql connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com](https://www.connectionstrings.com/postgresql/) for details of PostgreSql connection string options.

2
docs/en/framework/fundamentals/caching.md

@ -270,7 +270,7 @@ Distributed cache service provides an interesting feature. Assume that you've up
### IDistributedCacheSerializer ### IDistributedCacheSerializer
`IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTC8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache. `IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTF8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache.
You can [replace](./dependency-injection.md) this service with your own implementation if you want to implement your own serialization logic. You can [replace](./dependency-injection.md) this service with your own implementation if you want to implement your own serialization logic.

51
docs/en/framework/fundamentals/configuration.md

@ -1,3 +1,52 @@
# Configuration # Configuration
ASP.NET Core has an flexible and extensible key-value based configuration system. In fact, the configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system. ASP.NET Core has an flexible and extensible key-value based configuration system. The configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application.
See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system.
## Getting the Configuration
You may need to get the `IConfiguration` service in various places in your codebase. The following section shows two common ways.
### In Module Classes
You typically need to get configuration while initializing your application. You can get the `IConfiguration` service using the `ServiceConfigurationContext.Configuration` property inside your [module class](../architecture/modularity/basics.md) as the following example:
````csharp
public class MyAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var connectionString = context.Configuration["ConnectionStrings:Default"];
}
}
````
`context.Configuration` is a shortcut property for the `context.Services.GetConfiguration()` method. In general, prefer using `context.Configuration` for simplicity and readability when working within module classes. Use `context.Services.GetConfiguration()` in other contexts where you have an `IServiceCollection` object but do not have access to the `context.Configuration` property. (`IServiceCollection.GetConfiguration` is an extension method that can be used whenever you have an `IServiceCollection` object).
### In Your Services
You can directly [inject](dependency-injection.md) the `IConfiguration` service into your services:
````csharp
public class MyService : ITransientDependency
{
private readonly IConfiguration _configuration;
public MyService(IConfiguration configuration)
{
_configuration = configuration;
}
public string? GetConnectionString()
{
return _configuration["ConnectionStrings:Default"];
}
}
````
## See Also
* [Microsoft's Configuration Documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)
* [The Options Pattern](options.md)

2
docs/en/framework/fundamentals/fluent-validation.md

@ -21,7 +21,7 @@ If you want to manually install;
1. Add the [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package to your project: 1. Add the [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package to your project:
```` ````
Install-Package Volo.Abp.FluentValidation dotnet add package Volo.Abp.FluentValidation
```` ````
2. Add the `AbpFluentValidationModule` to the dependency list of your module: 2. Add the `AbpFluentValidationModule` to the dependency list of your module:

11
docs/en/framework/fundamentals/localization.md

@ -248,6 +248,17 @@ namespace MyProject
The `L` property is also available for some other base classes like `AbpController` and `AbpPageModel`. The `L` property is also available for some other base classes like `AbpController` and `AbpPageModel`.
## Supported Languages
You can configure the `AbpLocalizationOptions`'s `Languages` property to add the languages supported by the application. The template already sets common languages, but you can add new languages as shown below:
```csharp
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("uz", "uz", "Uzbek"));
});
```
## The Client Side ## The Client Side
See the following documents to learn how to reuse the same localization texts in the JavaScript side; See the following documents to learn how to reuse the same localization texts in the JavaScript side;

20
docs/en/framework/infrastructure/background-jobs/hangfire.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundJobs.HangFire](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.HangFire) NuGet package to your project: 1. Add the [Volo.Abp.BackgroundJobs.HangFire](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.HangFire) NuGet package to your project:
```` ````
Install-Package Volo.Abp.BackgroundJobs.HangFire dotnet add package Volo.Abp.BackgroundJobs.HangFire
```` ````
2. Add the `AbpBackgroundJobsHangfireModule` to the dependency list of your module: 2. Add the `AbpBackgroundJobsHangfireModule` to the dependency list of your module:
@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec
} }
```` ````
### AbpHangfireOptions
You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server.
````csharp
Configure<AbpHangfireOptions>(options =>
{
// If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance.
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = ["default", "alpha"],
//... other properties
};
});
````
> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server.
### Specifying Queue ### Specifying Queue
You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue: You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue:

2
docs/en/framework/infrastructure/background-jobs/quartz.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundJobs.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.Quartz) NuGet package to your project: 1. Add the [Volo.Abp.BackgroundJobs.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.Quartz) NuGet package to your project:
```` ````
Install-Package Volo.Abp.BackgroundJobs.Quartz dotnet add package Volo.Abp.BackgroundJobs.Quartz
```` ````
2. Add the `AbpBackgroundJobsQuartzModule` to the dependency list of your module: 2. Add the `AbpBackgroundJobsQuartzModule` to the dependency list of your module:

20
docs/en/framework/infrastructure/background-workers/hangfire.md

@ -23,7 +23,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundWorkers.Hangfire](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Hangfire) NuGet package to your project: 1. Add the [Volo.Abp.BackgroundWorkers.Hangfire](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Hangfire) NuGet package to your project:
```` ````
Install-Package Volo.Abp.BackgroundWorkers.Hangfire dotnet add package Volo.Abp.BackgroundWorkers.Hangfire
```` ````
2. Add the `AbpBackgroundWorkersHangfireModule` to the dependency list of your module: 2. Add the `AbpBackgroundWorkersHangfireModule` to the dependency list of your module:
@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec
} }
```` ````
### AbpHangfireOptions
You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server.
````csharp
Configure<AbpHangfireOptions>(options =>
{
// If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance.
options.ServerOptions = new BackgroundJobServerOptions
{
Queues = ["default", "alpha"],
//... other properties
};
});
````
> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server.
## Create a Background Worker ## Create a Background Worker
`HangfireBackgroundWorkerBase` is an easy way to create a background worker. `HangfireBackgroundWorkerBase` is an easy way to create a background worker.

2
docs/en/framework/infrastructure/background-workers/quartz.md

@ -21,7 +21,7 @@ If you want to manually install;
1. Add the [Volo.Abp.BackgroundWorkers.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Quartz) NuGet package to your project: 1. Add the [Volo.Abp.BackgroundWorkers.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Quartz) NuGet package to your project:
```` ````
Install-Package Volo.Abp.BackgroundWorkers.Quartz dotnet add package Volo.Abp.BackgroundWorkers.Quartz
```` ````
2. Add the `AbpBackgroundWorkersQuartzModule` to the dependency list of your module: 2. Add the `AbpBackgroundWorkersQuartzModule` to the dependency list of your module:

2
docs/en/framework/infrastructure/current-user.md

@ -138,7 +138,7 @@ public class MyAppService : ApplicationService
{ {
new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()), new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()),
new Claim(AbpClaimTypes.UserName, "john"), new Claim(AbpClaimTypes.UserName, "john"),
new Claim("MyCustomCliam", "42") new Claim("Claim", "42")
} }
) )
); );

8
docs/en/framework/infrastructure/image-manipulation.md

@ -22,7 +22,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.Abstractions](https://www.nuget.org/packages/Volo.Abp.Imaging.Abstractions) NuGet package to your project: 1. Add the [Volo.Abp.Imaging.Abstractions](https://www.nuget.org/packages/Volo.Abp.Imaging.Abstractions) NuGet package to your project:
``` ```
Install-Package Volo.Abp.Imaging.Abstractions dotnet add package Volo.Abp.Imaging.Abstractions
``` ```
2. Add the `AbpImagingAbstractionsModule` to the dependency list of your module: 2. Add the `AbpImagingAbstractionsModule` to the dependency list of your module:
@ -240,7 +240,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project: 1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project:
``` ```
Install-Package Volo.Abp.Imaging.MagickNet dotnet add package Volo.Abp.Imaging.MagickNet
``` ```
2. Add `AbpImagingMagickNetModule` to your [module](../architecture/modularity/basics.md)'s dependency list: 2. Add `AbpImagingMagickNetModule` to your [module](../architecture/modularity/basics.md)'s dependency list:
@ -284,7 +284,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project: 1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project:
``` ```
Install-Package Volo.Abp.Imaging.ImageSharp dotnet add package Volo.Abp.Imaging.ImageSharp
``` ```
2. Add `AbpImagingImageSharpModule` to your [module](../architecture/modularity/basics.md)'s dependency list: 2. Add `AbpImagingImageSharpModule` to your [module](../architecture/modularity/basics.md)'s dependency list:
@ -350,7 +350,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project: 1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project:
``` ```
Install-Package Volo.Abp.Imaging.AspNetCore dotnet add package Volo.Abp.Imaging.AspNetCore
``` ```
2. Add `AbpImagingAspNetCoreModule` to your [module](../architecture/modularity/basics.md)'s dependency list: 2. Add `AbpImagingAspNetCoreModule` to your [module](../architecture/modularity/basics.md)'s dependency list:

2
docs/en/framework/infrastructure/sms-sending.md

@ -26,7 +26,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Sms](https://www.nuget.org/packages/Volo.Abp.Sms) NuGet package to your project: 1. Add the [Volo.Abp.Sms](https://www.nuget.org/packages/Volo.Abp.Sms) NuGet package to your project:
``` ```
Install-Package Volo.Abp.Sms dotnet add package Volo.Abp.Sms
``` ```
2. Add the `AbpSmsModule` to the dependency list of your module: 2. Add the `AbpSmsModule` to the dependency list of your module:

8
docs/en/framework/infrastructure/string-encryption.md

@ -22,7 +22,7 @@ If you want to manually install;
1. Add the [Volo.Abp.Security](https://www.nuget.org/packages/Volo.Abp.Security) NuGet package to your project: 1. Add the [Volo.Abp.Security](https://www.nuget.org/packages/Volo.Abp.Security) NuGet package to your project:
`Install-Package Volo.Abp.Security` `dotnet add package Volo.Abp.Security`
2. Add the `AbpSecurityModule` to the dependency list of your module: 2. Add the `AbpSecurityModule` to the dependency list of your module:
@ -52,11 +52,11 @@ All encryption operations are included in `IStringEncryptionService`. You can in
public string Encrypt(string value) public string Encrypt(string value)
{ {
// To enrcypt a value // To encrypt a value
return StringEncryptionService.Encrypt(value); return StringEncryptionService.Encrypt(value);
} }
public string Decrpyt(string value) public string Decrypt(string value)
{ {
// To decrypt a value // To decrypt a value
return StringEncryptionService.Decrypt(value); return StringEncryptionService.Decrypt(value);
@ -122,4 +122,4 @@ Configure<AbpStringEncryptionOptions>(opts =>
- **Keysize:** This constant is used to determine the keysize of the encryption algorithm. - **Keysize:** This constant is used to determine the keysize of the encryption algorithm.
Default value: `256` Default value: `256`

2
docs/en/framework/infrastructure/text-templating/razor.md

@ -25,7 +25,7 @@ If you want to manually install;
1. Add the [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet package to your project: 1. Add the [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet package to your project:
```` ````
Install-Package Volo.Abp.TextTemplating.Razor dotnet add package Volo.Abp.TextTemplating.Razor
```` ````
2. Add the `AbpTextTemplatingRazorModule` to the dependency list of your module: 2. Add the `AbpTextTemplatingRazorModule` to the dependency list of your module:

2
docs/en/framework/infrastructure/text-templating/scriban.md

@ -19,7 +19,7 @@ If you want to manually install;
1. Add the [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet package to your project: 1. Add the [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet package to your project:
```` ````
Install-Package Volo.Abp.TextTemplating.Scriban dotnet add package Volo.Abp.TextTemplating.Scriban
```` ````
2. Add the `AbpTextTemplatingScribanModule` to the dependency list of your module: 2. Add the `AbpTextTemplatingScribanModule` to the dependency list of your module:

2
docs/en/framework/real-time/signalr.md

@ -27,7 +27,7 @@ If you want to manually install;
1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: 1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project:
``` ```
Install-Package Volo.Abp.AspNetCore.SignalR dotnet add package Volo.Abp.AspNetCore.SignalR
``` ```
Or use the Visual Studio NuGet package management UI to install it. Or use the Visual Studio NuGet package management UI to install it.

11
docs/en/framework/ui/maui/index.md

@ -37,10 +37,15 @@ Open a command line terminal and run the `adb reverse` command to expose a port
`adb reverse tcp:44305 tcp:44305` `adb reverse tcp:44305 tcp:44305`
> You should replace "44305" with the real port. > Replace `44305` with the port number your backend application is running on.
> You should run the command after starting the emulator. >
> Run this command **after** the Android emulator has started.
> If you don't have a separate installation of Android Debug Bridge, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation. > [!IMPORTANT]
> If your project uses a **tiered** or **microservice** architecture, ensure that both the **auth server** and all **remote service ports** are properly proxied using the `adb reverse` command. You can find all the required remote service ports and AuthServer configurations in your `YourProjectName.Maui/appsettings.json` file.
> [!NOTE]
> If you don't have a separate installation of **Android Debug Bridge** _(adb)_, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation.
### iOS ### iOS

5
docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md

@ -89,7 +89,10 @@ This example overrides the **login page** defined by the [Account Module](../../
Create a page model class deriving from the ` LoginModel ` (defined in the ` Volo.Abp.Account.Web.Pages.Account ` namespace): Create a page model class deriving from the ` LoginModel ` (defined in the ` Volo.Abp.Account.Web.Pages.Account ` namespace):
> If you are using the `AbpAccountWebOpenIddictModule` or `AbpAccountPublicWebOpenIddictModule`, the base class is `OpenIddictSupportedLoginModel` instead of `LoginModel`. And you should change the `ExposeServices` attribute to `[ExposeServices(typeof (MyLoginModel), typeof(OpenIddictSupportedLoginModel), typeof(LoginModel))]`
````csharp ````csharp
[ExposeServices(typeof (MyLoginModel), typeof(LoginModel))]
public class MyLoginModel : LoginModel public class MyLoginModel : LoginModel
{ {
public MyLoginModel( public MyLoginModel(
@ -114,8 +117,6 @@ public class MyLoginModel : LoginModel
You can override any method or add new properties/methods if needed. You can override any method or add new properties/methods if needed.
> Notice that we didn't use `[Dependency(ReplaceServices = true)]` or `[ExposeServices(typeof(LoginModel))]` since we don't want to replace the existing class in the dependency injection, we define a new one.
Copy `Login.cshtml` file into your solution as just described above. Change the **@model** directive to point to the `MyLoginModel`: Copy `Login.cshtml` file into your solution as just described above. Change the **@model** directive to point to the `MyLoginModel`:
````xml ````xml

2
docs/en/get-started/empty-aspnet-core-application.md

@ -104,7 +104,7 @@ Replacing ASP.NET Core's DI system by Autofac and integrating to ABP is pretty e
1. Install [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) package 1. Install [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) package
```` ````
Install-Package Volo.Abp.Autofac dotnet add package Volo.Abp.Autofac
```` ````
2. Add the ``AbpAutofacModule`` Dependency 2. Add the ``AbpAutofacModule`` Dependency

BIN
docs/en/images/samples-cms-kit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

BIN
docs/en/images/samples-easycrm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

BIN
docs/en/images/samples-eshoponabp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
docs/en/images/samples-eventhub.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

14
docs/en/modules/cms-kit-pro/url-forwarding.md

@ -37,6 +37,20 @@ You can create new forwardings or update/delete existing ones, in the admin side
![url-forwarding-page](../../images/url-forwarding-page.png) ![url-forwarding-page](../../images/url-forwarding-page.png)
## Options
### ShortenedUrlCacheOptions
`ShortenedUrlCacheOptions` is used to configure the cache settings for the URL forwarding system. Example:
```csharp
Configure<ShortenedUrlCacheOptions>(options =>
{
options.CacheAllOnStartup = true; // Cache all shortened URLs on startup
});
```
# Internals # Internals
## Domain Layer ## Domain Layer

8
docs/en/modules/docs.md

@ -73,25 +73,25 @@ Or you can also manually install nuget package to each project:
* Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project. * Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project.
```bash ```bash
Install-Package Volo.Docs.Domain dotnet add package Volo.Docs.Domain
``` ```
* Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project. * Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project.
```bash ```bash
Install-Package Volo.Docs.EntityFrameworkCore dotnet add package Volo.Docs.EntityFrameworkCore
``` ```
* Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project. * Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project.
```bash ```bash
Install-Package Volo.Docs.Application dotnet add package Volo.Docs.Application
``` ```
* Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project. * Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project.
```bash ```bash
Install-Package Volo.Docs.Web dotnet add package Volo.Docs.Web
``` ```
##### 3.2.1- Adding Module Dependencies ##### 3.2.1- Adding Module Dependencies

27
docs/en/modules/gdpr.md

@ -260,9 +260,11 @@ If you want to override the texts in the Cookie Consent component, you just need
> Refer to the [Localization documentation](../framework/fundamentals/localization.md) for more info about defining localization resources and overriding existing localization entries that comes from pre-built modules. > Refer to the [Localization documentation](../framework/fundamentals/localization.md) for more info about defining localization resources and overriding existing localization entries that comes from pre-built modules.
### Configuring the Cookie Consent ### Configuring Cookie Consent
You can add Cookie Consent to your application by configuring the `AddAbpCookieConsent` in your module class as below: To enable cookie consent in your application, follow these two steps:
**1. Configure the service in your module class (inside the `ConfigureServices` method):**
```csharp ```csharp
context.Services.AddAbpCookieConsent(options => context.Services.AddAbpCookieConsent(options =>
@ -273,4 +275,23 @@ context.Services.AddAbpCookieConsent(options =>
}); });
``` ```
After configuring the `AddAbpCookieConsent` and setting it enabled, a cookie consent text will be prepared according to the options and a cookie consent banner will be seen at the bottom of the page. Thus, the users of the application will be informed about the Cookie Policy and Privacy Policy of the company/application. **2. Add the middleware (`UseAbpCookieConsent`) to the request pipeline (in the `OnApplicationInitialization` method):**
```diff
public override void OnApplicationInitialization(ApplicationInitializationContenxt context)
{
var app = context.GetApplicationBuilder();
//...
+ app.UseAbpCookieConsent();
app.UseCorrelationId();
app.UseRouting();
app.MapAbpStaticAssets();
app.UseAbpSecurityHeaders();
app.UseAuthentication();
//...
}
```
Once configured, a cookie consent banner will be shown at the bottom of the page. It includes links to your _Cookie Policy_ and _Privacy Policy_, helping inform users and support GDPR compliance.

2
docs/en/modules/language-management.md

@ -62,7 +62,7 @@ This module adds some initial data (see [the data seed system](../framework/infr
* Creates language records configured using `AbpLocalizationOptions`. * Creates language records configured using `AbpLocalizationOptions`.
If you want to change the seeded language list, see the next section. If you want to change the seeded language list, see the [Localization](../framework/fundamentals/localization.md#Supported-Languages) document.
## Internals ## Internals

2
docs/en/modules/virtual-file-explorer.md

@ -24,7 +24,7 @@ Or you can also manually install nuget package to `Acme.MyProject.Web` project:
* Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project. * Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project.
`Install-Package Volo.Abp.VirtualFileExplorer.Web` `dotnet add package Volo.Abp.VirtualFileExplorer.Web`
##### 2.1- Adding Module Dependencies ##### 2.1- Adding Module Dependencies

14
docs/en/release-info/migration-guides/abp-8-2.md

@ -28,6 +28,7 @@ With this version on, ABP Framework allows you to use single blog mode, without
* `Volo.Blogging.Pages.Members` -> `Volo.Blogging.Pages.Blogs.Members` (members folder) * `Volo.Blogging.Pages.Members` -> `Volo.Blogging.Pages.Blogs.Members` (members folder)
> If you haven't overridden the pages above, then you don't need to make any additional changes. See [#19418](https://github.com/abpframework/abp/pull/19418) for more information. > If you haven't overridden the pages above, then you don't need to make any additional changes. See [#19418](https://github.com/abpframework/abp/pull/19418) for more information.
## Removed `FlagIcon` property from the `ILanguageInfo` ## Removed `FlagIcon` property from the `ILanguageInfo`
The `FlagIcon` property has been removed from the `ILanguageInfo` interface since we removed the flag icon library in the earlier versions from all of our themes and none of them using it now. The `FlagIcon` property has been removed from the `ILanguageInfo` interface since we removed the flag icon library in the earlier versions from all of our themes and none of them using it now.
@ -57,11 +58,20 @@ In this version, the Angular UI has been updated to use the Angular version 17.3
The **Session Management** feature allows you to prevent concurrent login and manage user sessions. The **Session Management** feature allows you to prevent concurrent login and manage user sessions.
In this version, a new entity called `IdentitySession` has been added to the framework and you should create a new migration and apply it to your database. In this version, a new entity called `IdentitySession` has been added to the framework and you need to add new `DbSet<IdentitySession>` to your `DbContext` class if it implements `IIdentityDbContext` interface.
```csharp
public class YourDbContext : AbpDbContext<YourDbContext>, IIdentityDbContext
{
public DbSet<IdentitySession> Sessions { get; set; }
}
```
You should also create a new migration and apply it to your database.
## Upgraded NuGet Dependencies ## Upgraded NuGet Dependencies
You can see the following list of NuGet libraries that have been upgraded with this release, if you are using one of these packages explicitly, you may consider upgrading them in your solution: You can see the following list of NuGet libraries that have been upgraded with this release, **if you are using one of these packages explicitly**, you may consider upgrading them in your solution, especially **Microsoft.IdentityModel.*** packages:
| Package | Old Version | New Version | | Package | Old Version | New Version |
| ---------------------------------------------------------- | ----------- | ----------- | | ---------------------------------------------------------- | ----------- | ----------- |

10
docs/en/release-info/release-notes.md

@ -1,8 +1,12 @@
# Release Notes # Release Notes
This document contains **brief release notes** for each release. Release notes only include **major features** and **visible enhancements**. They don't include all the development done in the related version. To see raw and detailed change logs for every release, please check <a href="https://github.com/abpframework/abp/milestones?state=closed" target="_blank">the related milestone</a> and [the change logs page](https://abp.io/pro-releases) (only for paid license holders). This document contains **brief release notes** for each release. Release notes only include **major features** and **visible enhancements**. They don't include all the development done in the related version.
Also see the following notes about ABP releases:
* [ABP Studio release notes](../studio/release-notes.md)
* [Change logs for ABP pro packages](https://abp.io/pro-releases)
> If you want to read the release notes for each ABP Studio release, check it out from [here](../studio/release-notes.md).
## 9.2 (2025-03-25) ## 9.2 (2025-03-25)
@ -15,7 +19,7 @@ This is currently a RC (release-candidate) and you can see the detailed **[blog
* Identity Pro Module: Require Email Verification to Register * Identity Pro Module: Require Email Verification to Register
* Switching users during OAuth login * Switching users during OAuth login
## 9.1 (2025-01-16) ## 9.1 (2025-03-05)
See the detailed **[blog post / announcement](https://abp.io/community/articles/abp.io-platform-9.1-final-has-been-released-h96a56qa)** for the v9.1 release. See the detailed **[blog post / announcement](https://abp.io/community/articles/abp.io-platform-9.1-final-has-been-released-h96a56qa)** for the v9.1 release.

8
docs/en/samples/index.md

@ -9,6 +9,8 @@ A reference application built with ABP. It implements the Domain Driven Design w
* [Live demo](https://www.openeventhub.com/) * [Live demo](https://www.openeventhub.com/)
* [Source code](https://github.com/abpframework/eventhub) * [Source code](https://github.com/abpframework/eventhub)
![samples-event-hub](../images/samples-eventhub.png)
## eShopOnAbp ## eShopOnAbp
> ⚠️ **Important Notice** > ⚠️ **Important Notice**
@ -18,6 +20,8 @@ Reference microservice solution built with ABP and .NET.
* [Source code](https://github.com/abpframework/eShopOnAbp) * [Source code](https://github.com/abpframework/eShopOnAbp)
![eshoponabp](../images/samples-eshoponabp.png)
## CMS Kit Demo ## CMS Kit Demo
A minimal example website built with the [CMS Kit module](../modules/cms-kit/index.md). A minimal example website built with the [CMS Kit module](../modules/cms-kit/index.md).
@ -25,6 +29,8 @@ A minimal example website built with the [CMS Kit module](../modules/cms-kit/ind
* [Live demo](https://cms-kit-demo.abpdemo.com/) * [Live demo](https://cms-kit-demo.abpdemo.com/)
* [Source code](https://github.com/abpframework/cms-kit-demo) * [Source code](https://github.com/abpframework/cms-kit-demo)
![samples-cms-kit](../images/samples-cms-kit.png)
## Easy CRM ## Easy CRM
A middle-size CRM application built with ABP. A middle-size CRM application built with ABP.
@ -32,6 +38,8 @@ A middle-size CRM application built with ABP.
* [Live demo](http://easycrm.abp.io/) * [Live demo](http://easycrm.abp.io/)
* [Click here](easy-crm.md) to see the details and download the source code. * [Click here](easy-crm.md) to see the details and download the source code.
![samples-easy-crm](../images/samples-easycrm.png)
## Book Store ## Book Store
A simple CRUD application to show basic principles of developing an application with ABP. The same sample was implemented with different technologies and different modules: A simple CRUD application to show basic principles of developing an application with ABP. The same sample was implemented with different technologies and different modules:

2
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md

@ -480,7 +480,7 @@ push:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

28
docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md

@ -283,6 +283,34 @@ See:
- https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications#resolve-http-405-errors - https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications#resolve-http-405-errors
- https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/iis/site-behavior-performance/http-error-405-website#resolution-for-cause-3 - https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/iis/site-behavior-performance/http-error-405-website#resolution-for-cause-3
## Publish the Application(s) as IIS sub-application
If your MVC application is a sub-application, you need to set the `BaseUrl` property of `AbpThemingOptions` to the sub-application’s path. The `BaseUrl` is used to configure the `base` element in the `head` section of the layout page.
```csharp
public void ConfigureServices(IServiceCollection services)
{
Configure<AbpThemingOptions>(options =>
{
options.BaseUrl = "/myapp/";
});
}
```
```html
<html>
<head>
<base href="/myapp/" />
...
</head>
<body>
...
</body>
</html>
```
For Blazor applications, you can to set the `base` tag in the `App.razor` file instead of configure `AbpThemingOptions`.
## How to get stdout-log ## How to get stdout-log
If your application is running on IIS and getting errors like `502.5, 500.3x`, you can enable stdout logs to see the error details. If your application is running on IIS and getting errors like `502.5, 500.3x`, you can enable stdout logs to see the error details.

4
docs/en/tutorials/microservice/part-06.md

@ -107,9 +107,9 @@ public class ProductIntegrationService : ApplicationService, IProductIntegration
Now that we have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class, we can consume this service from the Ordering service. Now that we have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class, we can consume this service from the Ordering service.
### Adding a Reference to the `CloudCrm.CatalogService.Contracts` Package ### Adding a Reference to the `CloudCrm.OrderingService` Package
First, we need to add a reference to the `CloudCrm.CatalogService.Contracts` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command: First, we need to add a reference to the `CloudCrm.OrderingService` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command:
![add-package-reference-ordering-service](images/add-package-reference-ordering-service.png) ![add-package-reference-ordering-service](images/add-package-reference-ordering-service.png)

2
docs/en/tutorials/modular-crm/part-03.md

@ -436,7 +436,7 @@ Open the `ModularCrm.Products` .NET solution in your IDE, and find the `Pages/Pr
![visual-studio-products-cshtml](images/visual-studio-products-cshtml.png) ![visual-studio-products-cshtml](images/visual-studio-products-cshtml.png)
Replace the `IndexModel.cshtml.cs` file with the following content: Replace the `Index.cshtml.cs` file with the following content:
````csharp ````csharp
using System.Collections.Generic; using System.Collections.Generic;

2
docs/en/tutorials/modular-crm/part-06.md

@ -256,7 +256,7 @@ Let's see what we've changed:
* In the last line, we are converting the product list to a dictionary, where the key is `Guid Id` and the value is `string Name`. That way, we can easily find a product's name with its ID. * In the last line, we are converting the product list to a dictionary, where the key is `Guid Id` and the value is `string Name`. That way, we can easily find a product's name with its ID.
* Finally, we are mapping the orders to `OrderDto` objects and setting the product name by looking up the product ID in the dictionary. * Finally, we are mapping the orders to `OrderDto` objects and setting the product name by looking up the product ID in the dictionary.
Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Model.ProductName` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following: Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Order.ProductName` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following:
````html ````html
@page @page

6
docs/en/tutorials/todo/layered/index.md

@ -528,7 +528,7 @@ The interesting part here is how we communicate with the server. See the *Dynami
### Index.css ### Index.css
As the final touch, Create a file named `Index.css` in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: As the final touch, Create a file named `Index.css` in the `Pages` folder of the *TodoApp.Web* project and add the following content:
```css ```css
#TodoList{ #TodoList{
@ -664,7 +664,7 @@ Open the `Index.razor` file in the `Pages` folder of the {{if UI=="Blazor" || UI
### Index.razor.css ### Index.razor.css
As the final touch, open the `Index.razor.css` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}}*TodoApp.Blazor.Client*{{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and replace it with the following content: As the final touch, open the `Index.razor.css` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}}*TodoApp.Blazor.Client*{{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and add the following content:
```css ```css
#TodoList{ #TodoList{
@ -827,7 +827,7 @@ Open the `/angular/src/app/home/home.component.html` file and replace its conten
### home.component.scss ### home.component.scss
As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: As the final touch, open the `/angular/src/app/home/home.component.scss` file and add the following code block:
```css ```css
#TodoList{ #TodoList{

8
docs/en/tutorials/todo/single-layer/index.md

@ -309,10 +309,8 @@ public interface ITodoAppService : IApplicationService
Create a `TodoAppService` class under the `Services` folder of {{if UI=="Blazor"}}your `TodoApp.Host` project{{else}}your project{{end}}, as shown below: Create a `TodoAppService` class under the `Services` folder of {{if UI=="Blazor"}}your `TodoApp.Host` project{{else}}your project{{end}}, as shown below:
```csharp ```csharp
using TodoApp.Services;
using TodoApp.Services.Dtos; using TodoApp.Services.Dtos;
using TodoApp.Entities; using TodoApp.Entities;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
namespace TodoApp.Services; namespace TodoApp.Services;
@ -517,7 +515,7 @@ The interesting part here is how we communicate with the server. See the *Dynami
### Index.cshtml.css ### Index.cshtml.css
As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and replace with the following content: As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and add the following code block at the end of the file:
````css ````css
#TodoList{ #TodoList{
@ -655,7 +653,7 @@ Open the `Index.razor` file in the `Pages` folder and replace the content with t
### Index.razor.css ### Index.razor.css
As the final touch, open the `Index.razor.css` file in the `Pages` folder and replace it with the following content: As the final touch, open the `Index.razor.css` file in the `Pages` folder and add the following code block at the end of the file:
````css ````css
#TodoList{ #TodoList{
@ -801,7 +799,7 @@ Open the `/angular/src/app/home/home.component.html` file and replace its conten
### home.component.scss ### home.component.scss
As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: As the final touch, open the `/angular/src/app/home/home.component.scss` file and add the following code block at the end of the file:
````css ````css
#TodoList{ #TodoList{

37
docs/en/ui-themes/lepton-x/blazor.md

@ -347,8 +347,43 @@ namespace LeptonX.DemoApp.Blazor.MyComponents
> Don't forget to remove repeated attributes from the razor page! > Don't forget to remove repeated attributes from the razor page!
--- #### Customizing the Footer Section
You can add a new `LeptonXFooter.razor` file and override the `Footer` component as follows:
```cs
@using Volo.Abp.DependencyInjection
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu
@inherits Footer
@attribute [ExposeServices(typeof(Footer))]
@attribute [Dependency(ReplaceServices = true)]
<div class="lpx-footbar-container">
<div class="lpx-footbar">
<div class="lpx-footbar-copyright">
<span>@DateTime.UtcNow.Year©</span>
<a href="https://leptontheme.com/" target="_blank">Lepton ThemeXXX</a>
<span>by</span>
<a href="https://volosoft.com/" target="_blank">Volosoft</a>
</div>
<div class="lpx-footbar-solo-links">
<a href="#">About</a>
<a href="#">Privacy</a>
<a href="#">Contact</a>
</div>
</div>
</div>
```
If you are using **Top Menu Layout**, then you can change the namespace as follows:
```diff
- @using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu
+ @using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu
```
> You can use the same approach customize the other components always.
## Common Components ## Common Components

BIN
docs/en/ui-themes/lepton-x/images/account-layout-background-style.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
docs/en/ui-themes/lepton-x/images/leptonx-mvc-customizing-footer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

32
docs/en/ui-themes/lepton-x/mvc.md

@ -47,7 +47,8 @@ Before starting to customize the theme, you can consider downloading the source
--- ---
### Appearance ### Appearance
You can set a default theme, add or remove appearance styles by using **LeptonXThemeOptions**.
You can set a default theme, add or remove appearance styles and layout background styles by using **LeptonXThemeOptions**.
- `DefaultStyle`: Defines the default fallback theme. The default value is **Dim** - `DefaultStyle`: Defines the default fallback theme. The default value is **Dim**
@ -133,15 +134,26 @@ Layout options of the MVC Razor Pages UI can be manageable by using **LeptonXThe
- `MobileMenuSelector`: Defines items to be displayed at the mobile menu. The default value is the first 2 items from the main menu items. - `MobileMenuSelector`: Defines items to be displayed at the mobile menu. The default value is the first 2 items from the main menu items.
```csharp
Configure<LeptonXThemeMvcOptions>(options =>
{
options.MobileMenuSelector = items => items.Where(x => x.MenuItem.Name == "MyProjectName.Home" || x.MenuItem.Name == "MyProjectName.Dashboard");
});
```
![leptonx-mobile-menu-preview](images/mobile-menu-preview.png) ![leptonx-mobile-menu-preview](images/mobile-menu-preview.png)
- `AccountLayoutBackgroundStyle`: Defines the background style of the account layout.
```csharp ```csharp
Configure<LeptonXThemeMvcOptions>(options => Configure<LeptonXThemeMvcOptions>(options =>
{ {
options.MobileMenuSelector = items => items.Where(x => x.MenuItem.Name == "MyProjectName.Home" || x.MenuItem.Name == "MyProjectName.Dashboard"); options.AccountLayoutBackgroundStyle = "background-image: url('/images/login-background-image.svg') !important;";
}); });
``` ```
![leptonx-account-layout-background-style](images/account-layout-background-style.png)
### Layouts ### Layouts
**LeptonX** offers two **ready-made layouts** for your web application. One of them is **placed** with the **menu items** on the **top** and the other with the **menu items** on the **sides**. **LeptonX** offers two **ready-made layouts** for your web application. One of them is **placed** with the **menu items** on the **top** and the other with the **menu items** on the **sides**.
@ -162,6 +174,22 @@ Layout options of the MVC Razor Pages UI can be manageable by using **LeptonXThe
> - **_Sidebar.cshtml** > - **_Sidebar.cshtml**
> - **_Toolbar.cshtml** > - **_Toolbar.cshtml**
#### Customizing the Footer Section
Add the **_Footer.cshtml** file under the **Themes/LeptonX/Layouts/Application** folder to customize the footer:
```html
<div class="text-center py-2 lpx-footer">
<span class="copyright-text">
@DateTime.Now.Year © AbpSolution1<br />
</span>
</div>
````
![Customizing Footer](images/leptonx-mvc-customizing-footer.png)
> You can customize the **_Sidebar.cshtml** and **_Toolbar.cshtml** files as well.
### Account Layout ### Account Layout
![Account layout](images/leptonx-account-layout-new.png) ![Account layout](images/leptonx-account-layout-new.png)

3
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs

@ -111,8 +111,7 @@ public partial class PageHeader : ComponentBase, IDisposable
public void Dispose() public void Dispose()
{ {
PageLayout.ToolbarItems.Clear(); PageLayout.Reset();
PageLayout.BreadcrumbItems.Clear();
ToolbarItemRenders.Clear(); ToolbarItemRenders.Clear();
} }
} }

8
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs

@ -41,4 +41,12 @@ public class PageLayout : IScopedDependency, INotifyPropertyChanged
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
public void Reset()
{
Title = string.Empty;
MenuItemName = string.Empty;
BreadcrumbItems.Clear();
ToolbarItems.Clear();
}
} }

2
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs

@ -29,6 +29,6 @@ public class DefaultThemeManager : IThemeManager, IScopedDependency, IServicePro
} }
_currentTheme = (ITheme)ServiceProvider.GetRequiredService(ThemeSelector.GetCurrentThemeInfo().ThemeType); _currentTheme = (ITheme)ServiceProvider.GetRequiredService(ThemeSelector.GetCurrentThemeInfo().ThemeType);
return CurrentTheme; return _currentTheme;
} }
} }

5
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs

@ -1,3 +1,4 @@
using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
@ -19,10 +20,14 @@ public static class AbpBlazorWebAppServiceCollectionExtensions
return services; return services;
} }
[Obsolete("Use AddBlazorWebAppServices instead. See https://github.com/abpframework/abp/issues/22622")]
public static IServiceCollection AddBlazorWebAppTieredServices([NotNull] this IServiceCollection services) public static IServiceCollection AddBlazorWebAppTieredServices([NotNull] this IServiceCollection services)
{ {
Check.NotNull(services, nameof(services)); Check.NotNull(services, nameof(services));
// Compatibility with old template code
services.AddTransient<AddBlazorWebAppTieredServicesHasBeenCalled>();
services.AddScoped<AuthenticationStateProvider, RemoteAuthenticationStateProvider>(); services.AddScoped<AuthenticationStateProvider, RemoteAuthenticationStateProvider>();
services.Replace(ServiceDescriptor.Singleton<IAbpAccessTokenProvider, PersistentComponentStateAbpAccessTokenProvider>()); services.Replace(ServiceDescriptor.Singleton<IAbpAccessTokenProvider, PersistentComponentStateAbpAccessTokenProvider>());

15
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs

@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp; namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp;
@ -8,21 +10,26 @@ public class RemoteAuthenticationStateProvider : AuthenticationStateProvider
{ {
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; } protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; }
protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; } protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; }
protected IServiceProvider ServiceProvider { get; }
public RemoteAuthenticationStateProvider( public RemoteAuthenticationStateProvider(
ICurrentPrincipalAccessor currentPrincipalAccessor, ICurrentPrincipalAccessor currentPrincipalAccessor,
WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient) WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient,
IServiceProvider serviceProvider)
{ {
CurrentPrincipalAccessor = currentPrincipalAccessor; CurrentPrincipalAccessor = currentPrincipalAccessor;
WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient; WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
ServiceProvider = serviceProvider;
} }
public async override Task<AuthenticationState> GetAuthenticationStateAsync() public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{ {
if (CurrentPrincipalAccessor.Principal.Identity == null || if (ServiceProvider.GetService<AddBlazorWebAppTieredServicesHasBeenCalled>() != null)
!CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated)
{ {
await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync(); if (CurrentPrincipalAccessor.Principal.Identity == null || !CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated)
{
await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync();
}
} }
return new AuthenticationState(CurrentPrincipalAccessor.Principal); return new AuthenticationState(CurrentPrincipalAccessor.Principal);

8
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp;
/// <summary>
/// This class is used to indicate that the AddBlazorWebAppTieredServices method has been called for compatibility with the old template code
/// </summary>
internal sealed class AddBlazorWebAppTieredServicesHasBeenCalled
{
}

4
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs

@ -73,8 +73,6 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio
await JSRuntime.InvokeVoidAsync("abp.utils.removeOidcUser"); await JSRuntime.InvokeVoidAsync("abp.utils.removeOidcUser");
} }
ApplicationConfigurationChangedService.NotifyChanged();
CurrentTenantAccessor.Current = new BasicTenantInfo( CurrentTenantAccessor.Current = new BasicTenantInfo(
configurationDto.CurrentTenant.Id, configurationDto.CurrentTenant.Id,
configurationDto.CurrentTenant.Name configurationDto.CurrentTenant.Name
@ -88,6 +86,8 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio
await JSRuntime.InvokeAsync<string>("abp.clock.setBrowserTimeZoneToCookie"); await JSRuntime.InvokeAsync<string>("abp.clock.setBrowserTimeZoneToCookie");
} }
ApplicationConfigurationChangedService.NotifyChanged();
} }
public virtual Task<ApplicationConfigurationDto> GetAsync() public virtual Task<ApplicationConfigurationDto> GetAsync()

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css

@ -10,3 +10,8 @@
.dataTable tbody tr td div.dropdown ul.dropdown-menu li { .dataTable tbody tr td div.dropdown ul.dropdown-menu li {
cursor: pointer; } cursor: pointer; }
.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{
visibility: hidden;
pointer-events: none;
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css

@ -1 +1 @@
.dataTable{width:100% !important;border-spacing:0 !important;}.table td,.table th{padding:8px 10px;}.dataTable tbody tr td button{cursor:pointer;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer;} .dataTable{width:100%!important;border-spacing:0!important}.table td,.table th{padding:8px 10px}.dataTable tbody tr td button{cursor:pointer}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer}.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{visibility:hidden;pointer-events:none}

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss

@ -27,3 +27,10 @@
} }
} }
} }
.abp-action-button {
ui.dropdown-menu[data-popper-reference-hidden] {
visibility: hidden;
pointer-events: none;
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs

@ -6,6 +6,12 @@ public class AbpThemingOptions
public string? DefaultThemeName { get; set; } public string? DefaultThemeName { get; set; }
/// <summary>
/// If set, the <c>base</c> element will be added to the <c>head</c> element of the page.
/// eg: <base href="/BaseUrl/" />
/// </summary>
public string? BaseUrl { get; set; }
public AbpThemingOptions() public AbpThemingOptions()
{ {
Themes = new ThemeDictionary(); Themes = new ThemeDictionary();

29
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -313,17 +313,42 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{ {
var timeZone = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); var timeZone = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone);
string? timeZoneId = null;
string? timeZoneName = null;
if (!timeZone.IsNullOrWhiteSpace())
{
try
{
if (_timezoneProvider.GetIanaTimezones().Any(x => x.Value == timeZone))
{
timeZoneId = _timezoneProvider.IanaToWindows(timeZone);
timeZoneName = timeZone;
}
else if (_timezoneProvider.GetWindowsTimezones().Any(x => x.Value == timeZone))
{
timeZoneId = timeZone;
timeZoneName = _timezoneProvider.WindowsToIana(timeZone);
}
}
catch (Exception ex)
{
timeZoneId = null;
timeZoneName = null;
Logger.LogWarning(ex, $"Exception occurred while getting timezone({timeZone}) information");
}
}
return new TimingDto return new TimingDto
{ {
TimeZone = new TimeZone TimeZone = new TimeZone
{ {
Windows = new WindowsTimeZone Windows = new WindowsTimeZone
{ {
TimeZoneId = timeZone.IsNullOrWhiteSpace() ? null : _timezoneProvider.IanaToWindows(timeZone) TimeZoneId = timeZoneId
}, },
Iana = new IanaTimeZone Iana = new IanaTimeZone
{ {
TimeZoneName = timeZone TimeZoneName = timeZoneName
} }
} }
}; };

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

@ -115,7 +115,7 @@ public static class AbpApplicationBuilderExtensions
return app.UseMiddleware<AbpExceptionHandlingMiddleware>(); return app.UseMiddleware<AbpExceptionHandlingMiddleware>();
} }
[Obsolete("Replace with AbpClaimsTransformation")] [Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")]
public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app) public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app)
{ {
return app.UseMiddleware<AbpClaimsMapMiddleware>(); return app.UseMiddleware<AbpClaimsMapMiddleware>();

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs

@ -11,7 +11,7 @@ using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims;
[Obsolete("Replace with AbpClaimsTransformation")] [Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")]
public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency
{ {
public async override Task InvokeAsync(HttpContext context, RequestDelegate next) public async override Task InvokeAsync(HttpContext context, RequestDelegate next)

7
framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs

@ -38,7 +38,7 @@ public static class AbpRegistrationBuilderExtensions
} }
registrationBuilder = registrationBuilder.EnablePropertyInjection(moduleContainer, implementationType); registrationBuilder = registrationBuilder.EnablePropertyInjection(moduleContainer, implementationType);
registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType); registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType, serviceDescriptor.ServiceKey);
return registrationBuilder; return registrationBuilder;
} }
@ -69,10 +69,11 @@ public static class AbpRegistrationBuilderExtensions
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder, this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceRegistrationActionList registrationActionList, ServiceRegistrationActionList registrationActionList,
Type serviceType, Type serviceType,
Type implementationType) Type implementationType,
object? serviceKey = null)
where TActivatorData : ReflectionActivatorData where TActivatorData : ReflectionActivatorData
{ {
var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType); var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType, serviceKey);
foreach (var registrationAction in registrationActionList) foreach (var registrationAction in registrationActionList)
{ {

9
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs

@ -25,13 +25,8 @@ public class AbpBackgroundJobsHangfireModule : AbpModule
if (!options.IsJobExecutionEnabled) if (!options.IsJobExecutionEnabled)
{ {
var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value; var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer; context.ServiceProvider.GetRequiredService<JobStorage>();
hangfireOptions.BackgroundJobServerFactory = _ => null;
} }
} }
private BackgroundJobServer? CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider)
{
serviceProvider.GetRequiredService<JobStorage>();
return null;
}
} }

12
framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs

@ -1,5 +1,4 @@
using System; using Hangfire;
using Hangfire;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Hangfire; using Volo.Abp.Hangfire;
@ -23,17 +22,12 @@ public class AbpBackgroundWorkersHangfireModule : AbpModule
if (!options.IsEnabled) if (!options.IsEnabled)
{ {
var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value; var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer; context.ServiceProvider.GetRequiredService<JobStorage>();
hangfireOptions.BackgroundJobServerFactory = _ => null;
} }
context.ServiceProvider context.ServiceProvider
.GetRequiredService<HangfireBackgroundWorkerManager>() .GetRequiredService<HangfireBackgroundWorkerManager>()
.Initialize(); .Initialize();
} }
private BackgroundJobServer? CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider)
{
serviceProvider.GetRequiredService<JobStorage>();
return null;
}
} }

8
framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor

@ -5,16 +5,12 @@
{ {
<Heading Size="HeadingSize.Is4" Class="alert-heading"> <Heading Size="HeadingSize.Is4" Class="alert-heading">
@alert.AlertMessage.Title @alert.AlertMessage.Title
@if (alert.AlertMessage.Dismissible)
{
<CloseButton Clicked="() => alert.IsVisible = false" />
}
</Heading> </Heading>
} }
<Paragraph>@alert.AlertMessage.Text</Paragraph> <Paragraph>@alert.AlertMessage.Text</Paragraph>
@if (alert.AlertMessage.Dismissible) @if (alert.AlertMessage.Dismissible)
{ {
<CloseButton /> <CloseButton Clicked="() => alert.IsVisible = false" />
} }
</Alert> </Alert>
} }

2
framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs

@ -86,7 +86,7 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
return null; return null;
} }
var result = ossClient.GetObject(containerName, blobName); var result = ossClient.GetObject(containerName, blobName);
return await TryCopyToMemoryStreamAsync(result.Content, args.CancellationToken); return result.ResponseStream;
} }
protected virtual string GetContainerName(BlobProviderArgs args) protected virtual string GetContainerName(BlobProviderArgs args)

2
framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs

@ -103,7 +103,7 @@ public class AwsBlobProvider : BlobProviderBase, ITransientDependency
Key = blobName Key = blobName
}); });
return await TryCopyToMemoryStreamAsync(response.ResponseStream, args.CancellationToken); return response.ResponseStream;
} }
} }

3
framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs

@ -66,8 +66,7 @@ public class AzureBlobProvider : BlobProviderBase, ITransientDependency
} }
var blobClient = GetBlobClient(args, blobName); var blobClient = GetBlobClient(args, blobName);
var download = await blobClient.DownloadAsync(); return await blobClient.OpenReadAsync(cancellationToken: args.CancellationToken);
return await TryCopyToMemoryStreamAsync(download.Value.Content, args.CancellationToken);
} }
protected virtual BlobClient GetBlobClient(BlobProviderArgs args, string blobName) protected virtual BlobClient GetBlobClient(BlobProviderArgs args, string blobName)

8
framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs

@ -70,13 +70,7 @@ public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency
return await Policy.Handle<IOException>() return await Policy.Handle<IOException>()
.WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(retryCount)) .WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(retryCount))
.ExecuteAsync(async () => .ExecuteAsync(() => Task.FromResult(File.OpenRead(filePath)));
{
using (var fileStream = File.OpenRead(filePath))
{
return await TryCopyToMemoryStreamAsync(fileStream, args.CancellationToken);
}
});
} }
protected virtual Task<bool> ExistsAsync(string filePath) protected virtual Task<bool> ExistsAsync(string filePath)

2
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs

@ -1,7 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using Microsoft.Extensions.Caching.Hybrid;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Caching.Hybrid; using Volo.Abp.Caching.Hybrid;
using Volo.Abp.Json; using Volo.Abp.Json;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;

15
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs

@ -75,7 +75,7 @@ public class AbpHybridCache<TCacheItem, TCacheKey> : IHybridCache<TCacheItem, TC
protected HybridCache HybridCache { get; } protected HybridCache HybridCache { get; }
protected IDistributedCache DistributedCacheCache { get; } protected IDistributedCache DistributedCache { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
@ -105,7 +105,7 @@ public class AbpHybridCache<TCacheItem, TCacheKey> : IHybridCache<TCacheItem, TC
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
DistributedCacheOption = distributedCacheOption.Value; DistributedCacheOption = distributedCacheOption.Value;
HybridCache = hybridCache; HybridCache = hybridCache;
DistributedCacheCache = distributedCache; DistributedCache = distributedCache;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<AbpHybridCache<TCacheItem, TCacheKey>>.Instance; Logger = NullLogger<AbpHybridCache<TCacheItem, TCacheKey>>.Instance;
KeyNormalizer = keyNormalizer; KeyNormalizer = keyNormalizer;
@ -215,10 +215,15 @@ public class AbpHybridCache<TCacheItem, TCacheKey> : IHybridCache<TCacheItem, TC
} }
} }
var bytes = await DistributedCacheCache.GetAsync(NormalizeKey(key), token); if (await DistributedCache.GetAsync(NormalizeKey(key), token) != null)
if (bytes != null)
{ {
return ResolveSerializer().Deserialize(new ReadOnlySequence<byte>(bytes, 0, bytes.Length));; // Because HybridCache wraps the cache in L2(distributed cache), we can’t unwrap it directly and can only retrieve the value through its API
return await HybridCache.GetOrCreateAsync(
key: NormalizeKey(key),
factory: async cancel => await factory(),
options: optionsFactory?.Invoke(),
tags: null,
cancellationToken: token);
} }
value = await factory(); value = await factory();

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs

@ -66,7 +66,14 @@ public class BundlingService : IBundlingService, ITransientDependency
var frameworkVersion = GetTargetFrameworkVersion(projectFilePath, projectType); var frameworkVersion = GetTargetFrameworkVersion(projectFilePath, projectType);
var projectName = Path.GetFileNameWithoutExtension(projectFilePath); var projectName = Path.GetFileNameWithoutExtension(projectFilePath);
var assemblyFilePath = projectType == BundlingConsts.WebAssembly? PathHelper.GetWebAssemblyFilePath(directory, frameworkVersion, projectName) : PathHelper.GetMauiBlazorAssemblyFilePath(directory, projectName); var assemblyFilePath = projectType == BundlingConsts.WebAssembly
? PathHelper.GetWebAssemblyFilePath(directory, frameworkVersion, projectName)
: PathHelper.GetMauiBlazorAssemblyFilePath(directory, projectName);
if (assemblyFilePath == null)
{
throw new BundlingException("No assembly file found. Please build the project first.");
}
var startupModule = GetStartupModule(assemblyFilePath); var startupModule = GetStartupModule(assemblyFilePath);
var bundleDefinitions = new List<BundleTypeDefinition>(); var bundleDefinitions = new List<BundleTypeDefinition>();

11
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs

@ -1,4 +1,5 @@
using System.IO; using System;
using System.IO;
using System.Linq; using System.Linq;
namespace Volo.Abp.Cli.Bundling; namespace Volo.Abp.Cli.Bundling;
@ -13,12 +14,16 @@ static internal class PathHelper
static internal string GetWebAssemblyFilePath(string directory, string frameworkVersion, string projectFileName) static internal string GetWebAssemblyFilePath(string directory, string frameworkVersion, string projectFileName)
{ {
var outputDirectory = Path.Combine(directory, "bin", "Debug", frameworkVersion); var outputDirectory = Path.Combine(directory, "bin", "Debug", frameworkVersion);
return Path.Combine(outputDirectory, projectFileName + ".dll"); var path = Path.Combine(outputDirectory, projectFileName + ".dll");
return !File.Exists(path) ? null : path;
} }
static internal string GetMauiBlazorAssemblyFilePath(string directory, string projectFileName) static internal string GetMauiBlazorAssemblyFilePath(string directory, string projectFileName)
{ {
return Directory.GetFiles(directory, "*.dll", SearchOption.AllDirectories).First(f => !f.Contains("android") && f.EndsWith(projectFileName + ".dll")); return Directory.GetFiles(Path.Combine(directory, "bin"), "*.dll", SearchOption.AllDirectories).FirstOrDefault(f =>
!f.Contains("android") &&
!f.Contains("windows10") &&
f.EndsWith(projectFileName + ".dll", StringComparison.OrdinalIgnoreCase));
} }
static internal string GetWwwRootPath(string directory) static internal string GetWwwRootPath(string directory)

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs

@ -2,24 +2,24 @@
public static class CliConsts public static class CliConsts
{ {
public const string Command = "AbpCliCommand"; public static string Command = "AbpCliCommand";
public const string BranchPrefix = "branch@"; public static string BranchPrefix = "branch@";
public const string DocsLink = "https://abp.io/docs"; public static string DocsLink = "https://abp.io/docs";
public const string HttpClientName = "AbpHttpClient"; public static string HttpClientName = "AbpHttpClient";
public const string GithubHttpClientName = "GithubHttpClient"; public static string GithubHttpClientName = "GithubHttpClient";
public const string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout"; public static string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout";
public const string LicenseCodePlaceHolder = @"<LICENSE_CODE/>"; public static string LicenseCodePlaceHolder = @"<LICENSE_CODE/>";
public const string AppSettingsJsonFileName = "appsettings.json"; public static string AppSettingsJsonFileName = "appsettings.json";
public static string AppSettingsSecretJsonFileName = "appsettings.secrets.json";
public const string AppSettingsSecretJsonFileName = "appsettings.secrets.json";
public static class MemoryKeys public static class MemoryKeys
{ {
public const string LatestCliVersionCheckDate = "LatestCliVersionCheckDate"; public const string LatestCliVersionCheckDate = "LatestCliVersionCheckDate";

25
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs

@ -5,19 +5,18 @@ namespace Volo.Abp.Cli;
public static class CliUrls public static class CliUrls
{ {
public const string WwwAbpIo = WwwAbpIoProduction; public static string WwwAbpIo = WwwAbpIoProduction;
public const string AccountAbpIo = AccountAbpIoProduction; public static string AccountAbpIo = AccountAbpIoProduction;
public const string NuGetRootPath = NuGetRootPathProduction; public static string NuGetRootPath = NuGetRootPathProduction;
public const string LatestVersionCheckFullPath = public static string LatestVersionCheckFullPath = "https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json";
"https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json";
public static string WwwAbpIoProduction = "https://abp.io/";
public const string WwwAbpIoProduction = "https://abp.io/"; public static string AccountAbpIoProduction = "https://account.abp.io/";
public const string AccountAbpIoProduction = "https://account.abp.io/"; public static string NuGetRootPathProduction = "https://nuget.abp.io/";
public const string NuGetRootPathProduction = "https://nuget.abp.io/";
public static string WwwAbpIoDevelopment = "https://localhost:44328/";
public const string WwwAbpIoDevelopment = "https://localhost:44328/"; public static string AccountAbpIoDevelopment = "https://localhost:44333/";
public const string AccountAbpIoDevelopment = "https://localhost:44333/"; public static string NuGetRootPathDevelopment = "https://localhost:44373/";
public const string NuGetRootPathDevelopment = "https://localhost:44373/";
public static string GetNuGetServiceIndexUrl(string apiKey) public static string GetNuGetServiceIndexUrl(string apiKey)
{ {

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs

@ -68,7 +68,7 @@ public class HelpCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("Command List:"); sb.AppendLine("Command List:");
sb.AppendLine(""); sb.AppendLine("");
foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList)) foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList).OrderBy(x => x.Key))
{ {
var method = command.Value.GetMethod("GetShortDescription", BindingFlags.Static | BindingFlags.Public); var method = command.Value.GetMethod("GetShortDescription", BindingFlags.Static | BindingFlags.Public);
if (method == null) if (method == null)

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs

@ -38,24 +38,25 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ?? var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ??
Directory.GetCurrentDirectory(); Directory.GetCurrentDirectory();
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
var leptonXVersion = commandLineArgs.Options.GetOrNull(Options.LeptonXVersion.Short, Options.LeptonXVersion.Long);
if (updateNuget || !updateNpm) if (updateNuget || !updateNpm)
{ {
await UpdateNugetPackages(commandLineArgs, directory, version); await UpdateNugetPackages(commandLineArgs, directory, version, leptonXVersion);
} }
if (updateNpm || !updateNuget) if (updateNpm || !updateNuget)
{ {
await UpdateNpmPackages(directory, version); await UpdateNpmPackages(directory, version, leptonXVersion);
} }
} }
private async Task UpdateNpmPackages(string directory, string version) private async Task UpdateNpmPackages(string directory, string version, string leptonXVersion)
{ {
await _npmPackagesUpdater.Update(directory, version: version); await _npmPackagesUpdater.Update(directory, version: version, leptonXVersion: leptonXVersion);
} }
private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version) private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version, string leptonXVersion)
{ {
var solutions = new List<string>(); var solutions = new List<string>();
var givenSolution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); var givenSolution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long);
@ -77,7 +78,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
{ {
var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); var solutionName = Path.GetFileName(solution).RemovePostFix(".sln");
await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version); await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion);
Logger.LogInformation("Volo packages are updated in {SolutionName} solution", solutionName); Logger.LogInformation("Volo packages are updated in {SolutionName} solution", solutionName);
} }
@ -90,7 +91,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
{ {
var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); var projectName = Path.GetFileName(project).RemovePostFix(".csproj");
await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version); await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion);
Logger.LogInformation("Volo packages are updated in {ProjectName} project", projectName); Logger.LogInformation("Volo packages are updated in {ProjectName} project", projectName);
return; return;
@ -120,6 +121,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("-sn|--solution-name (Specify the solution name)"); sb.AppendLine("-sn|--solution-name (Specify the solution name)");
sb.AppendLine("--check-all (Check the new version of each package separately)"); sb.AppendLine("--check-all (Check the new version of each package separately)");
sb.AppendLine("-v|--version <version> (default: latest version)"); sb.AppendLine("-v|--version <version> (default: latest version)");
sb.AppendLine("-lv|--leptonx-version <version> (default: latest LeptonX version)");
sb.AppendLine(""); sb.AppendLine("");
sb.AppendLine("Some examples:"); sb.AppendLine("Some examples:");
sb.AppendLine(""); sb.AppendLine("");
@ -167,5 +169,11 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency
public const string Short = "v"; public const string Short = "v";
public const string Long = "version"; public const string Long = "version";
} }
public static class LeptonXVersion
{
public const string Short = "lv";
public const string Long = "leptonx-version";
}
} }
} }

43
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -50,7 +50,7 @@ public class NpmPackagesUpdater : ITransientDependency
public async Task Update(string rootDirectory, bool includePreviews = false, public async Task Update(string rootDirectory, bool includePreviews = false,
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, string version = null, bool includePreRc = false) bool switchToStable = false, string version = null, string leptonXVersion = null, bool includePreRc = false)
{ {
var fileList = _packageJsonFileFinder.Find(rootDirectory); var fileList = _packageJsonFileFinder.Find(rootDirectory);
@ -80,6 +80,7 @@ public class NpmPackagesUpdater : ITransientDependency
var updated = await UpdatePackagesInFile(file, includePreviews, includeReleaseCandidates, var updated = await UpdatePackagesInFile(file, includePreviews, includeReleaseCandidates,
switchToStable, switchToStable,
version, version,
leptonXVersion,
includePreRc); includePreRc);
packagesUpdated.TryAdd(file, updated); packagesUpdated.TryAdd(file, updated);
@ -162,6 +163,7 @@ public class NpmPackagesUpdater : ITransientDependency
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, bool switchToStable = false,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
bool includePreRc = false) bool includePreRc = false)
{ {
var packagesUpdated = false; var packagesUpdated = false;
@ -177,7 +179,7 @@ public class NpmPackagesUpdater : ITransientDependency
foreach (var abpPackage in abpPackages) foreach (var abpPackage in abpPackages)
{ {
var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, includeReleaseCandidates, var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, includeReleaseCandidates,
switchToStable, specifiedVersion, includePreRc); switchToStable, specifiedVersion, specifiedLeptonXVersion, includePreRc);
if (updated) if (updated)
{ {
@ -188,7 +190,7 @@ public class NpmPackagesUpdater : ITransientDependency
var updatedContent = packageJson.ToString(Formatting.Indented); var updatedContent = packageJson.ToString(Formatting.Indented);
File.WriteAllText(filePath, updatedContent); File.WriteAllText(filePath, updatedContent);
return packagesUpdated; return packagesUpdated;
} }
@ -199,6 +201,7 @@ public class NpmPackagesUpdater : ITransientDependency
bool includeReleaseCandidates = false, bool includeReleaseCandidates = false,
bool switchToStable = false, bool switchToStable = false,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
bool includePreRc = false) bool includePreRc = false)
{ {
var currentVersion = (string)package.Value; var currentVersion = (string)package.Value;
@ -207,18 +210,36 @@ public class NpmPackagesUpdater : ITransientDependency
if (!specifiedVersion.IsNullOrWhiteSpace()) if (!specifiedVersion.IsNullOrWhiteSpace())
{ {
if (!SpecifiedVersionExists(specifiedVersion, package)) if (package.Name.IndexOf("leptonx", StringComparison.InvariantCultureIgnoreCase) > 0 && !specifiedLeptonXVersion.IsNullOrWhiteSpace())
{ {
return false; if (!SpecifiedVersionExists(specifiedLeptonXVersion, package))
} {
return false;
}
if (SemanticVersion.Parse(specifiedVersion) <= if (SemanticVersion.Parse(specifiedLeptonXVersion) <=
SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^"))) SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^")))
{ {
return false; return false;
}
version = specifiedLeptonXVersion.EnsureStartsWith('^');
} }
else
{
if (!SpecifiedVersionExists(specifiedVersion, package))
{
return false;
}
version = specifiedVersion.EnsureStartsWith('^'); if (SemanticVersion.Parse(specifiedVersion) <=
SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^")))
{
return false;
}
version = specifiedVersion.EnsureStartsWith('^');
}
} }
else else
{ {

47
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs

@ -27,7 +27,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
Logger = NullLogger<VoloNugetPackagesVersionUpdater>.Instance; Logger = NullLogger<VoloNugetPackagesVersionUpdater>.Instance;
} }
public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) public async Task UpdateSolutionAsync(
string solutionPath,
bool includePreviews = false,
bool includeReleaseCandidates = false,
bool switchToStable = false,
bool checkAll = false,
string version = null,
string leptonXVersion = null)
{ {
var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); var projectPaths = ProjectFinder.GetProjectFiles(solutionPath);
@ -58,6 +65,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
latestReleaseCandidateVersionInfo.Version, latestReleaseCandidateVersionInfo.Version,
latestVersionFromMyGet, latestVersionFromMyGet,
version, version,
leptonXVersion,
latestStableVersions: latestStableVersions); latestStableVersions: latestStableVersions);
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@ -75,7 +83,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
} }
} }
public async Task UpdateProjectAsync(string projectPath, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) public async Task UpdateProjectAsync(
string projectPath,
bool includeNightlyPreviews = false,
bool includeReleaseCandidates = false,
bool switchToStable = false,
bool checkAll = false,
string version = null,
string leptonXVersion = null)
{ {
if (checkAll && version.IsNullOrWhiteSpace()) if (checkAll && version.IsNullOrWhiteSpace())
{ {
@ -102,6 +117,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
latestReleaseCandidateVersionInfo.Version, latestReleaseCandidateVersionInfo.Version,
latestVersionFromMyGet, latestVersionFromMyGet,
version, version,
leptonXVersion,
latestStableVersions: latestStableVersions); latestStableVersions: latestStableVersions);
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@ -166,6 +182,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
SemanticVersion latestNugetReleaseCandidateVersion = null, SemanticVersion latestNugetReleaseCandidateVersion = null,
string latestMyGetVersion = null, string latestMyGetVersion = null,
string specifiedVersion = null, string specifiedVersion = null,
string specifiedLeptonXVersion = null,
List<PackageVersionCheckerService.LatestStableVersionResult> latestStableVersions = null) List<PackageVersionCheckerService.LatestStableVersionResult> latestStableVersions = null)
{ {
string packageId = null; string packageId = null;
@ -222,21 +239,35 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency
var leptonXPackageVersion = latestStableVersions? var leptonXPackageVersion = latestStableVersions?
.FirstOrDefault(v => v.Version.Equals(specifiedVersion, StringComparison.InvariantCultureIgnoreCase))?.LeptonX?.Version; .FirstOrDefault(v => v.Version.Equals(specifiedVersion, StringComparison.InvariantCultureIgnoreCase))?.LeptonX?.Version;
if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion)) || isStudioPackage) if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion) && specifiedLeptonXVersion.IsNullOrWhiteSpace()) || isStudioPackage)
{ {
Logger.LogWarning("Package: {PackageId} could not be updated. Please manually update the package version yourself to prevent version mismatches!", packageId); Logger.LogWarning("Package: {PackageId} could not be updated. Please manually update the package version yourself to prevent version mismatches!", packageId);
continue; continue;
} }
var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion); if (isLeptonXPackage)
if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedVersion, packageId))
{ {
TryUpdatingPackage(isLeptonXPackageWithVersion ? leptonXPackageVersion : specifiedVersion); var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion);
if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedLeptonXVersion, packageId))
{
TryUpdatingPackage(specifiedLeptonXVersion ?? leptonXPackageVersion);
}
else
{
Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedLeptonXVersion} does not exist!");
}
} }
else else
{ {
Logger.LogWarning("Package \"{PackageId}\" specified version v{SpecifiedVersion} does not exist!", packageId, specifiedVersion); if (await SpecifiedVersionExists(specifiedVersion, packageId))
{
TryUpdatingPackage(specifiedVersion);
}
else
{
Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedVersion} does not exist!");
}
} }
void TryUpdatingPackage(string versionToUpdate) void TryUpdatingPackage(string versionToUpdate)

2
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs

@ -9,4 +9,6 @@ public interface IOnServiceRegistredContext
ITypeList<IAbpInterceptor> Interceptors { get; } ITypeList<IAbpInterceptor> Interceptors { get; }
Type ImplementationType { get; } Type ImplementationType { get; }
object? ServiceKey { get; }
} }

5
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs

@ -13,10 +13,13 @@ public class OnServiceRegistredContext : IOnServiceRegistredContext
public virtual Type ImplementationType { get; } public virtual Type ImplementationType { get; }
public OnServiceRegistredContext(Type serviceType, [NotNull] Type implementationType) public virtual object? ServiceKey { get; }
public OnServiceRegistredContext(Type serviceType, [NotNull] Type implementationType, object? serviceKey = null)
{ {
ServiceType = Check.NotNull(serviceType, nameof(serviceType)); ServiceType = Check.NotNull(serviceType, nameof(serviceType));
ImplementationType = Check.NotNull(implementationType, nameof(implementationType)); ImplementationType = Check.NotNull(implementationType, nameof(implementationType));
ServiceKey = serviceKey;
Interceptors = new TypeList<IAbpInterceptor>(); Interceptors = new TypeList<IAbpInterceptor>();
} }

10
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Modularity; namespace Volo.Abp.Modularity;
@ -8,6 +9,13 @@ public class ServiceConfigurationContext
{ {
public IServiceCollection Services { get; } public IServiceCollection Services { get; }
public IConfiguration Configuration {
get {
return _configuration ??= Services.GetConfiguration();
}
}
private IConfiguration? _configuration;
public IDictionary<string, object?> Items { get; } public IDictionary<string, object?> Items { get; }
/// <summary> /// <summary>
@ -29,4 +37,4 @@ public class ServiceConfigurationContext
Services = Check.NotNull(services, nameof(services)); Services = Check.NotNull(services, nameof(services));
Items = new Dictionary<string, object?>(); Items = new Dictionary<string, object?>();
} }
} }

17
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs

@ -16,12 +16,12 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
where TEntityCacheItem : class where TEntityCacheItem : class
{ {
protected IReadOnlyRepository<TEntity, TKey> Repository { get; } protected IReadOnlyRepository<TEntity, TKey> Repository { get; }
protected IDistributedCache<TEntityCacheItem, TKey> Cache { get; } protected IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> Cache { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected EntityCacheBase( protected EntityCacheBase(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager) IUnitOfWorkManager unitOfWorkManager)
{ {
Repository = repository; Repository = repository;
@ -31,7 +31,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
public virtual async Task<TEntityCacheItem?> FindAsync(TKey id) public virtual async Task<TEntityCacheItem?> FindAsync(TKey id)
{ {
return await Cache.GetOrAddAsync( return (await Cache.GetOrAddAsync(
id, id,
async () => async () =>
{ {
@ -41,7 +41,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
} }
return MapToCacheItem(await Repository.FindAsync(id))!; return MapToCacheItem(await Repository.FindAsync(id))!;
}); }))?.Value;
} }
public virtual async Task<TEntityCacheItem> GetAsync(TKey id) public virtual async Task<TEntityCacheItem> GetAsync(TKey id)
@ -56,7 +56,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
} }
return MapToCacheItem(await Repository.GetAsync(id))!; return MapToCacheItem(await Repository.GetAsync(id))!;
}))!; }))!.Value!;
} }
protected virtual bool HasObjectExtensionInfo() protected virtual bool HasObjectExtensionInfo()
@ -65,15 +65,10 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null; ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null;
} }
protected abstract TEntityCacheItem? MapToCacheItem(TEntity? entity); protected abstract EntityCacheItemWrapper<TEntityCacheItem>? MapToCacheItem(TEntity? entity);
public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData) public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData)
{ {
if (eventData is EntityCreatedEventData<TEntity>)
{
return;
}
/* Why we are using double remove: /* Why we are using double remove:
* First Cache.RemoveAsync drops the cache item in a unit of work. * First Cache.RemoveAsync drops the cache item in a unit of work.
* Some other application / thread may read the value from database and put it to the cache again * Some other application / thread may read the value from database and put it to the cache again

12
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.Domain.Entities.Caching;
public class EntityCacheItemWrapper<TEntityCacheItem>
where TEntityCacheItem : class
{
public TEntityCacheItem? Value { get; set; }
public EntityCacheItemWrapper(TEntityCacheItem? value)
{
Value = value;
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs

@ -15,7 +15,7 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
public EntityCacheWithObjectMapper( public EntityCacheWithObjectMapper(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper) IObjectMapper objectMapper)
: base(repository, cache, unitOfWorkManager) : base(repository, cache, unitOfWorkManager)
@ -23,7 +23,7 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
ObjectMapper = objectMapper; ObjectMapper = objectMapper;
} }
protected override TEntityCacheItem? MapToCacheItem(TEntity? entity) protected override EntityCacheItemWrapper<TEntityCacheItem>? MapToCacheItem(TEntity? entity)
{ {
if (entity == null) if (entity == null)
{ {
@ -32,9 +32,9 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
if (typeof(TEntity) == typeof(TEntityCacheItem)) if (typeof(TEntity) == typeof(TEntityCacheItem))
{ {
return entity.As<TEntityCacheItem>(); return new EntityCacheItemWrapper<TEntityCacheItem>(entity.As<TEntityCacheItem>());
} }
return ObjectMapper.Map<TEntity, TEntityCacheItem>(entity); return new EntityCacheItemWrapper<TEntityCacheItem>(ObjectMapper.Map<TEntity, TEntityCacheItem>(entity));
} }
} }

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

Loading…
Cancel
Save