Browse Source

Merge branch 'dev' into issue/focusProblemOnFormProps

pull/23778/head
yagmurcelk 5 months ago
parent
commit
232c3a836c
  1. 2
      .github/workflows/build-and-test.yml
  2. 5
      Directory.Build.props
  3. 237
      Directory.Packages.props
  4. 25
      abp_io/AbpIoLocalization/AbpIoLocalization.sln
  5. 3
      abp_io/AbpIoLocalization/AbpIoLocalization.slnx
  6. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/AbpIoLocalization.csproj
  7. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  8. 192
      docs/en/Community-Articles/2025-09-18-How-Can-We-Apply-the-DRY-Principle-in-a-Better-Way/post.md
  9. 4
      docs/en/docs-nav.json
  10. 3
      docs/en/framework/fundamentals/caching.md
  11. 307
      docs/en/framework/infrastructure/artificial-intelligence.md
  12. 35
      docs/en/framework/infrastructure/blob-storing/memory.md
  13. 1
      docs/en/framework/infrastructure/index.md
  14. 5
      docs/en/framework/ui/angular/abp-window-service.md
  15. 53
      docs/en/framework/ui/angular/component-replacement.md
  16. 5
      docs/en/framework/ui/angular/config-state-service.md
  17. 12
      docs/en/framework/ui/angular/confirmation-service.md
  18. 3
      docs/en/framework/ui/angular/content-projection-service.md
  19. 13
      docs/en/framework/ui/angular/dom-insertion-service.md
  20. 3
      docs/en/framework/ui/angular/environment.md
  21. 3
      docs/en/framework/ui/angular/features.md
  22. 7
      docs/en/framework/ui/angular/global-features.md
  23. 38
      docs/en/framework/ui/angular/how-replaceable-components-work-with-extensions.md
  24. 13
      docs/en/framework/ui/angular/http-error-reporter-service.md
  25. 3
      docs/en/framework/ui/angular/http-requests.md
  26. 25
      docs/en/framework/ui/angular/lazy-load-service.md
  27. 11
      docs/en/framework/ui/angular/list-service.md
  28. 3
      docs/en/framework/ui/angular/localization.md
  29. 3
      docs/en/framework/ui/angular/modal.md
  30. 43
      docs/en/framework/ui/angular/modifying-the-menu.md
  31. 3
      docs/en/framework/ui/angular/page-alerts.md
  32. 2
      docs/en/framework/ui/angular/page-component.md
  33. 58
      docs/en/framework/ui/angular/permission-management-component-replacement.md
  34. 9
      docs/en/framework/ui/angular/permission-management.md
  35. 29
      docs/en/framework/ui/angular/router-events.md
  36. 5
      docs/en/framework/ui/angular/service-proxies.md
  37. 3
      docs/en/framework/ui/angular/settings.md
  38. 21
      docs/en/framework/ui/angular/subscription-service.md
  39. 45
      docs/en/framework/ui/angular/theming.md
  40. 35
      docs/en/framework/ui/angular/toaster-service.md
  41. 3
      docs/en/framework/ui/angular/track-by-service.md
  42. 262
      docs/en/framework/ui/react-native/index.md
  43. 127
      docs/en/framework/ui/react-native/setting-up-android-emulator.md
  44. BIN
      docs/en/get-started/images/abp-studio-mobile-sample.gif
  45. 5
      docs/en/get-started/layered-web-application.md
  46. BIN
      docs/en/images/author-input-in-book-form.png
  47. BIN
      docs/en/images/author-list-with-options.png
  48. BIN
      docs/en/images/author-list.png
  49. BIN
      docs/en/images/authors-in-book-form.png
  50. BIN
      docs/en/images/book-list-with-author.png
  51. BIN
      docs/en/images/book-list-with-options.png
  52. BIN
      docs/en/images/book-list.png
  53. BIN
      docs/en/images/book-store-menu-item.png
  54. BIN
      docs/en/images/books-menu-item.png
  55. BIN
      docs/en/images/create-author.png
  56. BIN
      docs/en/images/create-book-button-visibility.png
  57. BIN
      docs/en/images/create-book-icon.png
  58. BIN
      docs/en/images/create-book.png
  59. BIN
      docs/en/images/delete-author-alert.png
  60. BIN
      docs/en/images/delete-book-alert.png
  61. BIN
      docs/en/images/delete-book.png
  62. BIN
      docs/en/images/react-native-environment-local-ip.png
  63. BIN
      docs/en/images/react-native-introduction.gif
  64. BIN
      docs/en/images/react-native-microservice-be-config.png
  65. BIN
      docs/en/images/react-native-monolith-be-config.png
  66. BIN
      docs/en/images/react-native-monolith-environment-local-ip.png
  67. BIN
      docs/en/images/react-native-option.png
  68. BIN
      docs/en/images/react-native-store-folder.png
  69. BIN
      docs/en/images/react-native-tiered-be-config.png
  70. BIN
      docs/en/images/react-native-tiered-environment-local-ip.png
  71. BIN
      docs/en/images/rn-login-android-studio.png
  72. BIN
      docs/en/images/rn-login-iphone.png
  73. BIN
      docs/en/images/update-author.png
  74. BIN
      docs/en/images/update-book.png
  75. BIN
      docs/en/images/update-delete-book-button-visibility.png
  76. 18
      docs/en/modules/setting-management.md
  77. 86
      docs/en/release-info/migration-guides/abp-10-0.md
  78. 1
      docs/en/release-info/migration-guides/index.md
  79. 40
      docs/en/solution-templates/layered-web-application/deployment/deployment-docker-compose.md
  80. 10
      docs/en/solution-templates/layered-web-application/mobile-applications.md
  81. 22
      docs/en/solution-templates/microservice/mobile-applications.md
  82. 8
      docs/en/suite/solution-structure.md
  83. 5
      docs/en/tutorials/book-store/part-02.md
  84. 82
      docs/en/tutorials/book-store/part-03.md
  85. 12
      docs/en/tutorials/book-store/part-09.md
  86. 12
      docs/en/tutorials/book-store/part-10.md
  87. 7
      docs/en/tutorials/microservice/part-05.md
  88. 963
      docs/en/tutorials/mobile/react-native/index.md
  89. 11
      docs/en/tutorials/todo/layered/index.md
  90. 11
      docs/en/tutorials/todo/single-layer/index.md
  91. 9
      docs/en/ui-themes/lepton-x-lite/angular.md
  92. 18
      docs/en/ui-themes/lepton-x/angular-customization.md
  93. 1740
      framework/Volo.Abp.sln
  94. 1
      framework/Volo.Abp.sln.DotSettings
  95. 255
      framework/Volo.Abp.slnx
  96. 3
      framework/src/Volo.Abp.AI.Abstractions/FodyWeavers.xml
  97. 30
      framework/src/Volo.Abp.AI.Abstractions/FodyWeavers.xsd
  98. 3
      framework/src/Volo.Abp.AI.Abstractions/Volo.Abp.AI.Abstractions.abppkg
  99. 26
      framework/src/Volo.Abp.AI.Abstractions/Volo.Abp.AI.Abstractions.csproj
  100. 7
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/AbpAIAbstractionsModule.cs

2
.github/workflows/build-and-test.yml

@ -54,7 +54,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 9.0.100
dotnet-version: 10.0.x
- name: Build All
run: ./build-all.ps1

5
Directory.Build.props

@ -5,8 +5,9 @@
<ItemGroup>
<PackageReference Condition="'$(IsTestProject)' == 'true'" Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<Version Condition="$(MSBuildProjectFullPath.Contains('templates'))">6.0.4</Version>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

237
Directory.Packages.props

@ -1,131 +1,135 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageFloatingVersionsEnabled>true</CentralPackageFloatingVersionsEnabled>
</PropertyGroup>
<ItemGroup>
<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.3" />
<PackageVersion Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageVersion Include="AsyncKeyedLock" Version="7.1.6" />
<PackageVersion Include="Autofac" Version="8.2.1" />
<PackageVersion Include="Autofac" Version="8.4.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
<PackageVersion Include="AutoMapper" Version="14.0.0" />
<PackageVersion Include="AutoMapper" Version="15.0.1" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="AWSSDK.S3" Version="4.0.0" />
<PackageVersion Include="AWSSDK.SecurityToken" Version="4.0.0" />
<PackageVersion Include="AWSSDK.S3" Version="4.0.7.2" />
<PackageVersion Include="AWSSDK.SecurityToken" Version="4.0.2.2" />
<PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.19.0" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.24.0" />
<PackageVersion Include="Blazorise" Version="1.8.1" />
<PackageVersion Include="Blazorise.Components" Version="1.8.1" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.8.1" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.8.1" />
<PackageVersion Include="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.20.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.25.0" />
<PackageVersion Include="Blazorise" Version="1.8.2" />
<PackageVersion Include="Blazorise.Components" Version="1.8.2" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.8.2" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.8.2" />
<PackageVersion Include="Castle.Core" Version="5.2.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
<PackageVersion Include="Confluent.Kafka" Version="2.10.0" />
<PackageVersion Include="Confluent.Kafka" Version="2.11.1" />
<PackageVersion Include="Dapper" Version="2.1.66" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.15.4" />
<PackageVersion Include="Dapr.Client" Version="1.15.4" />
<PackageVersion Include="MyCSharp.HttpUserAgentParser" Version="3.0.25" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.235.9" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.16.0" />
<PackageVersion Include="Dapr.Client" Version="1.16.0" />
<PackageVersion Include="MyCSharp.HttpUserAgentParser" Version="3.0.28" />
<PackageVersion Include="Devart.Data.Oracle.EFCore" Version="10.4.290.9" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.8" />
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3" />
<PackageVersion Include="DistributedLock.Redis" Version="1.1.0" />
<PackageVersion Include="DeepL.net" Version="1.15.0" />
<PackageVersion Include="itext.bouncy-castle-adapter" Version="9.2.0" />
<PackageVersion Include="itext7" Version="9.2.0" />
<PackageVersion Include="itext7.pdfhtml" Version="6.2.0" />
<PackageVersion Include="itext.bouncy-castle-adapter" Version="9.3.0" />
<PackageVersion Include="itext7" Version="9.3.0" />
<PackageVersion Include="itext7.pdfhtml" Version="6.2.1" />
<PackageVersion Include="MongoSandbox.Core" Version="2.0.0" />
<PackageVersion Include="MongoSandbox8.runtime.linux-x64" Version="2.0.0" />
<PackageVersion Include="MongoSandbox8.runtime.osx-arm64" Version="2.0.0" />
<PackageVersion Include="MongoSandbox8.runtime.win-x64" Version="2.0.0" />
<PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="FluentValidation" Version="12.0.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.13.0" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.18" />
<PackageVersion Include="HtmlSanitizer" Version="9.0.884" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.21" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.21" />
<PackageVersion Include="HtmlSanitizer" Version="9.0.886" />
<PackageVersion Include="Duende.IdentityModel" Version="7.1.0" />
<PackageVersion Include="IdentityServer4" Version="4.1.2" />
<PackageVersion Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.2" />
<PackageVersion Include="LdapForNet" Version="2.7.15" />
<PackageVersion Include="LibGit2Sharp" Version="0.31.0" />
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="13.4.0" />
<PackageVersion Include="MailKit" Version="4.12.0" />
<PackageVersion Include="Markdig.Signed" Version="0.41.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.60" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.60" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.5" />
<PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="14.8.2" />
<PackageVersion Include="MailKit" Version="4.13.0" />
<PackageVersion Include="Markdig.Signed" Version="0.42.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.110" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.110" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.5" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.1.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.61.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.61.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.9.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Composite" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.14.0" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.14.0" />
<PackageVersion Include="Minio" Version="6.0.4" />
<PackageVersion Include="MongoDB.Driver" Version="3.3.0" />
<PackageVersion Include="Minio" Version="6.0.5" />
<PackageVersion Include="MongoDB.Driver" Version="3.5.0" />
<PackageVersion Include="NEST" Version="7.17.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Nito.AsyncEx.Context" Version="5.1.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0-rc.1" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Versioning" Version="6.13.2" />
<PackageVersion Include="NUglify" Version="1.21.15" />
<PackageVersion Include="NuGet.Versioning" Version="6.14.0" />
<PackageVersion Include="NUglify" Version="1.21.17" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="7.1.0" />
@ -133,24 +137,24 @@
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="7.1.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="7.1.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="7.1.0" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.80" />
<PackageVersion Include="Polly" Version="8.5.2" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.90" />
<PackageVersion Include="Polly" Version="8.6.3" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="9.0.6" />
<PackageVersion Include="Quartz" Version="3.14.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.14.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.14.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.14.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.0-preview" />
<PackageVersion Include="Quartz" Version="3.15.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.15.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.15.0" />
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
<PackageVersion Include="Rebus" Version="8.8.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.3.0" />
<PackageVersion Include="Rebus.ServiceProvider" Version="10.5.0" />
<PackageVersion Include="Riok.Mapperly" Version="4.2.1" />
<PackageVersion Include="Scriban" Version="6.2.1" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Scriban" Version="6.3.0" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.1" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
@ -158,34 +162,33 @@
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.8" />
<PackageVersion Include="Slugify.Core" Version="4.0.1" />
<PackageVersion Include="Spectre.Console" Version="0.50.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.31" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.5" />
<PackageVersion Include="SkiaSharp" Version="3.119.0" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.0" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="3.119.0" />
<PackageVersion Include="Slugify.Core" Version="5.1.1" />
<PackageVersion Include="Spectre.Console" Version="0.51.1" />
<PackageVersion Include="StackExchange.Redis" Version="2.9.17" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="9.0.4" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.0-rc.*" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.7" />
<PackageVersion Include="System.Linq.Queryable" Version="4.3.0" />
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
<PackageVersion Include="System.Security.Permissions" Version="9.0.5" />
<PackageVersion Include="System.Security.Permissions" Version="10.0.0-rc.*" />
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.5" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.5" />
<PackageVersion Include="System.Text.Json" Version="9.0.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0-rc.*" />
<PackageVersion Include="System.Text.Encodings.Web" Version="10.0.0-rc.*" />
<PackageVersion Include="System.Text.Json" Version="10.0.0-rc.*" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1231" />
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1273" />
<PackageVersion Include="TimeZoneConverter" Version="7.0.0" />
<PackageVersion Include="Unidecode.NET" Version="2.1.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.9.2" />
<PackageVersion Include="System.Management" Version="9.0.1"/>
<PackageVersion Include="Fody" Version="6.9.3" />
<PackageVersion Include="System.Management" Version="10.0.0-rc.*"/>
</ItemGroup>
</Project>
</Project>

25
abp_io/AbpIoLocalization/AbpIoLocalization.sln

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AbpIoLocalization", "AbpIoLocalization\AbpIoLocalization.csproj", "{35D94BAB-22DF-47E0-AB4A-99CB6495CF50}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{35D94BAB-22DF-47E0-AB4A-99CB6495CF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35D94BAB-22DF-47E0-AB4A-99CB6495CF50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35D94BAB-22DF-47E0-AB4A-99CB6495CF50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35D94BAB-22DF-47E0-AB4A-99CB6495CF50}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2CF52C6D-D914-44A3-8F02-7E7BEA0644C5}
EndGlobalSection
EndGlobal

3
abp_io/AbpIoLocalization/AbpIoLocalization.slnx

@ -0,0 +1,3 @@
<Solution>
<Project Path="AbpIoLocalization/AbpIoLocalization.csproj" />
</Solution>

2
abp_io/AbpIoLocalization/AbpIoLocalization/AbpIoLocalization.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

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

@ -1911,6 +1911,7 @@
"SupportCustomABPArchitecturesExplanation": "No. Support is only provided for standard ABP solution structures. On the other hand, you can always get support for your custom needs with a paid consultancy from the ABP Team.",
"DoesABPCollectAnyPersonalOrTechnicalData": "Does ABP collect any personal or technical data?",
"ABPCollectAnyDataExplanation": "The software may collect information about you and your use of the software, and send that to Volosoft. Volosoft as the software and service provider may use this information to provide services and improve its products & services. You may opt-out of these scenarios, as described in the <a href=\"{0}\">EULA</a> under PRIVACY AND COLLECTION OF PERSONAL DATA topic .",
"InThisDocument": "In this document"
"InThisDocument": "In this document",
"RecommendedArticles": "Recommended Articles"
}
}

192
docs/en/Community-Articles/2025-09-18-How-Can-We-Apply-the-DRY-Principle-in-a-Better-Way/post.md

@ -0,0 +1,192 @@
# How Can We Apply the DRY Principle in a Better Way
## Introduction
In modern software development, the **DRY principle** - short for *Don't Repeat Yourself* - is one of the most important rules for writing clean and easy-to-maintain code. First introduced by Andrew Hunt and David Thomas in *The Pragmatic Programmer*, DRY focuses on removing repetition in code, documentation, and processes. The main idea is: *"Every piece of knowledge should exist in only one place in the system."* Even if this idea looks simple, using it in real projects - especially big ones - needs discipline, good planning, and the right tools.
This article explains what the DRY principle is, why it matters, how we can use it in a better way, and how big projects can make it work.
---
## What is the DRY Principle?
The DRY principle is about reducing repetition. Repetition can happen in many forms:
- **Code repetition:** Writing the same logic in different functions, classes, or modules.
- **Business logic repetition:** Having the same business rules in different parts of the system.
- **Configuration repetition:** Keeping multiple versions of the same settings or constants.
- **Documentation repetition:** Copying the same explanations across files, which later become inconsistent.
When teams follow DRY, each piece of knowledge exists only once, making the system easier to update and lowering the chance of mistakes.
---
## Why is DRY Important?
1. **Easy Maintenance**
If some logic changes, we only need to update it in one place instead of many places.
2. **Consistency**
DRY helps avoid bugs caused by different versions of the same logic.
3. **Better Growth**
DRY codebases are smaller and more modular, so they grow more easily.
4. **Teamwork**
In large teams, DRY reduces confusion and makes it easier for everyone to understand the system.
---
## Common Mistakes with DRY
Sometimes developers use DRY in the wrong way. Trying too hard to remove repetition can create very complex code that is difficult to read. For example:
- **Abstracting too early:** Generalizing code before real patterns appear can cause confusion.
- **Over-engineering:** Building tools or frameworks for problems that only happen once.
- **Too much connection:** Forcing different modules to share code when they should stay independent.
The goal of DRY is not to remove *all* repetition, but to remove *meaningless repetition* that makes code harder to maintain.
---
## How Can We Use DRY Better?
### 1. Find Repetition Early
Tools like **linters, static analyzers, and code reviews** help find repeated logic. Some tools can even automatically detect duplicate code.
### 2. Create Reusable Components
Instead of copying code, move shared logic into:
- Functions or methods
- Utility classes
- Shared modules or libraries
- Configuration files (for constants)
**Example:**
```python
# Not DRY
send_email_to_admin(subject, body)
send_email_to_user(subject, body)
# DRY
send_email(recipient_type, subject, body)
```
### 3. Keep Documentation in One Place
Do not copy the same documentation to many files. Use wikis, shared docs, or API tools that pull from one single source.
### 4. Use Frameworks and Standards
Frameworks like **Spring Boot, ASP.NET, or Django** give built-in ways to follow DRY, so developers don’t have to repeat the same patterns.
### 5. Automate Tasks with High Repetition
Instead of writing the same boilerplate code again and again, use:
- Code generators
- CI/CD pipelines
- Infrastructure as Code (IaC)
---
## Practical Steps to Apply DRY in Daily Work
Applying DRY is not only about big design decisions; it is also about small habits in daily coding. Some practical steps include:
- **Review before copy-paste:** When you feel the need to copy code, stop and ask if it can be moved to a shared method or module.
- **Write helper functions:** If you see similar logic more than twice, create a helper function instead of repeating it.
- **Use clear naming:** Good names for functions and variables make shared code easier to understand and reuse.
- **Communicate with the team:** Before creating a new utility or feature, check if something similar already exists in the project.
- **Refactor regularly:** Small refactors during development help keep code clean and reduce hidden repetition.
These small actions in daily work make it easier to follow DRY naturally, without waiting for big rewrites.
---
## DRY in Large Projects
In big systems, DRY is harder but also more important. Large codebases often involve many developers working at the same time, which increases the chance of duplication.
### How Big Teams Use DRY:
1. **Modular Architectures**
Breaking applications into services, libraries, or packages helps to centralize shared functionality.
2. **Shared Libraries and APIs**
Instead of rewriting logic, teams create internal libraries that act as the single source of truth.
3. **Code Reviews and Pair Programming**
Developers check each other’s work to find repetition early.
4. **Automated Tools**
Tools like **SonarQube, PMD, and ESLint** help detect duplicate code.
5. **Clear Ownership**
Assigning owners for modules helps keep consistency and avoids duplication.
---
## Example: DRY in a Large Project
Imagine a large e-commerce platform with multiple teams:
- **Team A** handles user authentication.
- **Team B** handles orders.
- **Team C** handles payments.
Without DRY, both Team B and Team C might create their own email notification system. With DRY, they share a **Notification Service**:
```csharp
// Shared Notification Service
public class NotificationService {
public void Send(string recipient, string subject, string message) {
// Unified logic for sending notifications
}
}
// Usage in Orders Module
notificationService.Send(order.CustomerEmail, "Order Confirmed", "Your order has been placed.");
// Usage in Payments Module
notificationService.Send(payment.CustomerEmail, "Payment Received", "Your payment was successful.");
```
This way, if the email format changes, only the **Notification Service** needs updating.
---
## Conclusion
The DRY principle is still one of the most important rules in modern software development. While the rule sounds simple - *don’t repeat yourself* - using it in the right way requires careful thinking. Wrong use of DRY can make things more complex, but correct use of DRY improves maintainability, growth, and teamwork.
To use DRY better:
- Focus on meaningful repetition.
- Create reusable components.
- Use frameworks, automation, and shared libraries.
- Encourage teamwork and code reviews.
In large projects, DRY works best with strong architecture, helpful tools, and discipline. If teams apply these practices, they can build cleaner, more maintainable, and scalable systems.

4
docs/en/docs-nav.json

@ -603,6 +603,10 @@
{
"text": "Storage Providers",
"items": [
{
"text": "Memory Provider",
"path": "framework/infrastructure/blob-storing/memory.md"
},
{
"text": "File System Provider",
"path": "framework/infrastructure/blob-storing/file-system.md"

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

@ -224,9 +224,8 @@ Configure<AbpDistributedCacheOptions>(options =>
> Write that code inside the `ConfigureServices` method of your [module class](../architecture/modularity/basics.md).
#### Available Options
* `HideErrors` (`bool`, default: `true`): Enables/disables hiding the errors on writing/reading values from the cache server.
* `HideErrors` (`bool`, default: `true`): Enables or disables hiding errors when reading from or writing to the cache server. In the **development** environment, this option is **disabled** to help developers detect and fix any cache server issues.
* `KeyPrefix` (`string`, default: `null`): If your cache server is shared by multiple applications, you can set a prefix for the cache keys for your application. In this case, different applications can not overwrite each other's cache items.
* `GlobalCacheEntryOptions` (`DistributedCacheEntryOptions`): Used to set default distributed cache options (like `AbsoluteExpiration` and `SlidingExpiration`) used when you don't specify the options while saving cache items. The default value uses the `SlidingExpiration` as 20 minutes.

307
docs/en/framework/infrastructure/artificial-intelligence.md

@ -0,0 +1,307 @@
# Artificial Intelligence
ABP provides a simple way to integrate AI capabilities into your applications by unifying two popular .NET AI stacks under a common concept called a "workspace":
- Microsoft.Extensions.AI `IChatClient`
- Microsoft.SemanticKernel `Kernel`
A workspace is just a named scope. You configure providers per workspace and then resolve either default services (for the "Default" workspace) or workspace-scoped services.
## Installation
> This package is not included by default. Install it to enable AI features.
It is suggested to use the ABP CLI to install the package. Open a command line window in the folder of the project (.csproj file) and type the following command:
```bash
abp add-package Volo.Abp.AI
```
### Manual Installation
Add nuget package to your project:
```bash
dotnet add package Volo.Abp.AI
```
Then add the module dependency to your module class:
```csharp
using Volo.Abp.AI;
using Volo.Abp.Modularity;
[DependsOn(typeof(AbpAIModule))]
public class MyProjectModule : AbpModule
{
}
```
## Usage
### Chat Client
#### Default configuration (quick start)
Configure the default workspace to inject `IChatClient` directly.
```csharp
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;
using Volo.Abp.AI;
using Volo.Abp.Modularity;
public class MyProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpAIOptions>(options =>
{
options.Workspaces.ConfigureDefault(configuration =>
{
configuration.ConfigureChatClient(chatClientConfiguration =>
{
chatClientConfiguration.Builder = new ChatClientBuilder(
sp => new OllamaApiClient("http://localhost:11434", "mistral")
);
});
// Chat client only in this quick start
});
});
}
}
```
Once configured, inject the default chat client:
```csharp
using Microsoft.Extensions.AI;
public class MyService
{
private readonly IChatClient _chatClient; // default chat client
public MyService(IChatClient chatClient)
{
_chatClient = chatClient;
}
}
```
#### Workspace configuration
Workspaces allow multiple, isolated AI configurations. Define workspace types (optionally decorated with `WorkspaceNameAttribute`). If omitted, the type’s full name is used.
```csharp
using Volo.Abp.AI;
[WorkspaceName("GreetingAssistant")]
public class GreetingAssistant // ChatClient-only workspace
{
}
```
Configure a ChatClient workspace:
```csharp
public class MyProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpAIOptions>(options =>
{
options.Workspaces.Configure<GreetingAssistant>(configuration =>
{
configuration.ConfigureChatClient(chatClientConfiguration =>
{
chatClientConfiguration.Builder = new ChatClientBuilder(
sp => new OllamaApiClient("http://localhost:11434", "mistral")
);
chatClientConfiguration.BuilderConfigurers.Add(builder =>
{
// Anything you want to do with the builder:
// builder.UseFunctionInvocation().UseLogging(); // For example
});
});
});
});
}
}
```
### Semantic Kernel
#### Default configuration
```csharp
public class MyProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpAIOptions>(options =>
{
options.Workspaces.ConfigureDefault(configuration =>
{
configuration.ConfigureKernel(kernelConfiguration =>
{
kernelConfiguration.Builder = Kernel.CreateBuilder()
.AddAzureOpenAIChatClient("...", "...");
});
// Note: Chat client is not configured here
});
});
}
}
```
Once configured, inject the default kernel:
```csharp
using System.Threading.Tasks;
using Volo.Abp.AI;
public class MyService
{
private readonly IKernelAccessor _kernelAccessor;
public MyService(IKernelAccessor kernelAccessor)
{
_kernelAccessor = kernelAccessor;
}
public async Task DoSomethingAsync()
{
var kernel = _kernelAccessor.Kernel; // Kernel might be null if no workspace is configured.
var result = await kernel.InvokeAsync(/*... */);
}
}
```
#### Workspace configuration
```csharp
public class MyProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpAIOptions>(options =>
{
options.Workspaces.Configure<ContentPlanner>(configuration =>
{
configuration.ConfigureKernel(kernelConfiguration =>
{
kernelConfiguration.Builder = Kernel.CreateBuilder()
.AddOpenAIChatCompletion("...", "...");
});
});
});
}
}
```
#### Workspace usage
```csharp
using Microsoft.Extensions.AI;
using Volo.Abp.AI;
using Microsoft.SemanticKernel;
public class PlanningService
{
private readonly IKernelAccessor<ContentPlanner> _kernelAccessor;
private readonly IChatClient<ContentPlanner> _chatClient; // available even if only Kernel is configured
public PlanningService(
IKernelAccessor<ContentPlanner> kernelAccessor,
IChatClient<ContentPlanner> chatClient)
{
_kernelAccessor = kernelAccessor;
_chatClient = chatClient;
}
public async Task<string> PlanAsync(string topic)
{
var kernel = _kernelAccessor.Kernel; // Microsoft.SemanticKernel.Kernel
// Use Semantic Kernel APIs if needed...
var response = await _chatClient.GetResponseAsync(
[new ChatMessage(ChatRole.User, $"Create a content plan for: {topic}")]
);
return response?.Message?.Text ?? string.Empty;
}
}
```
## Options
`AbpAIOptions` configuration pattern offers `ConfigureChatClient(...)` and `ConfigureKernel(...)` methods for configuration. These methods are defined in the `WorkspaceConfiguration` class. They are used to configure the `ChatClient` and `Kernel` respectively.
`Builder` is set once and is used to build the `ChatClient` or `Kernel` instance. `BuilderConfigurers` is a list of actions that are applied to the `Builder` instance for incremental changes. These actions are executed in the order they are added.
If a workspace configures only the Kernel, a chat client may still be exposed for that workspace through the Kernel’s service provider (when available).
## Advanced Usage and Customizations
### Addding Your Own DelegatingChatClient
If you want to build your own decorator, implement a `DelegatingChatClient` derivative and provide an extension method that adds it to the `ChatClientBuilder` using `builder.Use(...)`.
Example sketch:
```csharp
using Microsoft.Extensions.AI;
public class SystemMessageChatClient : DelegatingChatClient
{
public SystemMessageChatClient(IChatClient inner, string systemMessage) : base(inner)
{
SystemMessage = systemMessage;
}
public string SystemMessage { get; set; }
public override Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
{
// Mutate messages/options as needed, then call base
return base.GetResponseAsync(messages, options, cancellationToken);
}
}
public static class SystemMessageChatClientExtensions
{
public static ChatClientBuilder UseSystemMessage(this ChatClientBuilder builder, string systemMessage)
{
return builder.Use(client => new SystemMessageChatClient(client, systemMessage));
}
}
```
```cs
chatClientConfiguration.BuilderConfigurers.Add(builder =>
{
builder.UseSystemMessage("You are a helpful assistant that greets users in a friendly manner with their names.");
});
```
## Technical Anatomy
- `AbpAIModule`: Wires up configured workspaces, registers keyed services and default services for the `"Default"` workspace.
- `AbpAIOptions`: Holds `Workspaces` and provides helper methods for internal keyed service naming.
- `WorkspaceConfigurationDictionary` and `WorkspaceConfiguration`: Configure per-workspace Chat Client and Kernel.
- `ChatClientConfiguration` and `KernelConfiguration`: Hold builders and a list of ordered builder configurers.
- `WorkspaceNameAttribute`: Names a workspace; falls back to the type’s full name if not specified.
- `IChatClient<TWorkspace>`: Typed chat client for a workspace.
- `IKernelAccessor<TWorkspace>`: Provides access to the workspace’s `Kernel` instance if configured.
- `AbpAIWorkspaceOptions`: Exposes `ConfiguredWorkspaceNames` for diagnostics.
There are no database tables for this feature; it is a pure configuration and DI integration layer.
## See Also
- Microsoft.Extensions.AI (Chat Client)
- Microsoft Semantic Kernel

35
docs/en/framework/infrastructure/blob-storing/memory.md

@ -0,0 +1,35 @@
# BLOB Storing Memory Provider
Memory Storage Provider is used to store BLOBs in the memory. This is mainly used for unit testing purposes.
> Read the [BLOB Storing document](../blob-storing) to understand how to use the BLOB storing system. This document only covers how to configure containers to use the memory.
## Installation
Use the ABP CLI to add [Volo.Abp.BlobStoring.Memory](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Memory) NuGet package to your project:
* Install the [ABP CLI](../../../cli) if you haven't installed before.
* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.Memory` package.
* Run `abp add-package Volo.Abp.BlobStoring.Memory` command.
If you want to do it manually, install the [Volo.Abp.BlobStoring.Memory](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Memory) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringMemoryModule))]` to the [ABP module](../../architecture/modularity/basics.md) class inside your project.
## Configuration
Configuration is done in the `ConfigureServices` method of your [module](../../architecture/modularity/basics.md) class, as explained in the [BLOB Storing document](../blob-storing).
**Example: Configure to use the Memory storage provider by default**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseMemory();
});
});
````
`UseMemory` extension method is used to set the Memory Provider for a container.
> See the [BLOB Storing document](../blob-storing) to learn how to configure this provider for a specific container.

1
docs/en/framework/infrastructure/index.md

@ -3,6 +3,7 @@
ABP provides a complete infrastructure for creating real world software solutions with modern architectures based on the .NET platform. Each of the following documents explains an infrastructure feature:
* [Audit Logging](./audit-logging.md)
* [Artificial Intelligence](./artificial-intelligence.md)
* [Background Jobs](./background-jobs/index.md)
* [Background Workers](./background-workers/index.md)
* [BLOB Storing](./blob-storing/index.md)

5
docs/en/framework/ui/angular/abp-window-service.md

@ -13,10 +13,9 @@ Firstly, ensure that the service is injected into the component or any other Ang
```js
import { AbpWindowService } from '@abp/ng.core';
import { inject } from '@angular/core';
constructor(private abpWindowService: AbpWindowService) { }
// or
// private abpWindowService = inject(AbpWindowService)
private abpWindowService = inject(AbpWindowService)
```
### Downloading a Blob

53
docs/en/framework/ui/angular/component-replacement.md

@ -13,13 +13,14 @@ Then, open the `app.component.ts` and execute the `add` method of `ReplaceableCo
```js
import { ReplaceableComponentsService } from '@abp/ng.core'; // imported ReplaceableComponentsService
import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum
import { Component, inject } from '@angular/core';
//...
@Component(/* component metadata */)
export class AppComponent {
constructor(
private replaceableComponents: ReplaceableComponentsService, // injected the service
) {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: YourNewRoleComponent,
key: eIdentityComponents.Roles,
@ -56,12 +57,13 @@ Open `app.component.ts` in `src/app` folder and modify it as shown below:
import { ReplaceableComponentsService } from '@abp/ng.core'; // imported ReplaceableComponentsService
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents enum for component keys
import { MyApplicationLayoutComponent } from './my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent
import { Component, inject } from '@angular/core';
@Component(/* component metadata */)
export class AppComponent {
constructor(
private replaceableComponents: ReplaceableComponentsService, // injected the service
) {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: MyApplicationLayoutComponent,
key: eThemeBasicComponents.ApplicationLayout,
@ -268,15 +270,15 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private replaceableComponents: ReplaceableComponentsService) {} // injected ReplaceableComponentsService
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: LogoComponent,
key: eThemeBasicComponents.Logo,
});
component: LogoComponent,
key: eThemeBasicComponents.Logo,
});
}
}
```
@ -454,15 +456,15 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private replaceableComponents: ReplaceableComponentsService) {} // injected ReplaceableComponentsService
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: RoutesComponent,
key: eThemeBasicComponents.Routes,
});
component: RoutesComponent,
key: eThemeBasicComponents.Routes,
});
}
}
```
@ -493,7 +495,7 @@ import {
SessionStateService,
LocalizationPipe
} from '@abp/ng.core';
import { Component, Inject } from '@angular/core';
import { Component, inject, Inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
@ -512,9 +514,13 @@ import snq from 'snq';
]
})
export class NavItemsComponent {
private configState = inject(ConfigStateService);
private authService = inject(AuthService);
private sessionState = inject(SessionStateService);
@Inject(NAVIGATE_TO_MANAGE_PROFILE) public navigateToManageProfile: any;
currentUser$: Observable<CurrentUserDto> = this.configState.getOne$('currentUser');
selectedTenant$ = this.sessionState.getTenant$();
languages$: Observable<LanguageInfo[]> = this.configState.getDeep$('localization.languages');
get smallScreen(): boolean {
@ -547,13 +553,6 @@ export class NavItemsComponent {
return this.sessionState.getLanguage();
}
constructor(
@Inject(NAVIGATE_TO_MANAGE_PROFILE) public navigateToManageProfile,
private configState: ConfigStateService,
private authService: AuthService,
private sessionState: SessionStateService
) {}
onChangeLang(cultureName: string) {
this.sessionState.setLanguage(cultureName);
}
@ -669,15 +668,15 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private replaceableComponents: ReplaceableComponentsService) {} // injected ReplaceableComponentsService
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: NavItemsComponent,
key: eThemeBasicComponents.NavItems,
});
component: NavItemsComponent,
key: eThemeBasicComponents.NavItems,
});
}
}
```

5
docs/en/framework/ui/angular/config-state-service.md

@ -8,12 +8,13 @@ In order to use the `ConfigStateService` you must inject it in your class as a d
```js
import { ConfigStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
private config = inject(ConfigStateService);
private config = inject(ConfigStateService);
}
```
@ -126,10 +127,12 @@ You can get the application configuration response and set the `ConfigStateServi
```js
import { AbpApplicationConfigurationService, ConfigStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
private abpApplicationConfigurationService = inject(AbpApplicationConfigurationService);
private config = inject(ConfigStateService);
constructor() {
this.abpApplicationConfigurationService.get({ includeLocalizationResources: false }).subscribe(config => {
this.config.setState(config);

12
docs/en/framework/ui/angular/confirmation-service.md

@ -8,12 +8,13 @@ You do not have to provide the `ConfirmationService` at component level, because
```js
import { ConfirmationService } from '@abp/ng.theme.shared';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private confirmation: ConfirmationService) {}
private confirmation = inject(ConfirmationService);
}
```
@ -36,8 +37,10 @@ You can subscribe to the confirmation closing event like below:
```js
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
import { inject } from '@angular/core';
constructor(private confirmation: ConfirmationService) {}
// inside the class:
private confirmation = inject(ConfirmationService);
this.confirmation
.warn('::WillBeDeleted', { key: '::AreYouSure', defaultValue: 'Are you sure?' })
@ -132,7 +135,10 @@ this.confirmation.clear();
You can change icons with the `withConfirmationIcon()` method inside `provideAbpThemeShared` function in the app.config.ts. The changes will affect all confirmation popup in the project.
```ts
import { provideAbpThemeShared, withConfirmationIcon } from '@abp/ng.theme.shared';
import {
provideAbpThemeShared,
withConfirmationIcon,
} from "@abp/ng.theme.shared";
export const appConfig: ApplicationConfig = {
providers: [

3
docs/en/framework/ui/angular/content-projection-service.md

@ -8,12 +8,13 @@ You do not have to provide the `ContentProjectionService` at module or component
```js
import { ContentProjectionService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private contentProjectionService: ContentProjectionService) {}
private contentProjectionService = inject(ContentProjectionService);
}
```

13
docs/en/framework/ui/angular/dom-insertion-service.md

@ -8,12 +8,13 @@ You do not have to provide the `DomInsertionService` at module or component leve
```js
import { DomInsertionService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private domInsertionService: DomInsertionService) {}
private domInsertionService = inject(DomInsertionService);
}
```
@ -27,12 +28,13 @@ The first parameter of `insertContent` method expects a `ContentStrategy`. If yo
```js
import { DomInsertionService, CONTENT_STRATEGY } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private domInsertionService: DomInsertionService) {}
private domInsertionService = inject(DomInsertionService);
ngOnInit() {
const scriptElement = this.domInsertionService.insertContent(
@ -54,12 +56,13 @@ If you pass a `StyleContentStrategy` instance as the first parameter of `insertC
```js
import { DomInsertionService, CONTENT_STRATEGY } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private domInsertionService: DomInsertionService) {}
private domInsertionService = inject(DomInsertionService);
ngOnInit() {
const styleElement = this.domInsertionService.insertContent(
@ -81,15 +84,15 @@ If you pass the inserted `HTMLScriptElement` or `HTMLStyleElement` element as th
```js
import { DomInsertionService, CONTENT_STRATEGY } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
private domInsertionService = inject(DomInsertionService);
private styleElement: HTMLStyleElement;
constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() {
this.styleElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('body {margin: 0;}')

3
docs/en/framework/ui/angular/environment.md

@ -132,7 +132,8 @@ export const appConfig: ApplicationConfig = {
In order to use the `EnvironmentService` you must inject it in your class as a dependency.
```js
import { EnvironmentService } from '@abp/ng.core';
import { EnvironmentService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */

3
docs/en/framework/ui/angular/features.md

@ -10,12 +10,13 @@ To use the `ConfigStateService`, you must inject it in your class as a dependenc
```js
import { ConfigStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private config: ConfigStateService) {}
private config = inject(ConfigStateService);
}
```

7
docs/en/framework/ui/angular/global-features.md

@ -9,14 +9,14 @@ The `ConfigStateService.getGlobalFeatures` API allows you to get the enabled fea
````js
import { ConfigStateService } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent implements OnInit {
constructor(private config: ConfigStateService) {}
private config = inject(ConfigStateService);
ngOnInit(): void {
// Gets all enabled global features.
const getGlobalFeatures = this.config.getGlobalFeatures();
@ -44,4 +44,3 @@ class DemoComponent implements OnInit {
}
}

38
docs/en/framework/ui/angular/how-replaceable-components-work-with-extensions.md

@ -17,6 +17,10 @@ yarn ng generate component my-roles/my-roles --flat --export
Open the generated `src/app/my-roles/my-roles.component.ts` file and replace its content with the following:
```js
import { Component, Injector, inject, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { finalize } from 'rxjs/operators';
import { ListService, PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { eIdentityComponents, RolesComponent } from '@abp/ng.identity';
import { IdentityRoleDto, IdentityRoleService } from '@abp/ng.identity/proxy';
@ -27,9 +31,6 @@ import {
FormPropData,
generateFormFromProps
} from '@abp/ng.components/extensible';
import { Component, Injector, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'app-my-roles',
@ -40,13 +41,18 @@ import { finalize } from 'rxjs/operators';
provide: EXTENSIONS_IDENTIFIER,
useValue: eIdentityComponents.Roles,
},
{
provide: RolesComponent,
useExisting: MyRolesComponent
}
{
provide: RolesComponent,
useExisting: MyRolesComponent,
},
],
})
export class MyRolesComponent implements OnInit {
public readonly list = inject<ListService<PagedAndSortedResultRequestDto>>(ListService);
protected readonly confirmationService = inject(ConfirmationService);
protected readonly injector = inject(Injector);
protected readonly service = inject(IdentityRoleService);
data: PagedResultDto<IdentityRoleDto> = { items: [], totalCount: 0 };
form: FormGroup;
@ -63,17 +69,10 @@ export class MyRolesComponent implements OnInit {
permissionManagementKey = ePermissionManagementComponents.PermissionManagement;
onVisiblePermissionChange = event => {
onVisiblePermissionChange = (event) => {
this.visiblePermissions = event;
};
constructor(
public readonly list: ListService<PagedAndSortedResultRequestDto>,
protected confirmationService: ConfirmationService,
protected injector: Injector,
protected service: IdentityRoleService,
) {}
ngOnInit() {
this.hookToQuery();
}
@ -253,13 +252,18 @@ export class MyRolesModule {}
As the last step, it is needs to be replaced the `RolesComponent` with the `MyRolesComponent`. Open the `app.component.ts` and modify its content as shown below:
```js
import { Component, inject } from '@angular/core';
import { ReplaceableComponentsService } from '@abp/ng.core';
import { eIdentityComponents } from '@abp/ng.identity';
import { MyRolesComponent } from './my-roles/my-roles.component';
@Component(/* component metadata */)
@Component({
// component metadata
})
export class AppComponent {
constructor(private replaceableComponents: ReplaceableComponentsService) {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({ component: MyRolesComponent, key: eIdentityComponents.Roles });
}
}

13
docs/en/framework/ui/angular/http-error-reporter-service.md

@ -7,13 +7,14 @@ See the example below to learn how to report an error:
```ts
import { HttpErrorReporterService } from '@abp/ng.core';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class SomeService {
constructor(private http: HttpClient, private httpErrorReporter: HttpErrorReporterService) {}
private http = inject(HttpClient);
private httpErrorReporter = inject(HttpErrorReporterService);
getData() {
return this.http.get('http://example.com/get-data').pipe(
@ -31,17 +32,19 @@ See the following example to learn listening the reported errors:
```ts
import { HttpErrorReporterService } from '@abp/ng.core';
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
@Injectable()
export class MyErrorHandler {
constructor(private httpErrorReporter: HttpErrorReporterService) {
private httpErrorReporter = inject(HttpErrorReporterService);
constructor() {
this.handleErrors();
}
handleErrors() {
this.httpErrorReporter.reporter$.subscribe((err: HttpErrorResponse) => {
// handle the errors here
// handle the errors here
});
}
}

3
docs/en/framework/ui/angular/http-requests.md

@ -31,12 +31,13 @@ In order to use the `RestService`, you must inject it in your class as a depende
```js
import { RestService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Injectable({
/* class metadata here */
})
class DemoService {
constructor(private rest: RestService) {}
private rest = inject(RestService);
}
```

25
docs/en/framework/ui/angular/lazy-load-service.md

@ -11,12 +11,13 @@ You do not have to provide the `LazyLoadService` at module or component level, b
```js
import { LazyLoadService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private lazyLoadService: LazyLoadService) {}
private lazyLoadService = inject(LazyLoadService);
}
```
@ -35,6 +36,7 @@ The first parameter of `load` method expects a `LoadingStrategy`. If you pass a
```js
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
template: `
@ -42,11 +44,11 @@ import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
`
})
class DemoComponent {
private lazyLoadService = inject(LazyLoadService);
libraryLoaded$ = this.lazyLoadService.load(
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/some-library.js'),
);
constructor(private lazyLoadService: LazyLoadService) {}
}
```
@ -64,6 +66,7 @@ If you pass a `StyleLoadingStrategy` instance as the first parameter of `load` m
```js
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
template: `
@ -71,11 +74,11 @@ import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
`
})
class DemoComponent {
private lazyLoadService = inject(LazyLoadService);
stylesLoaded$ = this.lazyLoadService.load(
LOADING_STRATEGY.AppendAnonymousStyleToHead('/assets/some-styles.css'),
);
constructor(private lazyLoadService: LazyLoadService) {}
}
```
@ -114,7 +117,8 @@ A common usecase is **loading multiple scripts and/or styles before using a feat
```js
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
import { frokJoin } from 'rxjs';
import { forkJoin } from 'rxjs';
import { inject } from '@angular/core';
@Component({
template: `
@ -122,6 +126,8 @@ import { frokJoin } from 'rxjs';
`
})
class DemoComponent {
private lazyLoad = inject(LazyLoadService);
private stylesLoaded$ = forkJoin(
this.lazyLoad.load(
LOADING_STRATEGY.PrependAnonymousStyleToHead('/assets/library-dark-theme.css'),
@ -141,8 +147,6 @@ class DemoComponent {
);
scriptsAndStylesLoaded$ = forkJoin(this.scriptsLoaded$, this.stylesLoaded$);
constructor(private lazyLoadService: LazyLoadService) {}
}
```
@ -156,6 +160,7 @@ Another frequent usecase is **loading dependent scripts in order**:
```js
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
import { concat } from 'rxjs';
import { inject } from '@angular/core';
@Component({
template: `
@ -163,6 +168,8 @@ import { concat } from 'rxjs';
`
})
class DemoComponent {
private lazyLoad = inject(LazyLoadService);
scriptsLoaded$ = concat(
this.lazyLoad.load(
LOADING_STRATEGY.PrependAnonymousScriptToHead('/assets/library.js'),
@ -171,8 +178,6 @@ class DemoComponent {
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/script-that-requires-library.js'),
),
);
constructor(private lazyLoadService: LazyLoadService) {}
}
```

11
docs/en/framework/ui/angular/list-service.md

@ -12,6 +12,7 @@
import { ListService } from '@abp/ng.core';
import { BookDto } from '../models';
import { BookService } from '../services';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
@ -32,10 +33,10 @@ class BookComponent {
items: BookDto[] = [];
count = 0;
constructor(
public readonly list: ListService,
private bookService: BookService,
) {
public readonly list = inject(ListService);
private bookService = inject(BookService);
constructor() {
// change ListService defaults here
this.list.maxResultCount = 20;
}
@ -78,7 +79,7 @@ You can extend the query parameter of the `ListService`'s `hookToQuery` method.
Firstly, you should pass your own type to `ListService` as shown below:
```typescript
constructor(public readonly list: ListService<BooksSearchParamsDto>) { }
public readonly list = inject(ListService<BooksSearchParamsDto>);
```
Then update the `bookStreamCreator` constant like following:

3
docs/en/framework/ui/angular/localization.md

@ -71,9 +71,10 @@ First of all you should import the `LocalizationService` from **@abp/ng.core**
```js
import { LocalizationService } from '@abp/ng.core';
import { inject } from '@angular/core';
class MyClass {
constructor(private localizationService: LocalizationService) {}
private localizationService = inject(LocalizationService);
}
```

3
docs/en/framework/ui/angular/modal.md

@ -132,12 +132,12 @@ See an example form inside a modal:
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { inject } from '@angular/core';
@Component(/* component metadata */)
export class BookComponent {
private fb = inject(FormBuilder);
private service = inject(BookService);
form = this.fb.group({
author: [null, [Validators.required]],
name: [null, [Validators.required]],
@ -147,7 +147,6 @@ export class BookComponent {
});
inProgress: boolean;
isModalOpen: boolean;
save() {

43
docs/en/framework/ui/angular/modifying-the-menu.md

@ -47,12 +47,14 @@ You can add routes to the menu by calling the `add` method of `RoutesService`. I
```js
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component(/* component metadata */)
export class AppComponent {
constructor(routes: RoutesService) {
routes.add([
private routes = inject(RoutesService);
constructor() {
this.routes.add([
{
path: '/your-path',
name: 'Your navigation',
@ -138,15 +140,16 @@ To get the route items as grouped we can use the `groupedVisible` (or Observable
```js
import { ABP, RoutesService, RouteGroup } from "@abp/ng.core";
import { Component } from "@angular/core";
import { Component, inject } from "@angular/core";
import { Observable } from "rxjs";
@Component(/* component metadata */)
export class AppComponent {
private routes = inject(RoutesService);
visible: RouteGroup<ABP.Route>[] | undefined = this.routes.groupedVisible;
//Or
visible$:Observable<RouteGroup<ABP.Route>[] | undefined> = this.routes.groupedVisible$;
constructor(private routes: RoutesService) {}
// Or
visible$: Observable<RouteGroup<ABP.Route>[] | undefined> = this.routes.groupedVisible$;
}
```
@ -177,12 +180,14 @@ export const appConfig: ApplicationConfig = {
```typescript
import { RoutesService } from '@abp/ng.core';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component(/* component metadata */)
export class AppComponent {
constructor(private routes: RoutesService) {
routes.setSingularizeStatus(false);
private routes = inject(RoutesService);
constructor() {
this.routes.setSingularizeStatus(false);
}
}
```
@ -311,7 +316,7 @@ You can add elements to the right part of the menu by calling the `addItems` met
```js
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component({
template: `
@ -323,8 +328,10 @@ export class MySearchInputComponent {}
@Component(/* component metadata */)
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.addItems([
private navItems = inject(NavItemsService);
constructor() {
this.navItems.addItems([
{
id: 'MySearchInput',
order: 1,
@ -353,13 +360,15 @@ The `patchItem` method of `NavItemsService` finds an element by its `id` propert
```js
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.patchItem(eThemeBasicComponents.Languages, {
private navItems = inject(NavItemsService);
constructor() {
this.navItems.patchItem(eThemeBasicComponents.Languages, {
requiredPolicy: 'new policy here',
order: 1,
});
navItems.removeItem(eThemeBasicComponents.CurrentUser);
this.navItems.removeItem(eThemeBasicComponents.CurrentUser);
}
}
```

3
docs/en/framework/ui/angular/page-alerts.md

@ -8,12 +8,13 @@ You can simply import `PageAlertService` from `@abp/ng.theme.shared` and utilize
```js
import { PageAlertService } from '@abp/ng.theme.shared';
import { inject } from '@angular/core';
@Component({
// ...
})
export class MyComponent {
constructor(private service: PageAlertService) {}
private service = inject(PageAlertService);
showWarning() {
this.service.show({

2
docs/en/framework/ui/angular/page-component.md

@ -175,7 +175,7 @@ export class MyPageRenderStrategy implements PageRenderStrategy {
* shouldRender can also return an Observable<boolean> which means
* an async service can be used within.
constructor(private service: SomeAsyncService) {}
service = inject(SomeAsyncService)
shouldRender(type: string) {
return this.service.checkTypeAsync(type).pipe(map(val => val.isTrue()));

58
docs/en/framework/ui/angular/permission-management-component-replacement.md

@ -19,7 +19,7 @@ import {
import { LocaleDirection } from '@abp/ng.theme.shared';
import {
Component,
EventEmitter, Inject, Input, Optional, Output, TrackByFunction
EventEmitter, Inject, inject, Input, Optional, Output, TrackByFunction
} from '@angular/core';
import { of } from 'rxjs';
import { finalize, switchMap, tap } from 'rxjs/operators';
@ -42,16 +42,18 @@ type PermissionWithStyle = PermissionGrantInfoDto & {
})
export class PermissionManagementComponent
implements
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
private readonly service = inject(PermissionsService);
private readonly configState = inject(ConfigStateService);
protected _providerName: string;
@Input()
get providerName(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerName;
return this._providerName;
}
set providerName(value: string) {
this._providerName = value;
}
@ -60,10 +62,8 @@ export class PermissionManagementComponent
@Input()
get providerKey(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerKey;
return this._providerKey;
}
set providerKey(value: string) {
this._providerKey = value;
}
@ -72,10 +72,8 @@ export class PermissionManagementComponent
@Input()
get hideBadges(): boolean {
if (this.replaceableData) return this.replaceableData.inputs.hideBadges;
return this._hideBadges;
}
set hideBadges(value: boolean) {
this._hideBadges = value;
}
@ -85,7 +83,6 @@ export class PermissionManagementComponent
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
if (value === this._visible) return;
@ -106,15 +103,10 @@ export class PermissionManagementComponent
@Output() readonly visibleChange = new EventEmitter<boolean>();
data: GetPermissionListResultDto = { groups: [], entityDisplayName: null };
selectedGroup: PermissionGroupDto;
permissions: PermissionGrantInfoDto[] = [];
selectThisTab = false;
selectAllTab = false;
modalBusy = false;
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name;
@ -142,7 +134,6 @@ export class PermissionManagementComponent
get isVisible(): boolean {
if (!this.replaceableData) return this.visible;
return this.replaceableData.inputs.visible;
}
@ -152,9 +143,7 @@ export class PermissionManagementComponent
public replaceableData: ReplaceableComponents.ReplaceableTemplateData<
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs
>,
private service: PermissionsService,
private configState: ConfigStateService
>
) {}
getChecked(name: string) {
@ -162,17 +151,11 @@ export class PermissionManagementComponent
}
isGrantedByOtherProviderName(grantedProviders: ProviderInfoDto[]): boolean {
if (grantedProviders.length) {
return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1;
}
return false;
return grantedProviders?.some(p => p.providerName !== this.providerName);
}
onClickCheckbox(clickedPermission: PermissionGrantInfoDto, value) {
if (
clickedPermission.isGranted &&
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders)
)
if (clickedPermission.isGranted && this.isGrantedByOtherProviderName(clickedPermission.grantedProviders))
return;
setTimeout(() => {
@ -184,7 +167,6 @@ export class PermissionManagementComponent
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true };
}
return per;
});
@ -255,16 +237,11 @@ export class PermissionManagementComponent
this.setTabCheckboxState();
}
submit() {
const unchangedPermissions = getPermissions(this.data.groups);
const changedPermissions: UpdatePermissionDto[] = this.permissions
.filter(per =>
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted ===
per.isGranted
? false
: true,
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted !== per.isGranted
)
.map(({ name, isGranted }) => ({ name, isGranted }));
@ -321,26 +298,23 @@ export class PermissionManagementComponent
this.replaceableData.outputs.visibleChange(visible);
}
}
shouldFetchAppConfig() {
const currentUser = this.configState.getOne('currentUser') as CurrentUserDto;
if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey);
if (this.providerName === 'U') return currentUser.id === this.providerKey;
return false;
}
}
function findMargin(permissions: PermissionGrantInfoDto[], permission: PermissionGrantInfoDto) {
function findMargin(permissions: PermissionGrantInfoDto[], permission: PermissionGrantInfoDto): number {
const parentPermission = permissions.find(per => per.name === permission.parentName);
if (parentPermission && parentPermission.parentName) {
let margin = 20;
return (margin += findMargin(permissions, parentPermission));
return margin + findMargin(permissions, parentPermission);
}
return parentPermission ? 20 : 0;
}
@ -461,12 +435,12 @@ Open `app.component.ts` in `src/app` folder and modify it as shown below:
```js
import { ReplaceableComponentsService } from '@abp/ng.core';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { PermissionManagementComponent } from './permission-management/permission-management.component';
//...
export class AppComponent implements OnInit {
constructor(private replaceableComponents: ReplaceableComponentsService) {} // injected ReplaceableComponentsService
private readonly replaceableComponents = inject(ReplaceableComponentsService); // injected ReplaceableComponentsService
ngOnInit() {
this.replaceableComponents.add({

9
docs/en/framework/ui/angular/permission-management.md

@ -8,9 +8,10 @@ You can get permission as boolean value:
```js
import { PermissionService } from '@abp/ng.core';
import { inject } from '@angular/core';
export class YourComponent {
constructor(private permissionService: PermissionService) {}
private permissionService = inject(PermissionService);
ngOnInit(): void {
const canCreate = this.permissionService.getGrantedPolicy('AbpIdentity.Roles.Create');
@ -83,14 +84,14 @@ In some cases, a custom permission management may be needed. All you need to do
```js
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class CustomPermissionService extends PermissionService {
constructor(configStateService: ConfigStateService) {
super(configStateService);
constructor() {
super(inject(ConfigStateService));
}
// This is an example to show how to override the methods

29
docs/en/framework/ui/angular/router-events.md

@ -14,9 +14,12 @@ import {
Router,
} from '@angular/router';
import { filter } from 'rxjs/operators';
import { inject, Injectable } from '@angular/core';
@Injectable()
class SomeService {
private router = inject(Router);
navigationFinish$ = this.router.events.pipe(
filter(
event =>
@ -26,8 +29,6 @@ class SomeService {
),
);
/* Observable<Event> */
constructor(private router: Router) {}
}
```
@ -38,10 +39,10 @@ import { RouterEvents } from '@abp/ng.core';
@Injectable()
class SomeService {
private routerEvents = inject(RouterEvents);
navigationFinish$ = this.routerEvents.getNavigationEvents('End', 'Error', 'Cancel');
/* Observable<NavigationCancel | NavigationEnd | NavigationError> */
constructor(private routerEvents: RouterEvents) {}
}
```
@ -62,6 +63,8 @@ import { mapTo } from 'rxjs/operators';
@Injectable()
class SomeService {
private routerEvents = inject(RouterEvents);
navigationStart$ = this.routerEvents.getNavigationEvents('Start');
/* Observable<NavigationStart> */
@ -73,8 +76,6 @@ class SomeService {
this.navigationFinish$.pipe(mapTo(false)),
);
/* Observable<boolean> */
constructor(private routerEvents: RouterEvents) {}
}
```
@ -88,6 +89,8 @@ import { map } from 'rxjs/operators';
@Injectable()
class SomeService {
private routerEvents = inject(RouterEvents);
navigationEvent$ = this.routerEvents.getAllNavigationEvents();
/* Observable<NavigationCancel | NavigationEnd | NavigationError | NavigationStart> */
@ -95,8 +98,6 @@ class SomeService {
map(event => event instanceof NavigationStart),
);
/* Observable<boolean> */
constructor(private routerEvents: RouterEvents) {}
}
```
@ -127,7 +128,7 @@ class SomeService {
### How to Get Specific Router Events
You can use `getEvents` to get a stream of router events matching given event constructors.
You can use `getEvents` to get a stream of router events matching given event classes.
```js
import { RouterEvents } from '@abp/ng.core';
@ -135,16 +136,16 @@ import { ActivationEnd, ChildActivationEnd } from '@angular/router';
@Injectable()
class SomeService {
private routerEvents = inject(RouterEvents);
moduleActivation$ = this.routerEvents.getEvents(ActivationEnd, ChildActivationEnd);
/* Observable<ActivationEnd | ChildActivationEnd> */
constructor(private routerEvents: RouterEvents) {}
}
```
### How to Get All Router Events
You can use `getEvents` to get a stream of all router events without passing any event constructors. This is nothing different from accessing `events` property of `Router` and is added to the service just for convenience.
You can use `getEvents` to get a stream of all router events without passing any event classes. This is nothing different from accessing `events` property of `Router` and is added to the service just for convenience.
```js
import { RouterEvents } from '@abp/ng.core';
@ -152,9 +153,9 @@ import { ActivationEnd, ChildActivationEnd } from '@angular/router';
@Injectable()
class SomeService {
private routerEvents = inject(RouterEvents);
routerEvent$ = this.routerEvents.getAllEvents();
/* Observable<Event> */
constructor(private routerEvents: RouterEvents) {}
}
```

5
docs/en/framework/ui/angular/service-proxies.md

@ -89,14 +89,15 @@ The `generate-proxy` command generates one service per back-end controller and a
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's `RemoteServiceName`. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./http-requests#how-to-get-a-specific-api-endpoint-from-application-config)
The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting them into the constructor as shown below:
The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting as shown below:
```js
import { BookService } from '@proxy/books';
import { inject } from '@angular/core';
@Component(/* component metadata here */)
export class BookComponent implements OnInit {
constructor(private service: BookService) {}
private service = inject(BookService);
ngOnInit() {
this.service.get().subscribe(

3
docs/en/framework/ui/angular/settings.md

@ -10,12 +10,13 @@ To use the `ConfigStateService`, you must inject it in your class as a dependenc
```js
import { ConfigStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private config: ConfigStateService) {}
private config = inject(ConfigStateService);
}
```

21
docs/en/framework/ui/angular/subscription-service.md

@ -8,6 +8,7 @@ You have to provide the `SubscriptionService` at component or directive level, b
```js
import { SubscriptionService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
@ -16,7 +17,9 @@ import { SubscriptionService } from '@abp/ng.core';
class DemoComponent {
count$ = interval(1000);
constructor(private subscription: SubscriptionService) {
private subscription = inject(SubscriptionService);
constructor() {
this.subscription.addOne(this.count$, console.log);
}
}
@ -38,7 +41,7 @@ You can pass a `next` function and an `error` function.
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
const source$ = interval(1000);
@ -61,7 +64,7 @@ Or, you can pass an observer.
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
const source$ = interval(1000);
@ -87,7 +90,7 @@ There are two ways to do that. If you are not going to subscribe again, you may
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);
@ -107,7 +110,7 @@ This will clear all subscriptions, but you will not be able to subscribe again.
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);
@ -131,8 +134,7 @@ Sometimes, you may need to unsubscribe from a particular subscription but leave
})
class DemoComponent implements OnInit {
countSubscription: Subscription;
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
this.countSubscription = this.subscription.addOne(
@ -159,8 +161,7 @@ You may want to take control of a particular subscription. In such a case, you m
})
class DemoComponent implements OnInit {
countSubscription: Subscription;
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
this.countSubscription = this.subscription.addOne(
@ -186,7 +187,7 @@ Please use `isClosed` getter to check if `closeAll` was called before.
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
private subscription = inject(SubscriptionService);
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);

45
docs/en/framework/ui/angular/theming.md

@ -105,12 +105,14 @@ The [Application Startup Template](../../../solution-templates/application-modul
```ts
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component(/* component metadata */)
export class AppComponent {
constructor(routes: RoutesService) {
routes.add([
private routes = inject(RoutesService);
constructor() {
this.routes.add([
{
path: '/your-path',
name: 'Your navigation',
@ -141,7 +143,7 @@ See the [Modifying the Menu](modifying-the-menu.md) document to learn more about
````ts
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component({
template: `
@ -153,8 +155,10 @@ export class MySearchInputComponent {}
@Component(/* component metadata */)
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.addItems([
private navItems = inject(NavItemsService);
constructor() {
this.navItems.addItems([
{
id: 'MySearchInput',
order: 1,
@ -184,25 +188,25 @@ Language Selection toolbar item is generally a dropdown that is used to switch b
**Example: Get the currently selected language**
````ts
import {SessionStateService} from '@abp/ng.core';
import { SessionStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
//...
constructor(private sessionState: SessionStateService) {
const lang = this.sessionState.getLanguage()
}
const sessionState = inject(SessionStateService);
const lang = sessionState.getLanguage();
````
**Example: Set the selected language**
````ts
import {SessionStateService} from '@abp/ng.core';
import { SessionStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
//...
constructor(private sessionState: SessionStateService) {
const lang = this.sessionState.setLanguage('en')
}
const sessionState = inject(SessionStateService);
sessionState.setLanguage('en');
````
@ -219,7 +223,7 @@ All of the options are shown below. You can choose either of them.
````ts
import { eUserMenuItems } from '@abp/ng.theme.basic';
import { UserMenuService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
// make sure that you import this component in a NgModule
@ -234,20 +238,23 @@ import { Router } from '@angular/router';
})
export class UserMenuItemComponent {
// you can inject the data through `INJECTOR_PIPE_DATA_TOKEN` token
constructor(@Inject(INJECTOR_PIPE_DATA_TOKEN) public data: UserMenu) {}
public data = inject<UserMenu>(INJECTOR_PIPE_DATA_TOKEN);
}
@Component({/* component metadata */})
export class AppComponent {
constructor(private userMenu: UserMenuService, private router: Router) {
private userMenu = inject(UserMenuService);
private router = inject(Router);
constructor() {
this.userMenu.addItems([
{
id: 'UserMenu.MyAccount',
order: 1,
// HTML example
html: `<a class="dropdown-item pointer">My account</a>`,
// text template example
textTemplate: {
text: 'AbpAccount::MyAccount',

35
docs/en/framework/ui/angular/toaster-service.md

@ -8,12 +8,13 @@ You do not have to provide the `ToasterService` at component level, because it i
```js
import { ToasterService } from '@abp/ng.theme.shared';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent {
constructor(private toaster: ToasterService) {}
private toaster = inject(ToasterService);
}
```
@ -36,22 +37,23 @@ Options can be passed as the third parameter to `success`, `warn`, `error`, and
```js
import { Toaster, ToasterService } from '@abp/ng.theme.shared';
//...
import { inject } from '@angular/core';
constructor(private toaster: ToasterService) {}
// inside the class
private toaster = inject(ToasterService);
//...
const options: Partial<Toaster.ToastOptions> = {
life: 10000,
sticky: false,
closable: true,
tapToDismiss: true,
messageLocalizationParams: ['Demo', '1'],
titleLocalizationParams: [],
iconClass: 'custom-icon-name';
};
this.toaster.error('AbpUi::EntityNotFoundErrorMessage', 'AbpUi::Error', options);
life: 10000,
sticky: false,
closable: true,
tapToDismiss: true,
messageLocalizationParams: ['Demo', '1'],
titleLocalizationParams: [],
iconClass: 'custom-icon-name'
};
this.toaster.error('AbpUi::EntityNotFoundErrorMessage', 'AbpUi::Error', options);
```
- `life` option is the closing time in milliseconds. Default value is `5000`.
@ -93,15 +95,16 @@ If you want the ABP to utilize 3rd party libraries for the toasters instead of t
```js
// your-custom-toaster.service.ts
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { Config, LocalizationService } from '@abp/ng.core';
import { Toaster } from '@abp/ng.theme.shared';
import { ToastrService } from 'ngx-toastr';
@Injectable()
export class CustomToasterService implements Toaster.Service {
constructor(private toastr: ToastrService, private localizationService: LocalizationService) {}
private toastr = inject(ToastrService);
private localizationService = inject(LocalizationService)
error(
message: Config.LocalizationParam,
title?: Config.LocalizationParam,

3
docs/en/framework/ui/angular/track-by-service.md

@ -10,6 +10,7 @@ You do not have to provide the `TrackByService` at module or component level, be
```js
import { TrackByService } from '@abp/ng.core';
import { inject } from '@angular/core';
@Component({
/* class metadata here */
@ -17,7 +18,7 @@ import { TrackByService } from '@abp/ng.core';
class DemoComponent {
list: Item[];
constructor(public readonly track: TrackByService<Item>) {}
public readonly track = inject(TrackByService<Item>);
}
```

262
docs/en/framework/ui/react-native/index.md

@ -1,7 +1,7 @@
````json
//[doc-params]
{
"Tiered": ["No", "Yes"]
"Architecture": ["Monolith", "Tiered", "Microservice"]
}
````
@ -20,15 +20,23 @@ Please follow the steps below to prepare your development environment for React
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v20.11+ installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
2. **[Optional] Install Yarn:** You may install Yarn v1 (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v6 and below. You may skip this step and work with npm, which is built-in in Node.js, instead.
3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to React Native projects.
4. **Install an Emulator:** React Native applications need an Android emulator or an iOS simulator to run on your OS. See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/) or [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) on expo.io documentation to learn how to set up an emulator.
4. **Install an Emulator/Simulator:** React Native applications need an Android emulator or an iOS simulator to run on your OS. If you do not have Android Studio installed and configured on your system, we recommend [setting up android emulator without android studio](setting-up-android-emulator.md).
If you prefer the other way, you can check the [Android Studio Emulator](https://docs.expo.dev/workflow/android-studio-emulator/) or [iOS Simulator](https://docs.expo.dev/workflow/ios-simulator/) on expo.io documentation to learn how to set up an emulator.
## How to Start a New React Native Project
You have multiple options to initiate a new React Native project that works with ABP:
### 1. Using ABP CLI
### 1. Using ABP Studio
ABP Studio application is the most convenient and flexible way to initiate a React Native application based on ABP framework. You can follow the [tool documentation](../../../studio) and select the option below:
![React Native option](../../../images/react-native-option.png)
### 2. Using ABP CLI
ABP CLI is probably the most convenient and flexible way to initiate an ABP solution with a React Native application. Simply [install the ABP CLI](../../../cli) and run the following command in your terminal:
ABP CLI is another way of creating an ABP solution with a React Native application. Simply [install the ABP CLI](../../../cli) and run the following command in your terminal:
```shell
abp new MyCompanyName.MyProjectName -csf -u <angular or mvc> -m react-native
@ -38,33 +46,209 @@ abp new MyCompanyName.MyProjectName -csf -u <angular or mvc> -m react-native
This command will prepare a solution with an **Angular** or an **MVC** (depends on your choice), a **.NET Core**, and a **React Native** project in it.
### 2. Generating a CLI Command from Get Started Page
You can generate a CLI command on the [get started page of the abp.io website](https://abp.io/get-started). Then, use the command on your terminal to create a new [Startup Template](../../../solution-templates).
## How to Configure & Run the Backend
> React Native application does not trust the auto-generated .NET HTTPS certificate. You should use **HTTP** during the development.
> When you are using OpenIddict, You should remove 'clientSecret' on Environment.js (if exists) and disable "HTTPS-only" settings. (Openiddict has default since Version 6.0)
A React Native application running on an Android emulator or a physical phone **can not connect to the backend** on `localhost`. To fix this problem, it is necessary to run the backend application on your **local IP address**.
{{ if Tiered == "No"}}
![React Native host project local IP entry](../../../images/rn-host-local-ip.png)
- Open the `appsettings.json` file in the `.HttpApi.Host` folder. Replace the `localhost` address on the `SelfUrl` and `Authority` properties with your local IP address.
- Open the `launchSettings.json` file in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](../../../images/rn-tiered-local-ip.png)
- Open the `appsettings.json` file in the `.AuthServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
- Open the `launchSettings.json` file in the `.AuthServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
- Open the `appsettings.json` file in the `.HttpApi.Host` folder. Replace the `localhost` address on the `Authority` property with your local IP address.
- Open the `launchSettings.json` file in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
A React Native application running on an Android emulator or a physical phone **can not connect to the backend** on `localhost`. To fix this problem, it is necessary to run the backend application using the `Kestrel` configuration.
{{ if Architecture == "Monolith" }}
![React Native monolith host project configuration](../../../images/react-native-monolith-be-config.png)
- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address on the `RootUrl` property with your local IP address. Then, run the database migrator.
- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests just to test the react native application on the development environment.
```json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:44323" //replace with your host port
}
}
}
}
```
{{ else if Architecture == "Tiered" }}
![React Native tiered project configuration](../../../images/react-native-tiered-be-config.png)
- Open the `appsettings.json` file in the `.DbMigrator` folder. Replace the `localhost` address on the `RootUrl` property with your local IP address. Then, run the database migrator.
- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests just to test the react native application on the development environment.
```json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:44337"
}
}
}
}
```
- Open the `appsettings.Development.json` file in the `.HttpApi.Host` folder. Add this configuration to accept global requests again. In addition, you will need to introduce the authentication server as mentioned above.
```json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:44389" //replace with your host port
}
}
},
"AuthServer": {
"Authority": "http://192.168.1.37:44337/", //replace with your IP and authentication port
"MetaAddress": "http://192.168.1.37:44337/",
"RequireHttpsMetadata": false,
"Audience": "MyTieredProject" //replace with your application name
}
}
```
{{ else if Architecture == "Microservice" }}
![React Native microservice project configuration](../../../images/react-native-microservice-be-config.png)
- Open the `appsettings.Development.json` file in the `.AuthServer` folder. Add this configuration to accept global requests just to test the react native application on the development environment.
```json
{
"App": {
"EnablePII": true
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:44319"
}
}
}
}
```
- Open the `appsettings.Development.json` file in the `.AdministrationService` folder. Add this configuration to accept global requests just to test the react native application on the development environment. You should also provide the authentication server configuration. In addition, you need to apply the same process for all the services you would use in the react native application.
```json
{
"App": {
"EnablePII": true
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:44357"
}
}
},
"AuthServer": {
"Authority": "http://192.168.1.36:44319/",
"MetaAddress": "http://192.168.1.36:44319/",
"RequireHttpsMetadata": false,
"Audience": "AdministrationService"
}
}
```
- Update the `appsettings.json` file in the `.IdentityService` folder. Replace the `localhost` configuration with your local IP address for the react native application
```json
{
//...
"OpenIddict": {
"Applications": {
//...
"ReactNative": {
"RootUrl": "exp://192.168.1.36:19000"
},
"MobileGateway": {
"RootUrl": "http://192.168.1.36:44347/"
},
//...
},
//...
}
}
```
- Lastly, update the mobile gateway configurations as following:
```json
//gateways/mobile/MyMicroserviceProject.MobileGateway/Properties/launchSettings.json
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://192.168.1.36:44347" //update with your IP address
}
},
"profiles": {
//...
"MyMicroserviceProject.MobileGateway": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "http://192.168.1.36:44347",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
```
```json
//gateways/mobile/MyMicroserviceProject.MobileGateway/appsettings.json
{
//Update clusters with your IP address
//...
"ReverseProxy": {
//...
"Clusters": {
"AuthServer": {
"Destinations": {
"AuthServer": {
"Address": "http://192.168.1.36:44319/"
}
}
},
"Administration": {
"Destinations": {
"Administration": {
"Address": "http://192.168.1.36:44357/"
}
}
},
"Saas": {
"Destinations": {
"Saas": {
"Address": "http://192.168.1.36:44330/"
}
}
},
"Identity": {
"Destinations": {
"Identity": {
"Address": "http://192.168.1.36:44397/"
}
}
},
"Language": {
"Destinations": {
"Identity": {
"Address": "http://192.168.1.36:44310/"
}
}
}
}
}
}
```
{{ end }}
Run the backend application as described in the [getting started document](../../../get-started).
@ -73,7 +257,7 @@ Run the backend application as described in the [getting started document](../..
## How to disable the Https-only settings of OpenIddict
Open the {{ if Tiered == "No" }}`MyProjectNameHttpApiHostModule`{{ else if Tiered == "Yes" }}`MyProjectNameAuthServerModule`{{ end }} project and copy-paste the below code-block to the `PreConfigureServices` method:
Open the {{ if Architecture == "Monolith" }}`MyProjectNameHttpApiHostModule`{{ if Architecture == "Tiered" }}`MyProjectNameAuthServerModule`{{ end }} project and copy-paste the below code-block to the `PreConfigureServices` method:
```csharp
#if DEBUG
@ -89,21 +273,27 @@ Open the {{ if Tiered == "No" }}`MyProjectNameHttpApiHostModule`{{ else if Tiere
1. Make sure the [database migration is complete](../../../get-started?UI=NG&DB=EF&Tiered=No#create-the-database) and the [API is up and running](../../../get-started?UI=NG&DB=EF&Tiered=No#run-the-application).
2. Open `react-native` folder and run `yarn` or `npm install` if you have not already.
3. Open the `Environment.js` in the `react-native` folder and replace the `localhost` address on the `apiUrl` and `issuer` properties with your local IP address as shown below:
3. Open the `Environment.ts` in the `react-native` folder and replace the `localhost` address on the `apiUrl` and `issuer` properties with your local IP address as shown below:
{{ if Architecture == "Monolith" }}
![react native environment local IP](../../../images/rn-environment-local-ip.png)
![react native monolith environment local IP](../../../images/react-native-monolith-environment-local-ip.png)
{{ if Tiered == "Yes" }}
{{ else if Architecture == "Tiered" }}
![react native tiered environment local IP](../../../images/react-native-tiered-environment-local-ip.png)
> Make sure that `issuer` matches the running address of the `.AuthServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
{{else}}
{{ else }}
![react native microservice environment local IP](../../../images/react-native-microservice-environment-local-ip.png)
> Make sure that `issuer` and `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
> Make sure that `issuer` matches the running address of the `.AuthServer` project, `apiUrl` matches the running address of the `.AuthServer` project.
{{ end }}
4. Run `yarn start` or `npm start`. Wait for the Expo CLI to print the opitons.
1. Run `yarn start` or `npm start`. Wait for the Expo CLI to print the opitons.
> The React Native application was generated with [Expo](https://expo.io/). Expo is a set of tools built around React Native to help you quickly start an app and, while it has many features.
@ -113,14 +303,14 @@ In the above image, you can start the application with an Android emulator, an i
### Expo
![React Native login screen on iPhone 11](../../../images/rn-login-iphone.png)
![React Native login screen on iPhone 16](../../../images/rn-login-iphone.png)
### Android Studio
1. Start the emulator in **Android Studio** before running the `yarn start` or `npm start` command.
2. Press **a** to open in Android Studio.
![React Native login screen on iPhone 11](../../../images/rn-login-android-studio.png)
![React Native login screen on Android Device](../../../images/rn-login-android-studio.png)
Enter **admin** as the username and **1q2w3E\*** as the password to login to the application.

127
docs/en/framework/ui/react-native/setting-up-android-emulator.md

@ -0,0 +1,127 @@
# Setting Up Android Emulator Without Android Studio (Windows, macOS, Linux)
This guide explains how to install and run an Android emulator **without Android Studio**, using only **Command Line Tools**.
---
## 1. Download Required Tools
Go to: [https://developer.android.com/studio#command-tools](https://developer.android.com/studio#command-tools)
Download the "Command line tools only" package for your OS:
- **Windows:** `commandlinetools-win-*.zip`
- **macOS:** `commandlinetools-mac-*.zip`
- **Linux:** `commandlinetools-linux-*.zip`
---
## 2. Create the Required Directory Structure
### Windows:
```
C:\Android\
└── cmdline-tools\
└── latest\
└── [extract all files from the zip here]
```
### macOS / Linux:
```
~/Android/
└── cmdline-tools/
└── latest/
└── [extract all files from the zip here]
```
> You need to create the `latest` folder manually.
---
## 3. Set Environment Variables
### Windows (temporary for CMD session):
```cmd
set PATH=C:\Android\cmdline-tools\latest\bin;C:\Android\platform-tools;C:\Android\emulator;%PATH%
```
### macOS / Linux:
Add the following to your `.bashrc`, `.zshrc`, or `.bash_profile` file:
```bash
export ANDROID_HOME=$HOME/Android
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/emulator
```
> Apply the changes:
```bash
source ~/.zshrc # or ~/.bashrc if you're using bash
```
---
## 4. Install SDK Components
Install platform tools, emulator, and a system image:
```bash
sdkmanager --sdk_root=$ANDROID_HOME "platform-tools" "platforms;android-34" "system-images;android-34;google_apis;x86_64" "emulator"
```
> On Windows, replace `$ANDROID_HOME` with `--sdk_root=C:\Android`.
---
## 5. Create an AVD (Android Virtual Device)
### List available devices:
```bash
avdmanager list devices
```
### Create your AVD:
```bash
avdmanager create avd -n myEmu -k "system-images;android-34;google_apis;x86_64" --device "pixel"
```
---
## 6. Start the Emulator
```bash
emulator -avd myEmu
```
The emulator window should open
---
## Extra Tools and Commands
### List connected devices with ADB:
```bash
adb devices
```
### Install an APK:
```bash
adb install myApp.apk
```
---
## Troubleshooting
| Problem | Explanation |
|--------|-------------|
| `sdkmanager not found` | Make sure `PATH` includes the `latest/bin` directory |
| `x86_64 system image not found` | Make sure you've downloaded it using `sdkmanager` |
| `emulator not found` | Add the `emulator` directory to `PATH` |
| `setx` truncates path (Windows) | Use GUI to update environment variables manually |
---
## Summary
You can now run an Android emulator without installing Android Studio, entirely through the command line. This emulator can be used for React Native or any mobile development framework.

BIN
docs/en/get-started/images/abp-studio-mobile-sample.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 5.4 MiB

5
docs/en/get-started/layered-web-application.md

@ -242,6 +242,8 @@ You can use `admin` as username and `1q2w3E*` as default password to login to th
> Note: If you haven't selected a mobile framework, you can skip this step.
Before starting the mobile application, ensure that you have configured it for [react-native](../framework/ui/react-native) or [MAUI](../framework/ui/maui).
You can start the following application(s):
{{ if Tiered == "Yes" }}
@ -255,8 +257,7 @@ You can start the following application(s):
{{ else }}
- `Acme.BookStore.Web`
{{ end }}
Before starting the mobile application, ensure that you configure it for [react-native](../framework/ui/react-native) or [MAUI](../framework/ui/maui).
- `react-native` or `Acme.Bookstore.Maui`
![mobile-sample](images/abp-studio-mobile-sample.gif)

BIN
docs/en/images/author-input-in-book-form.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/images/author-list-with-options.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 83 KiB

BIN
docs/en/images/author-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/en/images/authors-in-book-form.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/en/images/book-list-with-author.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 77 KiB

BIN
docs/en/images/book-list-with-options.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/images/book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 74 KiB

BIN
docs/en/images/book-store-menu-item.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/en/images/books-menu-item.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/en/images/create-author.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/images/create-book-button-visibility.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
docs/en/images/create-book-icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/en/images/create-book.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/en/images/delete-author-alert.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/en/images/delete-book-alert.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/en/images/delete-book.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 84 KiB

BIN
docs/en/images/react-native-environment-local-ip.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
docs/en/images/react-native-introduction.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 KiB

After

Width:  |  Height:  |  Size: 4.9 MiB

BIN
docs/en/images/react-native-microservice-be-config.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
docs/en/images/react-native-monolith-be-config.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
docs/en/images/react-native-monolith-environment-local-ip.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/images/react-native-option.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
docs/en/images/react-native-store-folder.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/images/react-native-tiered-be-config.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
docs/en/images/react-native-tiered-environment-local-ip.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/en/images/rn-login-android-studio.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/images/rn-login-iphone.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 682 KiB

BIN
docs/en/images/update-author.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 664 KiB

BIN
docs/en/images/update-book.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 669 KiB

BIN
docs/en/images/update-delete-book-button-visibility.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 98 KiB

18
docs/en/modules/setting-management.md

@ -289,16 +289,18 @@ yarn ng generate component my-settings
Open the `app.component.ts` and modify the file as shown below:
```js
import { Component } from '@angular/core';
import { SettingTabsService } from '@abp/ng.setting-management/config'; // imported SettingTabsService
import { MySettingsComponent } from './my-settings/my-settings.component'; // imported MySettingsComponent
import { Component, inject } from '@angular/core';
import { SettingTabsService } from '@abp/ng.setting-management/config';
import { MySettingsComponent } from './my-settings/my-settings.component';
@Component(/* component metadata */)
@Component({
// component metadata
})
export class AppComponent {
constructor(private settingTabs: SettingTabsService) // injected MySettingsComponent
{
// added below
settingTabs.add([
private readonly settingTabs = inject(SettingTabsService);
constructor() {
this.settingTabs.add([
{
name: 'MySettings',
order: 1,

86
docs/en/release-info/migration-guides/abp-10-0.md

@ -0,0 +1,86 @@
# ABP Version 10.0 Migration Guide
This document is a guide for upgrading ABP v9.x solutions to ABP v10.0. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application.
## Open-Source (Framework)
### Upgraded to .NET 10.0
We've upgraded ABP to .NET 10.0, so you need to move your solutions to .NET 10.0 if you want to use ABP 10.0. You can check Microsoft’s [Migrate from ASP.NET Core 9.0 to 10.0](https://learn.microsoft.com/en-us/aspnet/core/migration/90-to-100) documentation, to see how to update an existing ASP.NET Core 9.0 project to ASP.NET Core 10.0.
### Razor Runtime Compilation Obsolete
We removed the Razor Runtime Compilation support.
For more information, you can check the [Razor Runtime Compilation Obsolete](https://learn.microsoft.com/en-us/dotnet/core/compatibility/aspnet-core/10/razor-runtime-compilation-obsolete) page.
### IActionContextAccessor Obsolete
We removed the `IActionContextAccessor` from dependency injection.
> We are not using `IActionContextAccessor` in ABP core framework and modules.
See [IActionContextAccessor Obsolete](https://learn.microsoft.com/en-us/dotnet/core/compatibility/aspnet-core/10/iactioncontextaccessor-obsolete) for more information.
### Add `BLOB Storing Memory Provider` module.
In this version, we added the `BLOB Storing Memory Provider` module for unit testing purposes.
See the [BLOB Storing Memory Provider](https://github.com/abpframework/abp/blob/dev/docs/en/framework/infrastructure/blob-storing/memory.md) document for more information.
### Always use `MapStaticAssets`
Provious, the `MapStaticAssets` has performance problems if there are too many static files. and we will use `StaticFileMiddleware` to serve the static files in development mode. NET 10.0 has fixed this problem. We will always use `MapStaticAssets` to serve the static files.
See [Static file serving performance issues](https://github.com/dotnet/aspnetcore/issues/59673) for more information.
### C# 14 overload resolution with span parameters
NET Core will redirect `array.Contains` extension method to `MemoryExtensions.Contains`, This will cause `MongoDB.Driver` to be unable to translate `IQueryable<T>`, If you are using `array.Contains` in your code, you should use following code to avoid this problem.
> Only array has this problem, other types are not affected. eg List<T>, HashSet<T>, etc.
> MongoDB.Driver will be fixed in the future.
```csharp
M((array, num) => array.Contains(num)); // fails, binds to MemoryExtensions.Contains
M((array, num) => ((IEnumerable<int>)array).Contains(num)); // ok, binds to Enumerable.Contains
M((array, num) => array.AsEnumerable().Contains(num)); // ok, binds to Enumerable.Contains
M((array, num) => Enumerable.Contains(array, num)); // ok, binds to Enumerable.Contains
```
See the [C# 14 overload resolution with span parameters](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/10.0/csharp-overload-resolution#recommended-action) for more information.
### OpenIddict 7.X
We upgraded OpenIddict to 7.X. See the [OpenIddict 6.x to 7.x Migration Guide](https://documentation.openiddict.com/guides/migration/60-to-70.html) for more information.
OpenIddict 7.X changed the `OpenIddictToken` entity, you must create a new database migration if you use Entity Framework Core.
### Migrating from AutoMapper to Mapperly
The AutoMapper library is no longer free for commercial use. For more details, you can refer to this [announcement post](https://www.jimmybogard.com/automapper-and-mediatr-going-commercial/).
In this version, all ABP modules use Mapperly instead of AutoMapper. ABP Framework provides both AutoMapper and Mapperly integrations. If your project currently uses AutoMapper and you don't have a commercial license, you can follow the [Migrating from AutoMapper to Mapperly](https://github.com/abpframework/abp/blob/dev/docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md) document to migrate to Mapperly.
### Failure Retry Policy for InboxProcessor
We added a failure retry policy to `AbpEventBusBoxesOptions` (see `InboxProcessorFailurePolicy` and `InboxProcessorRetryBackoffFactor`). Because this change adds and removes fields in the `IncomingEventRecord` entity, you must create a new database migration if you use Inbox/Outbox with Entity Framework Core.
* `InboxProcessorFailurePolicy`: The policy to handle the failure of the inbox processor. Default value is `Retry`. Possible values are:
* `Retry`: The current exception and subsequent events will continue to be processed in order in the next cycle.
* `RetryLater`: Skip the event that caused the exception and continue with the following events. The failed event will be retried after a delay that doubles with each retry, starting from the configured `InboxProcessorRetryBackoffFactor` (e.g., 10, 20, 40, 80 seconds). The default maximum retry count is 10 (configurable). Discard the event if it still fails after reaching the maximum retry count.
* `Discard`: The event that caused the exception will be discarded and will not be retried.
* `InboxProcessorRetryBackoffFactor`: The initial retry delay factor (double) used when `InboxProcessorFailurePolicy` is `RetryLater`. The retry delay is calculated as: `delay = InboxProcessorRetryBackoffFactor × 2^retryCount`. Default value is `10`.
See the [Add failure retry policy to InboxProcessor](https://github.com/abpframework/abp/pull/23563) PR for more information.
### Disable Cache Error Hiding in Development Environment
Starting from **ABP 10.0**, the [`HideErrors`](../../framework/fundamentals/caching#Available-Options) option of `AbpDistributedCacheOptions` is **disabled by default in the development environment**.
By default, ABP hides and logs cache server errors to keep the application running even when the cache is unavailable.
However, in the **development environment**, errors are no longer hidden so that developers can immediately detect and fix **any cache server issues** (such as connection, configuration, or runtime errors).
## PRO

1
docs/en/release-info/migration-guides/index.md

@ -2,6 +2,7 @@
The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../upgrading.md).
- [9.x to 10.0](abp-10-0.md)
- [9.2 to 9.3](abp-9-3.md)
- [9.x to 9.2](abp-9-2.md)
- [9.0 to 9.1](abp-9-1.md)

40
docs/en/solution-templates/layered-web-application/deployment/deployment-docker-compose.md

@ -204,8 +204,8 @@ DbMigrator is a console application that is used to migrate the database of your
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "BookStore.DbMigrator.dll"]
```
@ -273,8 +273,8 @@ The `appsettings.json` file does not contain `AuthServer:IsOnK8s` and `AuthServe
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.Web.dll"]
```
@ -289,8 +289,8 @@ docker build -f Dockerfile.local -t acme/bookstore-web:latest . #Builds the imag
​ {{ end }} {{ if Tiered == "No" }}MVC/Razor Pages application is a server-side rendering application that contains both the OpenID-provider and the Http.Api endpoints within self; it will be a single application to deploy. `Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
@ -397,8 +397,8 @@ The `appsettings.json` file does not contain `AuthServer:IsOnK8s` and `AuthServe
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.Blazor.dll"]
```
@ -413,8 +413,8 @@ docker build -f Dockerfile.local -t acme/bookstore-blazor:latest . #Builds the i
​ {{ end }} {{ if Tiered == "No" }}Blazor Server application is a server-side rendering application that contains both the OpenID-provider and the Http.Api endpoints within self; it will be a single application to deploy. `Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
@ -607,8 +607,8 @@ docker build -f Dockerfile.local -t acme/bookstore-angular:latest . #Builds the
The Blazor application uses [nginx:alpine-slim](https://hub.docker.com/layers/library/nginx/alpine-slim/images/sha256-0f859db466fda2c52f62b48d0602fb26867d98edbd62c26ae21414b3dea8d8f4?context=explore) base image to host the blazor application. You can modify the base image based on your preference in the `Dockerfile.local` which provided under the Blazor folder of your solution as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS build
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS build
COPY bin/Release/net10.0/publish/ app/
FROM nginx:alpine-slim AS final
WORKDIR /usr/share/nginx/html
@ -663,8 +663,8 @@ docker build -f Dockerfile.local -t acme/bookstore-blazor:latest . #Builds the i
This is the backend application that contains the openid-provider functionality as well. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
@ -720,8 +720,8 @@ docker build -f Dockerfile.local -t acme/bookstore-api:latest . #Builds the imag
This is the backend application that contains the OpenID-provider functionality as well. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
@ -779,8 +779,8 @@ docker build -f Dockerfile.local -t acme/bookstore-api:latest . #Builds the imag
This is the openid-provider application, the authentication server, which should be individually hosted compared to non-tiered application templates. The `dockerfile.local` is located under the `AuthServer` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
@ -832,8 +832,8 @@ docker build -f Dockerfile.local -t acme/bookstore-authserver:latest . #Builds t
This is the backend application that exposes the endpoints and swagger UI. It is not a multi-stage dockerfile; hence you need to have already built this application in **Release mode** to use this dockerfile. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.HttpApi.Host.dll"]
```

10
docs/en/solution-templates/layered-web-application/mobile-applications.md

@ -94,22 +94,22 @@ You can follow [Mobile Application Development Tutorial - MAUI](../../tutorials/
This is the mobile application that is built based on Facebook's [React Native framework](https://reactnative.dev/) and [Expo](https://expo.dev/). It will be in the solution only if you've selected React Native as your mobile application option.
#### Project Structure
- **Environment.js**: file using for provide application level variables like `apiUrl`, `oAuthConfig` and etc.
- **Environment.ts**: file using for provide application level variables like `apiUrl`, `oAuthConfig` and etc.
- **api**: The `api` folder contains HTTP request files that simplify API management in the React Native starter template
- `API.js:` exports **axiosInstance**. It provides axios instance filled api url
- `API.ts:` exports **axiosInstance**. It provides axios instance filled api url
- **components**: In the `components` folder you can reach built in react native components that you can use in your app. These components **facilitates** your list, select and etc. operations
- **contexts**: `contexts` folder contains [react context](https://react.dev/reference/react/createContext). You can expots your contexts in this folder. `Localization context provided in here`
- **navigators**: folder contains [react-native stacks](https://reactnavigation.org/docs/stack-navigator/). After create new *FeatureName*Navigator we need to provide in `DrawerNavigator.js` file as `Drawer.Screen`
- **navigators**: folder contains [react-native stacks](https://reactnavigation.org/docs/stack-navigator/). After create new *FeatureName*Navigator we need to provide in `DrawerNavigator.tsx` file as `Drawer.Screen`
- **screens**: is the content of navigated page. We'll pass as component property to [Stack.Screen](https://reactnavigation.org/docs/native-stack-navigator/)
- **store**: folder manages state-management operations. We will define `actions`, `reducers`, `sagas` and `selectors` here.
- **store**: folder manages state-management operations. We will define `actions`, `listeners`, `reducers`, and `selectors` here.
- **styles**: folder contains app styles. `system-style.js` comes built in template we can also add new styles.
- **styles**: folder contains app styles. `system-style.ts` comes built in template we can also add new styles.
- **utils**: folder contains helper functions that we can use in application

22
docs/en/solution-templates/microservice/mobile-applications.md

@ -139,22 +139,28 @@ You can follow [Mobile Application Development Tutorial - MAUI](../../tutorials/
This is the mobile application that is built based on Facebook's [React Native framework](https://reactnative.dev/) and [Expo](https://expo.dev/). It will be in the solution only if you've selected React Native as your mobile application option.
#### Project Structure
- **Environment.js**: file using for provide application level variables like `apiUrl`, `oAuthConfig` and etc.
- **Environment.ts**: file using for providing application level variables like `apiUrl`, `oAuthConfig` and etc.
- **api**: The `api` folder contains HTTP request files that simplify API management in the React Native starter template
- `API.js:` exports **axiosInstance**. It provides axios instance filled api url
- `API.ts:` exports **axiosInstance**. It provides axios instance filled api url.
- **components**: In the `components` folder you can reach built in react native components that you can use in your app. These components **facilitates** your list, select and etc. operations
- **components**: In the `components` folder, you can reach built in react native components that you can use in your app. These components **facilitates** your list, select and etc. operations.
- **contexts**: `contexts` folder contains [react context](https://react.dev/reference/react/createContext). You can expots your contexts in this folder. `Localization context provided in here`
- **navigators**: folder contains [react-native stacks](https://reactnavigation.org/docs/stack-navigator/). After create new *FeatureName*Navigator we need to provide in `DrawerNavigator.js` file as `Drawer.Screen`
- **hocs**: this folder is added to contain higher order components. The purpose is to wrap components with additional features or properties. It initially has a `PermissionHoc.tsx` that wraps a component to check the permission grant status.
- **screens**: is the content of navigated page. We'll pass as component property to [Stack.Screen](https://reactnavigation.org/docs/native-stack-navigator/)
- **hooks**: covers the react native hooks where you can get a reference from [the official documentation](https://react.dev/reference/react/hooks).
- **store**: folder manages state-management operations. We will define `actions`, `reducers`, `sagas` and `selectors` here.
- **interceptors**: initializes a file called `APIInterceptor.ts` that has a function to manage the http operations in a better way.
- **styles**: folder contains app styles. `system-style.js` comes built in template we can also add new styles.
- **navigators**: folder contains [react-native stacks](https://reactnavigation.org/docs/stack-navigator/). After creating a new *FeatureName*Navigator we need to provide in `DrawerNavigator.ts` file as `Drawer.Screen`
- **screens**: folder has the content of navigated page. We will pass as component property to [Stack.Screen](https://reactnavigation.org/docs/native-stack-navigator/)
- **store**: folder manages state-management operations. We will define `actions`, `listeners`, `reducers`, and `selectors` here.
- **styles**: folder contains app styles. `system-style.ts` comes built in template we can also add new styles.
- **utils**: folder contains helper functions that we can use in application
@ -162,6 +168,6 @@ This is the mobile application that is built based on Facebook's [React Native f
React Native applications can't be run with the solution runner. You need to run them with the React Native CLI. You can check the [React Native documentation](https://reactnative.dev/docs/environment-setup) to learn how to setup the environment for React Native development.
Before running the React Native application, rest of the applications in the solution must be running. Such as AuthServer, MobileGateway and the microservices.
Before running the React Native application, the rest of the applications in the solution must be running. Such as AuthServer, MobileGateway and the microservices.
Then you can run the React Native application by following this documentation: [Getting Started with the React Native](../../framework/ui/react-native/index.md).

8
docs/en/suite/solution-structure.md

@ -318,8 +318,8 @@ React Native application folder structure is like below:
![react-native-folder-structure](../images/react-native-folder-structure.png)
* `App.js` is the bootstrap component of the application.
* `Environment.js` file has the essential configuration of the application. `prod` and `dev` configurations are defined in this file.
* `App.tsx` is the bootstrap component of the application.
* `Environment.ts` file has the essential configuration of the application. `prod` and `dev` configurations are defined in this file.
* [Contexts](https://reactjs.org/docs/context.html) are created in the `src/contexts` folder.
* [Higher order components](https://reactjs.org/docs/higher-order-components.html) are created in the `src/hocs` folder.
* [Custom hooks](https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook) are created in the `src/hooks`.
@ -353,12 +353,11 @@ Actions, reducers, sagas, selectors are created in the `src/store` folder. Store
* [**Store**](https://redux.js.org/basics/store) is defined in the `src/store/index.js` file.
* [**Actions**](https://redux.js.org/basics/actions/) are payloads of information that send data from your application to your store.
* [**Reducers**](https://redux.js.org/basics/reducers) specify how the application's state changes in response to actions sent to the store.
* [**Redux-Saga**](https://redux-saga.js.org/) is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage. Sagas are created in the `src/store/sagas` folder.
* [**Reselect**](https://github.com/reduxjs/reselect) library is used to create memoized selectors. Selectors are created in the `src/store/selectors` folder.
### APIs
[Axios](https://github.com/axios/axios) is used as the HTTP client library. An Axios instance is exported from `src/api/API.js` file to make HTTP calls with the same config. `src/api` folder also has the API files that have been created for API calls.
[Axios](https://github.com/axios/axios) is used as the HTTP client library. An Axios instance is exported from `src/api/API.ts` file to make HTTP calls with the same config. `src/api` folder also has the API files that have been created for API calls.
### Theming
@ -381,7 +380,6 @@ See the [Testing Overview](https://reactjs.org/docs/testing.html) document.
* [Axios](https://github.com/axios/axios) is used as HTTP client library.
* [Redux](https://redux.js.org/) is used as state management library.
* [Redux Toolkit](https://redux-toolkit.js.org/) library is used as a toolset for efficient Redux development.
* [Redux-Saga](https://redux-saga.js.org/) is used to manage asynchronous processes.
* [Redux Persist](https://github.com/rt2zz/redux-persist) is used for state persistance.
* [Reselect](https://github.com/reduxjs/reselect) is used to create memoized selectors.
* [i18n-js](https://github.com/fnando/i18n-js) is used as i18n library.

5
docs/en/tutorials/book-store/part-02.md

@ -446,7 +446,7 @@ Open the `/src/app/book/book.component.ts` file and replace the content as below
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { BookService, BookDto } from '@proxy/books';
@Component({
@ -458,7 +458,8 @@ import { BookService, BookDto } from '@proxy/books';
export class BookComponent implements OnInit {
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
constructor(public readonly list: ListService, private bookService: BookService) {}
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);

82
docs/en/tutorials/book-store/part-03.md

@ -576,7 +576,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below:
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { BookService, BookDto } from '@proxy/books';
@Component({
@ -588,9 +588,10 @@ import { BookService, BookDto } from '@proxy/books';
export class BookComponent implements OnInit {
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
isModalOpen = false; // add this line
isModalOpen = false;
constructor(public readonly list: ListService, private bookService: BookService) {}
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
@ -600,7 +601,7 @@ export class BookComponent implements OnInit {
});
}
// add new method
//add new method
createBook() {
this.isModalOpen = true;
}
@ -668,7 +669,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below:
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; // add bookTypeOptions
import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this
@ -688,11 +689,9 @@ export class BookComponent implements OnInit {
isModalOpen = false;
constructor(
public readonly list: ListService,
private bookService: BookService,
private fb: FormBuilder // inject FormBuilder
) {}
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
private readonly fb = inject(FormBuilder); // inject FormBuilder
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
@ -702,6 +701,7 @@ export class BookComponent implements OnInit {
});
}
// add new method
createBook() {
this.buildForm(); // add this line
this.isModalOpen = true;
@ -735,7 +735,7 @@ export class BookComponent implements OnInit {
* Imported `FormGroup`, `FormBuilder` and `Validators` from `@angular/forms`.
* Added a `form: FormGroup` property.
* Added a `bookTypes` property as a list of `BookType` enum members. That will be used in form options.
* Injected `FormBuilder` into the constructor. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms.
* Injected with the `FormBuilder` inject function.. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms.
* Added a `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method.
* Added a `save` method.
@ -823,7 +823,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below:
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { BookService, BookDto, bookTypeOptions } from '@proxy/books';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@ -848,11 +848,9 @@ export class BookComponent implements OnInit {
isModalOpen = false;
constructor(
public readonly list: ListService,
private bookService: BookService,
private fb: FormBuilder
) {}
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
private readonly fb = inject(FormBuilder);
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
@ -903,7 +901,7 @@ Open `/src/app/book/book.component.ts` and replace the content as shown below:
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { BookService, BookDto, bookTypeOptions } from '@proxy/books';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
@ -925,11 +923,9 @@ export class BookComponent implements OnInit {
isModalOpen = false;
constructor(
public readonly list: ListService,
private bookService: BookService,
private fb: FormBuilder
) {}
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
private readonly fb = inject(FormBuilder);
ngOnInit() {
const bookStreamCreator = (query) => this.bookService.getList(query);
@ -1039,34 +1035,40 @@ This template will show the **Edit** text for edit record operation, **New Book*
Open the `/src/app/book/book.component.ts` file and inject the `ConfirmationService`.
Replace the constructor as below:
Replace the injected services as below:
```js
// ...
// add new imports
import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared';
import { Component, OnInit, inject } from '@angular/core';
//change the constructor
constructor(
public readonly list: ListService,
private bookService: BookService,
private fb: FormBuilder,
private confirmation: ConfirmationService // inject the ConfirmationService
) {}
// Add a delete method
delete(id: string) {
this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => {
if (status === Confirmation.Status.confirm) {
this.bookService.delete(id).subscribe(() => this.list.get());
}
});
// ...
export class BookComponent implements OnInit {
// ...
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
private readonly fb = inject(FormBuilder);
private readonly confirmation = inject(ConfirmationService); // inject the ConfirmationService
// ...
// Add a delete method
delete(id: string) {
this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => {
if (status === Confirmation.Status.confirm) {
this.bookService.delete(id).subscribe(() => this.list.get());
}
});
}
}
```
* We imported `ConfirmationService`.
* We injected `ConfirmationService` to the constructor.
* We injected `ConfirmationService` using the `inject()` function.
* Added a `delete` method.
> Check out the [Confirmation Popup documentation](../../framework/ui/angular/confirmation-service.md) for more about this service.

12
docs/en/tutorials/book-store/part-09.md

@ -601,7 +601,7 @@ This command generates the service proxy for the author service and the related
Open the `/src/app/author/author.component.ts` file and replace the content as below:
```js
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { ListService, PagedResultDto } from '@abp/ng.core';
import { AuthorService, AuthorDto } from '@proxy/authors';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@ -623,12 +623,10 @@ export class AuthorComponent implements OnInit {
selectedAuthor = {} as AuthorDto;
constructor(
public readonly list: ListService,
private authorService: AuthorService,
private fb: FormBuilder,
private confirmation: ConfirmationService
) {}
public readonly list = inject(ListService);
private readonly authorService = inject(AuthorService);
private readonly fb = inject(FormBuilder);
private readonly confirmation = inject(ConfirmationService);
ngOnInit(): void {
const authorStreamCreator = (query) => this.authorService.getList(query);

12
docs/en/tutorials/book-store/part-10.md

@ -1009,12 +1009,12 @@ export class BookComponent implements OnInit {
isModalOpen = false;
constructor(
public readonly list: ListService,
private bookService: BookService,
private fb: FormBuilder,
private confirmation: ConfirmationService
) {
public readonly list = inject(ListService);
private readonly bookService = inject(BookService);
private readonly fb = inject(FormBuilder);
private readonly confirmation = inject(ConfirmationService);
constructor() {
this.authors$ = bookService.getAuthorLookup().pipe(map((r) => r.items));
}

7
docs/en/tutorials/microservice/part-05.md

@ -643,7 +643,7 @@ export const ORDER_SERVICE_ROUTES: Routes = [
* Create `order.component.ts` file under the `projects/ordering-service/src/lib/order` folder as following code:
```typescript
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OrderDto, OrderService } from './proxy/ordering-service/services';
@ -657,12 +657,13 @@ export class OrderComponent {
items: OrderDto[] = [];
constructor(private readonly proxy: OrderService) {
private readonly proxy = inject(OrderService);
constructor() {
this.proxy.getList().subscribe((res) => {
this.items = res;
});
}
}
```

963
docs/en/tutorials/mobile/react-native/index.md

File diff suppressed because it is too large

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

@ -751,7 +751,7 @@ Open the `/angular/src/app/home/home.component.ts` file and replace its content
```js
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { TodoItemDto, TodoService } from '@proxy';
@Component({
@ -764,10 +764,8 @@ export class HomeComponent implements OnInit {
todoItems: TodoItemDto[];
newTodoText: string;
constructor(
private todoService: TodoService,
private toasterService: ToasterService)
{ }
private readonly todoService = inject(TodoService);
private readonly toasterService = inject(ToasterService);
ngOnInit(): void {
this.todoService.getList().subscribe(response => {
@ -775,7 +773,7 @@ export class HomeComponent implements OnInit {
});
}
create(): void{
create(): void {
this.todoService.create(this.newTodoText).subscribe((result) => {
this.todoItems = this.todoItems.concat(result);
this.newTodoText = null;
@ -789,7 +787,6 @@ export class HomeComponent implements OnInit {
});
}
}
```
We've used `todoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used on the view side.

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

@ -718,7 +718,7 @@ Open the `/angular/src/app/home/home.component.ts` file and replace its content
```ts
import { ToasterService } from "@abp/ng.theme.shared";
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { TodoItemDto } from "@proxy/services/dtos";
import { TodoService } from "@proxy/services";
@ -727,16 +727,13 @@ import { TodoService } from "@proxy/services";
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
todoItems: TodoItemDto[];
newTodoText: string;
constructor(
private todoService: TodoService,
private toasterService: ToasterService)
{ }
private readonly todoService = inject(TodoService);
private readonly toasterService = inject(ToasterService);
ngOnInit(): void {
this.todoService.getList().subscribe(response => {
@ -744,7 +741,7 @@ export class HomeComponent implements OnInit {
});
}
create(): void{
create(): void {
this.todoService.create(this.newTodoText).subscribe((result) => {
this.todoItems = this.todoItems.concat(result);
this.newTodoText = null;

9
docs/en/ui-themes/lepton-x-lite/angular.md

@ -131,15 +131,16 @@ The **Layout components** and all the replacable components are predefined in `e
```js
import { ReplaceableComponentsService } from '@abp/ng.core'; // imported ReplaceableComponentsService
import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum
import { eThemeLeptonXComponents } from '@abp/ng.theme.lepton-x'; // imported eThemeLeptonXComponents enum
import { eThemeLeptonXComponents } from '@abp/ng.theme.lepton-x'; // imported eThemeLeptonXComponents enum
import { Component, inject } from '@angular/core';
//...
@Component(/* component metadata */)
export class AppComponent {
constructor(
private replaceableComponents: ReplaceableComponentsService, // injected the service
) {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: YourNewApplicationLayoutComponent,
key: eThemeLeptonXComponents.ApplicationLayout,

18
docs/en/ui-themes/lepton-x/angular-customization.md

@ -18,14 +18,18 @@ The **Layout components** and all the replacable components are predefined in `e
### How to replace a component
```js
import { ReplaceableComponentsService } from '@abp/ng.core'; // imported ReplaceableComponentsService
import {eThemeLeptonXComponents} from "@volosoft/abp.ng.theme.lepton-x"; // imported eThemeLeptonXComponents enum
//...
@Component(/* component metadata */)
import { Component, inject } from '@angular/core';
import { ReplaceableComponentsService } from '@abp/ng.core';
import { eThemeLeptonXComponents } from '@volosoft/abp.ng.theme.lepton-x';
import { YourNewApplicationLayoutComponent } from './your-new-application-layout.component'; // varsa
@Component({
// component metadata
})
export class AppComponent {
constructor(
private replaceableComponents: ReplaceableComponentsService, // injected the service
) {
private readonly replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: YourNewApplicationLayoutComponent,
key: eThemeLeptonXComponents.ApplicationLayout,

1740
framework/Volo.Abp.sln

File diff suppressed because it is too large

1
framework/Volo.Abp.sln.DotSettings

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=E193A13E6CDABC4687BFBCAD92DEDC16/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=E193A13E6CDABC4687BFBCAD92DEDC16/AbsolutePath/@EntryValue">D:\Github\abp\common.DotSettings</s:String>

255
framework/Volo.Abp.slnx

@ -0,0 +1,255 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Volo.Abp.ApiVersioning.Abstractions/Volo.Abp.ApiVersioning.Abstractions.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Abstractions/Volo.Abp.AspNetCore.Abstractions.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Bundling/Volo.Abp.AspNetCore.Bundling.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming.Bundling/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming.Bundling.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo.Abp.AspNetCore.Components.MauiBlazor.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.Server.Theming/Volo.Abp.AspNetCore.Components.Server.Theming.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.Web.Theming/Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.Web/Volo.Abp.AspNetCore.Components.Web.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo.Abp.AspNetCore.Mvc.Client.Common.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo.Abp.AspNetCore.Mvc.Contracts.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj" />
<Project Path="src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj" />
<Project Path="src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj" />
<Project Path="src/Volo.Abp.Auditing.Contracts/Volo.Abp.Auditing.Contracts.csproj" />
<Project Path="src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj" />
<Project Path="src/Volo.Abp.Authorization.Abstractions/Volo.Abp.Authorization.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj" />
<Project Path="src/Volo.Abp.Autofac.WebAssembly/Volo.Abp.Autofac.WebAssembly.csproj" />
<Project Path="src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj" />
<Project Path="src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj" />
<Project Path="src/Volo.Abp.AzureServiceBus/Volo.Abp.AzureServiceBus.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs.Abstractions/Volo.Abp.BackgroundJobs.Abstractions.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs.HangFire/Volo.Abp.BackgroundJobs.HangFire.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs.Quartz/Volo.Abp.BackgroundJobs.Quartz.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo.Abp.BackgroundJobs.RabbitMQ.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs/Volo.Abp.BackgroundJobs.csproj" />
<Project Path="src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj" />
<Project Path="src/Volo.Abp.BackgroundWorkers.Quartz/Volo.Abp.BackgroundWorkers.Quartz.csproj" />
<Project Path="src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj" />
<Project Path="src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Aliyun/Volo.Abp.BlobStoring.Aliyun.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Aws/Volo.Abp.BlobStoring.Aws.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Bunny/Volo.Abp.BlobStoring.Bunny.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Google/Volo.Abp.BlobStoring.Google.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Memory/Volo.Abp.BlobStoring.Memory.csproj" />
<Project Path="src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj" />
<Project Path="src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj" />
<Project Path="src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj" />
<Project Path="src/Volo.Abp.Caching/Volo.Abp.Caching.csproj" />
<Project Path="src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj" />
<Project Path="src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj" />
<Project Path="src/Volo.Abp.Cli/Volo.Abp.Cli.csproj" />
<Project Path="src/Volo.Abp.Core/Volo.Abp.Core.csproj" />
<Project Path="src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj" />
<Project Path="src/Volo.Abp.Dapr/Volo.Abp.Dapr.csproj" />
<Project Path="src/Volo.Abp.Data/Volo.Abp.Data.csproj" />
<Project Path="src/Volo.Abp.Ddd.Application.Contracts/Volo.Abp.Ddd.Application.Contracts.csproj" />
<Project Path="src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj" />
<Project Path="src/Volo.Abp.Ddd.Domain.Shared/Volo.Abp.Ddd.Domain.Shared.csproj" />
<Project Path="src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj" />
<Project Path="src/Volo.Abp.DistributedLocking.Abstractions/Volo.Abp.DistributedLocking.Abstractions.csproj" />
<Project Path="src/Volo.Abp.DistributedLocking.Dapr/Volo.Abp.DistributedLocking.Dapr.csproj" />
<Project Path="src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj" />
<Project Path="src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.MySQL.Pomelo/Volo.Abp.EntityFrameworkCore.MySQL.Pomelo.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />
<Project Path="src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj" />
<Project Path="src/Volo.Abp.EventBus.Abstractions/Volo.Abp.EventBus.Abstractions.csproj" />
<Project Path="src/Volo.Abp.EventBus.Azure/Volo.Abp.EventBus.Azure.csproj" />
<Project Path="src/Volo.Abp.EventBus.Dapr/Volo.Abp.EventBus.Dapr.csproj" />
<Project Path="src/Volo.Abp.EventBus.Kafka/Volo.Abp.EventBus.Kafka.csproj" />
<Project Path="src/Volo.Abp.EventBus.RabbitMQ/Volo.Abp.EventBus.RabbitMQ.csproj" />
<Project Path="src/Volo.Abp.EventBus.Rebus/Volo.Abp.EventBus.Rebus.csproj" />
<Project Path="src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj" />
<Project Path="src/Volo.Abp.ExceptionHandling/Volo.Abp.ExceptionHandling.csproj" />
<Project Path="src/Volo.Abp.Features/Volo.Abp.Features.csproj" />
<Project Path="src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj" />
<Project Path="src/Volo.Abp.Gdpr.Abstractions/Volo.Abp.Gdpr.Abstractions.csproj" />
<Project Path="src/Volo.Abp.GlobalFeatures/Volo.Abp.GlobalFeatures.csproj" />
<Project Path="src/Volo.Abp.Guids/Volo.Abp.Guids.csproj" />
<Project Path="src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj" />
<Project Path="src/Volo.Abp.Http.Abstractions/Volo.Abp.Http.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Http.Client.Dapr/Volo.Abp.Http.Client.Dapr.csproj" />
<Project Path="src/Volo.Abp.Http.Client.IdentityModel.MauiBlazor/Volo.Abp.Http.Client.IdentityModel.MauiBlazor.csproj" />
<Project Path="src/Volo.Abp.Http.Client.IdentityModel.Web/Volo.Abp.Http.Client.IdentityModel.Web.csproj" />
<Project Path="src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo.Abp.Http.Client.IdentityModel.WebAssembly.csproj" />
<Project Path="src/Volo.Abp.Http.Client.IdentityModel/Volo.Abp.Http.Client.IdentityModel.csproj" />
<Project Path="src/Volo.Abp.Http.Client.Web/Volo.Abp.Http.Client.Web.csproj" />
<Project Path="src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj" />
<Project Path="src/Volo.Abp.Http/Volo.Abp.Http.csproj" />
<Project Path="src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj" />
<Project Path="src/Volo.Abp.Imaging.Abstractions/Volo.Abp.Imaging.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Imaging.AspNetCore/Volo.Abp.Imaging.AspNetCore.csproj" />
<Project Path="src/Volo.Abp.Imaging.ImageSharp/Volo.Abp.Imaging.ImageSharp.csproj" />
<Project Path="src/Volo.Abp.Imaging.MagickNet/Volo.Abp.Imaging.MagickNet.csproj" />
<Project Path="src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj" />
<Project Path="src/Volo.Abp.Json.Abstractions/Volo.Abp.Json.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Json.Newtonsoft/Volo.Abp.Json.Newtonsoft.csproj" />
<Project Path="src/Volo.Abp.Json.SystemTextJson/Volo.Abp.Json.SystemTextJson.csproj" />
<Project Path="src/Volo.Abp.Json/Volo.Abp.Json.csproj" />
<Project Path="src/Volo.Abp.Kafka/Volo.Abp.Kafka.csproj" />
<Project Path="src/Volo.Abp.Ldap.Abstractions/Volo.Abp.Ldap.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Ldap/Volo.Abp.Ldap.csproj" />
<Project Path="src/Volo.Abp.Localization.Abstractions/Volo.Abp.Localization.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Localization/Volo.Abp.Localization.csproj" />
<Project Path="src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj" />
<Project Path="src/Volo.Abp.Mapperly/Volo.Abp.Mapperly.csproj" />
<Project Path="src/Volo.Abp.Maui.Client/Volo.Abp.Maui.Client.csproj" />
<Project Path="src/Volo.Abp.MemoryDb/Volo.Abp.MemoryDb.csproj" />
<Project Path="src/Volo.Abp.Minify/Volo.Abp.Minify.csproj" />
<Project Path="src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj" />
<Project Path="src/Volo.Abp.MultiLingualObjects/Volo.Abp.MultiLingualObjects.csproj" />
<Project Path="src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj" />
<Project Path="src/Volo.Abp.MultiTenancy/Volo.Abp.MultiTenancy.csproj" />
<Project Path="src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj" />
<Project Path="src/Volo.Abp.ObjectMapping/Volo.Abp.ObjectMapping.csproj" />
<Project Path="src/Volo.Abp.Quartz/Volo.Abp.Quartz.csproj" />
<Project Path="src/Volo.Abp.RabbitMQ/Volo.Abp.RabbitMQ.csproj" />
<Project Path="src/Volo.Abp.RemoteServices/Volo.Abp.RemoteServices.csproj" />
<Project Path="src/Volo.Abp.Security/Volo.Abp.Security.csproj" />
<Project Path="src/Volo.Abp.Serialization/Volo.Abp.Serialization.csproj" />
<Project Path="src/Volo.Abp.Settings/Volo.Abp.Settings.csproj" />
<Project Path="src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj" />
<Project Path="src/Volo.Abp.Sms.TencentCloud/Volo.Abp.Sms.TencentCloud.csproj" />
<Project Path="src/Volo.Abp.Sms/Volo.Abp.Sms.csproj" />
<Project Path="src/Volo.Abp.Specifications/Volo.Abp.Specifications.csproj" />
<Project Path="src/Volo.Abp.Swashbuckle/Volo.Abp.Swashbuckle.csproj" />
<Project Path="src/Volo.Abp.TestBase/Volo.Abp.TestBase.csproj" />
<Project Path="src/Volo.Abp.TextTemplating.Core/Volo.Abp.TextTemplating.Core.csproj" />
<Project Path="src/Volo.Abp.TextTemplating.Razor/Volo.Abp.TextTemplating.Razor.csproj" />
<Project Path="src/Volo.Abp.TextTemplating.Scriban/Volo.Abp.TextTemplating.Scriban.csproj" />
<Project Path="src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj" />
<Project Path="src/Volo.Abp.Threading/Volo.Abp.Threading.csproj" />
<Project Path="src/Volo.Abp.Timing/Volo.Abp.Timing.csproj" />
<Project Path="src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj" />
<Project Path="src/Volo.Abp.UI/Volo.Abp.UI.csproj" />
<Project Path="src/Volo.Abp.Uow/Volo.Abp.Uow.csproj" />
<Project Path="src/Volo.Abp.Validation.Abstractions/Volo.Abp.Validation.Abstractions.csproj" />
<Project Path="src/Volo.Abp.Validation/Volo.Abp.Validation.csproj" />
<Project Path="src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj" />
<Project Path="src/Volo.Abp/Volo.Abp.csproj" />
<Project Path="src/Volo.Abp.AI.Abstractions/Volo.Abp.AI.Abstractions.csproj" />
<Project Path="src/Volo.Abp.AI/Volo.Abp.AI.csproj" />
</Folder>
<Folder Name="/test/">
<Project Path="test/AbpTestBase/AbpTestBase.csproj" />
<Project Path="test/SimpleConsoleDemo/SimpleConsoleDemo.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.PlugIn/Volo.Abp.AspNetCore.Mvc.PlugIn.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj" />
<Project Path="test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj" />
<Project Path="test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj" />
<Project Path="test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj" />
<Project Path="test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj" />
<Project Path="test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Aws.Tests/Volo.Abp.BlobStoring.Aws.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Bunny.Tests/Volo.Abp.BlobStoring.Bunny.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Google.Tests/Volo.Abp.BlobStoring.Google.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Memory.Tests/Volo.Abp.BlobStoring.Memory.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Minio.Tests/Volo.Abp.BlobStoring.Minio.Tests.csproj" />
<Project Path="test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj" />
<Project Path="test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj" />
<Project Path="test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj" />
<Project Path="test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj" />
<Project Path="test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj" />
<Project Path="test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj" />
<Project Path="test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj" />
<Project Path="test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj" />
<Project Path="test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj" />
<Project Path="test/Volo.Abp.DistributedLocking.Abstractions.Tests/Volo.Abp.DistributedLocking.Abstractions.Tests.csproj" />
<Project Path="test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj" />
<Project Path="test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj" />
<Project Path="test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj" />
<Project Path="test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj" />
<Project Path="test/Volo.Abp.ExceptionHandling.Tests/Volo.Abp.ExceptionHandling.Tests.csproj" />
<Project Path="test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj" />
<Project Path="test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj" />
<Project Path="test/Volo.Abp.GlobalFeatures.Tests/Volo.Abp.GlobalFeatures.Tests.csproj" />
<Project Path="test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo.Abp.Http.Client.IdentityModel.Web.Tests.csproj" />
<Project Path="test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj" />
<Project Path="test/Volo.Abp.Http.Tests/Volo.Abp.Http.Tests.csproj" />
<Project Path="test/Volo.Abp.IdentityModel.Tests/Volo.Abp.IdentityModel.Tests.csproj" />
<Project Path="test/Volo.Abp.Imaging.Abstractions.Tests/Volo.Abp.Imaging.Abstractions.Tests.csproj" />
<Project Path="test/Volo.Abp.Imaging.AspNetCore.Tests/Volo.Abp.Imaging.AspNetCore.Tests.csproj" />
<Project Path="test/Volo.Abp.Imaging.ImageSharp.Tests/Volo.Abp.Imaging.ImageSharp.Tests.csproj" />
<Project Path="test/Volo.Abp.Imaging.MagickNet.Tests/Volo.Abp.Imaging.MagickNet.Tests.csproj" />
<Project Path="test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj" />
<Project Path="test/Volo.Abp.Json.Tests/Volo.Abp.Json.Tests.csproj" />
<Project Path="test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj" />
<Project Path="test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj" />
<Project Path="test/Volo.Abp.MailKit.Tests/Volo.Abp.MailKit.Tests.csproj" />
<Project Path="test/Volo.Abp.Mapperly.Tests/Volo.Abp.Mapperly.Tests.csproj" />
<Project Path="test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj" />
<Project Path="test/Volo.Abp.Minify.Tests/Volo.Abp.Minify.Tests.csproj" />
<Project Path="test/Volo.Abp.MongoDB.Tests.SecondContext/Volo.Abp.MongoDB.Tests.SecondContext.csproj" />
<Project Path="test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj" />
<Project Path="test/Volo.Abp.MultiLingualObjects.Tests/Volo.Abp.MultiLingualObjects.Tests.csproj" />
<Project Path="test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj" />
<Project Path="test/Volo.Abp.ObjectExtending.Tests/Volo.Abp.ObjectExtending.Tests.csproj" />
<Project Path="test/Volo.Abp.ObjectMapping.Tests/Volo.Abp.ObjectMapping.Tests.csproj" />
<Project Path="test/Volo.Abp.RemoteServices.Tests/Volo.Abp.RemoteServices.Tests.csproj" />
<Project Path="test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj" />
<Project Path="test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj" />
<Project Path="test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj" />
<Project Path="test/Volo.Abp.Sms.Aliyun.Tests/Volo.Abp.Sms.Aliyun.Tests.csproj" />
<Project Path="test/Volo.Abp.Sms.TencenCloud.Tests/Volo.Abp.Sms.TencentCloud.Tests.csproj" />
<Project Path="test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj" />
<Project Path="test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj" />
<Project Path="test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj" />
<Project Path="test/Volo.Abp.TextTemplating.Razor.Tests/Volo.Abp.TextTemplating.Razor.Tests.csproj" />
<Project Path="test/Volo.Abp.TextTemplating.Scriban.Tests/Volo.Abp.TextTemplating.Scriban.Tests.csproj" />
<Project Path="test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj" />
<Project Path="test/Volo.Abp.Threading.Tests/Volo.Abp.Threading.Tests.csproj" />
<Project Path="test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj" />
<Project Path="test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj" />
<Project Path="test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj" />
<Project Path="test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj" />
<Project Path="test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj" />
</Folder>
</Solution>

3
framework/src/Volo.Abp.AI.Abstractions/FodyWeavers.xml

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

30
framework/src/Volo.Abp.AI.Abstractions/FodyWeavers.xsd

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

3
framework/src/Volo.Abp.AI.Abstractions/Volo.Abp.AI.Abstractions.abppkg

@ -0,0 +1,3 @@
{
"role": "lib.framework"
}

26
framework/src/Volo.Abp.AI.Abstractions/Volo.Abp.AI.Abstractions.csproj

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<PackageId>Volo.Abp.AI.Abstractions</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SemanticKernel.Abstractions" />
</ItemGroup>
</Project>

7
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/AbpAIAbstractionsModule.cs

@ -0,0 +1,7 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.AI;
public class AbpAIAbstractionsModule : AbpModule
{
}

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

Loading…
Cancel
Save