Browse Source

Merge remote-tracking branch 'origin/net7' into json

pull/13357/head
maliming 4 years ago
parent
commit
321ebc352d
  1. 6
      .github/workflows/angular.yml
  2. 6
      .github/workflows/build-and-test.yml
  3. 94
      .github/workflows/codeql-analysis.yml
  4. 47
      .github/workflows/image-compression.yml
  5. 8
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  6. 8
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  7. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  8. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  9. 8
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  10. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/tr.json
  11. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  12. 22
      docs/en/CLI.md
  13. 690
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/How-to-Design-Multi-Lingual-Entity.md
  14. BIN
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/data-model.png
  15. BIN
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/result.gif
  16. 4
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
  17. 154
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
  18. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png
  19. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png
  20. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png
  21. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png
  22. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png
  23. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png
  24. 452
      docs/en/Deploy-azure-app-service.md
  25. 425
      docs/en/Deployment/Deploy-Azure-App-Service.md
  26. 5
      docs/en/Deployment/Index.md
  27. 12
      docs/en/Modules/OpenIddict.md
  28. 11
      docs/en/Modules/Setting-Management.md
  29. 3
      docs/en/UI/Angular/Theming.md
  30. 3
      docs/en/UI/AspNetCore/Overall.md
  31. 8
      docs/en/UI/AspNetCore/Page-Header.md
  32. 6
      docs/en/UI/AspNetCore/Toolbars.md
  33. 3
      docs/en/UI/Blazor/Overall.md
  34. 2
      docs/en/UI/Blazor/Page-Header.md
  35. 4
      docs/en/UI/Blazor/Page-Layout.md
  36. 6
      docs/en/UI/Blazor/Toolbars.md
  37. 8
      docs/en/docs-nav.json
  38. BIN
      docs/en/images/azdevops-1.png
  39. BIN
      docs/en/images/azdevops-10.png
  40. BIN
      docs/en/images/azdevops-11.png
  41. BIN
      docs/en/images/azdevops-12.png
  42. BIN
      docs/en/images/azdevops-13.png
  43. BIN
      docs/en/images/azdevops-14.png
  44. BIN
      docs/en/images/azdevops-15.png
  45. BIN
      docs/en/images/azdevops-16.png
  46. BIN
      docs/en/images/azdevops-17.png
  47. BIN
      docs/en/images/azdevops-18.png
  48. BIN
      docs/en/images/azdevops-19.png
  49. BIN
      docs/en/images/azdevops-2.png
  50. BIN
      docs/en/images/azdevops-20.png
  51. BIN
      docs/en/images/azdevops-21.png
  52. BIN
      docs/en/images/azdevops-22.png
  53. BIN
      docs/en/images/azdevops-23.png
  54. BIN
      docs/en/images/azdevops-3.png
  55. BIN
      docs/en/images/azdevops-4.png
  56. BIN
      docs/en/images/azdevops-5.png
  57. BIN
      docs/en/images/azdevops-6.png
  58. BIN
      docs/en/images/azdevops-7.png
  59. BIN
      docs/en/images/azdevops-8.png
  60. BIN
      docs/en/images/azdevops-9.png
  61. BIN
      docs/en/images/breadcrumbs-example.png
  62. BIN
      docs/en/images/leptonx-selected-menu-item-example.gif
  63. BIN
      docs/en/images/leptonxlite-toolbar-main-example.png
  64. BIN
      docs/en/images/leptonxlite-toolbar-mainmobile-example.png
  65. BIN
      docs/en/images/setting-management-email-ui.png
  66. 1
      docs/zh-Hans/CLI.md
  67. 8
      docs/zh-Hans/Modules/Setting-Management.md
  68. 4
      docs/zh-Hans/docs-nav.json
  69. BIN
      docs/zh-Hans/images/setting-management-email-ui.png
  70. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj
  71. 37
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs
  72. 2
      framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj
  73. 34
      framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
  74. 2
      framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj
  75. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  76. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs
  77. 32
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs
  78. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs
  79. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
  80. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  81. 2
      framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj
  82. 10
      framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs
  83. 4
      framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/ConfigurationHelper.cs
  84. 6
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  85. 21
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs
  86. 16
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs
  87. 2
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/pt-BR.json
  88. 2
      framework/src/Volo.Abp.ExceptionHandling/Volo.Abp.ExceptionHandling.csproj
  89. 6
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json
  90. 2
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json
  91. 2
      framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj
  92. 2
      framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj
  93. 2
      framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj
  94. 50
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AbpLazyServiceProvider_Tests.cs
  95. 2
      framework/test/Volo.Abp.Sms.Aliyun.Tests/Volo.Abp.Sms.Aliyun.Tests.csproj
  96. 2
      framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj
  97. 2
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.csproj
  98. 18
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/AbpAccountApplicationContractsModule.cs
  99. 2
      modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj
  100. 3
      modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorAutoMapperProfile.cs

6
.github/workflows/angular.yml

@ -10,11 +10,17 @@ on:
branches:
- 'rel-*'
- 'dev'
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
contents: read
jobs:
build-test-lint:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

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

@ -31,12 +31,18 @@ on:
- 'templates/**/*.cshtml'
- 'templates/**/*.csproj'
- 'templates/**/*.razor'
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
contents: read
jobs:
build-test:
runs-on: windows-latest
if: ${{ !github.event.pull_request.draft }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master

94
.github/workflows/codeql-analysis.yml

@ -9,26 +9,32 @@ on:
push:
branches: [dev, rel-*]
paths:
- 'abp/**/*.js'
- 'abp/**/*.cs'
- 'abp/**/*.cshtml'
- 'abp/**/*.csproj'
- 'abp/**/*.razor'
- "abp/**/*.js"
- "abp/**/*.cs"
- "abp/**/*.cshtml"
- "abp/**/*.csproj"
- "abp/**/*.razor"
pull_request:
# The branches below must be a subset of the branches above
branches: [dev]
paths:
- 'abp/**/*.js'
- 'abp/**/*.cs'
- 'abp/**/*.cshtml'
- 'abp/**/*.csproj'
- 'abp/**/*.razor'
- "abp/**/*.js"
- "abp/**/*.cs"
- "abp/**/*.cshtml"
- "abp/**/*.csproj"
- "abp/**/*.razor"
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
contents: read
jobs:
analyze:
if: ${{ !github.event.pull_request.draft }}
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
@ -41,48 +47,48 @@ jobs:
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['csharp', 'javascript']
language: ["csharp", "javascript"]
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

47
.github/workflows/image-compression.yml

@ -1,21 +1,26 @@
name: Compress Images
on:
pull_request:
paths:
- '**.jpg'
- '**.jpeg'
- '**.png'
- '**.webp'
jobs:
build:
if: github.event.pull_request.head.repo.full_name == github.repository
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Compress Images
uses: calibreapp/image-actions@main
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
name: Compress Images
on:
pull_request:
paths:
- "**.jpg"
- "**.jpeg"
- "**.png"
- "**.webp"
types:
- opened
- synchronize
- reopened
- ready_for_review
jobs:
build:
if: github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Compress Images
uses: calibreapp/image-actions@main
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}

8
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -73,6 +73,7 @@
"DeveloperFocused": "Developer Focused",
"ShareYourExperiences": "Share your experiences with the ABP Framework",
"LatestPosts": "Latest Posts",
"LatestVideos": "Latest Videos",
"Views": "Views",
"LearnLatestNewsAboutABPFramework": "Get information about happenings in ABP like new releases, free sources, posts, and more.",
"DeveloperTools": "Developer Tools",
@ -98,6 +99,7 @@
"Logout": "Logout",
"Home": "Home",
"Posts": "Posts",
"Videos": "Videos",
"JoinTheABPCommunity": "Join the ABP Community",
"SubmitYourPost": "Submit Your Post",
"Modules": "Modules",
@ -171,6 +173,10 @@
"BuyOrRenewLicenseToGetExtra2Months": "Buy or Renew License Now and Get 2 Extra Months! HURRY UP! ⏰ Last Day: {0}",
"HurryUp": "HURRY UP!",
"LastDay": "Last Day: {0}",
"BuyNewLicenseBetweenDatesToGetBenefit": "Buy a new license between {0} and {1} to get benefit for extra 2 months!"
"BuyNewLicenseBetweenDatesToGetBenefit": "Buy a new license between {0} and {1} to get benefit for extra 2 months!",
"CheckAllCommunityTalks": "Check All Community Posts",
"ReadMore": "Read More",
"Post": "Post",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community."
}
}

8
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json

@ -73,6 +73,7 @@
"DeveloperFocused": "以开发者为中心",
"ShareYourExperiences": "分享您使用 ABP 框架的经验",
"LatestPosts": "最新的帖子",
"LatestVideos": "最新的视频",
"Views": "意见",
"LearnLatestNewsAboutABPFramework": "获取有关 ABP 的最新相关信息,例如新版本、免费资源、帖子等。",
"DeveloperTools": "开发者工具",
@ -98,6 +99,7 @@
"Logout": "登出",
"Home": "主页",
"Posts": "帖子",
"Videos": "视频",
"JoinTheABPCommunity": "加入 ABP 社区",
"SubmitYourPost": "提交您的帖子",
"Modules": "模块",
@ -168,6 +170,10 @@
"BuyOrRenewLicenseToGetExtra2Months": "立即购买或续订 ABP 商业许可证(适用于所有版本)并额外获得 2 个月!",
"HurryUp": "赶快下单!",
"LastDay": "活动截止日期: {0}",
"BuyNewLicenseBetweenDatesToGetBenefit": "在 {0} 和 {1} 之间购买一个新的许可证以获得额外 2 个月的收益!"
"BuyNewLicenseBetweenDatesToGetBenefit": "在 {0} 和 {1} 之间购买一个新的许可证以获得额外 2 个月的收益!",
"CheckAllCommunityTalks": "检查所有社区帖子",
"ReadMore": "阅读更多",
"Post": "邮政",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。"
}
}

1
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json

@ -150,7 +150,6 @@
"GetStarted": "Get Started",
"SourceCode": "Source Code",
"LeaveComment": "Leave Comment",
"ReadMore": "Read more",
"ShowMore": "Show More",
"NoPublishedPostsYet": "No published posts yet.",
"Name": "Name",

1
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json

@ -150,7 +150,6 @@
"GetStarted": "开始使用",
"SourceCode": "源代码",
"LeaveComment": "发表评论",
"ReadMore": "阅读更多",
"ShowMore": "展示更多",
"NoPublishedPostsYet": "还没有发布的帖子。",
"Name": "名字",

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

@ -293,7 +293,6 @@
"ExploreDocumentationAndGuides": "Explore the comprehensive documentation and guides.",
"Documentations": "Documentation",
"Views": "Views",
"ReadMore": "Read More",
"EnterYouEmailToGetNews": "Enter your email to get the latest news about the ABP Framework",
"Tiered": "Tiered",
"SeparateIdentityServer": "Separate Identity Server",
@ -378,6 +377,13 @@
"IntroducingTheSolution": "Introducing the eShopOnAbp Solution",
"RunningTheSolution": "Running the Solution",
"UnderstandingTheAuthenticationSystem": "Understanding the Authentication System",
"ExploringTheApplications": "Exploring the Applications",
"UnderstandingTheAPIGateways": "Understanding the API Gateways",
"DevelopingTheMicroservices": "Developing the Microservices",
"UnderstandingTheInfrastructure": "Understanding the Infrastructure",
"DiggingInTheUseCases": "Digging in the Use Cases",
"DeployingTheSolution": "Deploying the Solution",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "This book is in draft stage and is not completed yet.",
"Authors": "Authors",
"MicroserviceEBook": "Microservice E-Book",
"SelectUITheme": "Select UI Theme",

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

@ -279,6 +279,7 @@
"SubscribeToNewsletter": "ABP.IO Platform'u ile ilgili yeni haberler, makaleler, teklifler ve daha fazlası gibi gelişmeler hakkında bilgi almak için bültene abone olun.",
"FirstEdition": "İlk Baskı",
"ThankYou": "Teşekkürler!",
"CheckboxMandatory": "Devam etmek için bunu kontrol etmeniz gerekiyor!"
"CheckboxMandatory": "Devam etmek için bunu kontrol etmeniz gerekiyor!",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "Bu kitap taslak aşamasındadır ve henüz tamamlanmamıştır."
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json

@ -293,7 +293,6 @@
"ExploreDocumentationAndGuides": "探索全面的文档和指南。",
"Documentations": "文档",
"Views": "意见",
"ReadMore": "阅读更多",
"EnterYouEmailToGetNews": "输入您的电子邮件以获取有关 ABP 框架的最新消息",
"Tiered": "分层",
"SeparateIdentityServer": "独立的身份服务器",
@ -372,6 +371,7 @@
"MasteringAbpFramework_Book_What_You_Will_Learn_8": "使用 ABP 框架编写单元、集成和 UI 测试。",
"MasteringAbpFramework_Book_WhoIsThisBookFor": "这本书是给谁看的",
"MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "本书适用于希望学习软件架构和最佳实践的 Web 开发人员,以使用 Microsoft 技术和 ABP 框架构建\n 可维护的基于 Web 的解决方案。 C#\n 和 ASP.NET Core 的基本知识是开始阅读本书所必需的。",
"ComputersAndTechnology": "计算机与技术"
"ComputersAndTechnology": "计算机与技术",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "这本书正在草案阶段,还没有完成。"
}
}

22
docs/en/CLI.md

@ -124,7 +124,7 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample
* `--separate-auth-server`: The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--mobile` or `-m`: Specifies the mobile application framework. If not specified, no mobile application will be created. Available options:
* `react-native`: React Native.
* `maui`: MAUI.
* `maui`: MAUI. This mobile option is only available for ABP Commercial.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
@ -145,7 +145,8 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample
* `mongodb`: MongoDB.
* `--theme`: Specifes the theme. Default theme is `leptonx-lite`. Available themes:
* `leptonx-lite`: [LeptonX Lite Theme](/Themes/LeptonXLite/mvc.md).
* `basic`: [Basic Theme](/UI/AspNetCore/Basic-Theme.md).
* `basic`: [Basic Theme](/UI/AspNetCore/Basic-Theme.md).
* **`maui`**: .NET MAUI. A minimalist .NET MAUI application will be created if you specify this option.
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.
* `--preview`: Use latest preview version.
@ -161,6 +162,7 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample
* `PostgreSQL`
* `--local-framework-ref --abp-path`: Uses local projects references to the ABP framework instead of using the NuGet packages. This can be useful if you download the ABP Framework source code and have a local reference to the framework from your application.
* `--no-random-port`: Uses template's default ports.
* `--skip-installing-libs` or `-sib`: Skip installing client side packages.
See some [examples for the new command](CLI-New-Command-Samples.md) here.
@ -244,7 +246,7 @@ It can also create a new module for your solution and add it to your solution. S
> A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Usage
Usage:
````bash
abp add-module <module-name> [options]
@ -278,7 +280,7 @@ abp add-module ProductManagement --new --add-to-solution-file
Lists names of open-source application modules.
Usage
Usage:
````bash
abp list-modules [options]
@ -294,11 +296,21 @@ abp list-modules
* `--include-pro-modules`: Includes commercial (pro) modules in the output.
### list-templates
Lists all available templates to create a solution.
Usage:
```bash
abp list-templates
```
### get-source
Downloads the source code of a module to your computer.
Usage
Usage:
````bash
abp get-source <module-name> [options]

690
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/How-to-Design-Multi-Lingual-Entity.md

@ -0,0 +1,690 @@
# How to Design Multi-Lingual Entity
## Introduction
If you want to open up to the global market these days, end-to-end localization is a must. ABP provides an already established infrastructure for static texts. However, this may not be sufficient for many applications. You may need to fully customize your app for a particular language and region.
Let's take a look at a few quotes from Christian Arno's article "[How Foreign-Language Internet Strategies Boost Sales](https://www.mediapost.com/publications/article/155250/how-foreign-language-internet-strategies-boost-sal.html)" to better understand the impact of this:
- 82% of European consumers are less likely to buy online if the site is not in their native tongue ([Eurobarometer survey](http://europa.eu/rapid/pressReleasesAction.do?reference=IP/11/556)).
- 72.4% of global consumers are more likely to buy a product if the information is available in their own language ([Common Sense Advisory](http://www.commonsenseadvisory.com/)).
- The English language currently only accounts for 31% of all online use, and more than half of all searches are in languages other than English.
- Today, 42% of all Internet users are in Asia, while almost one-quarter are in Europe and just over 10% are in Latin America.
- Foreign languages have experienced exponential growth in online usage in the past decade -- with Chinese now officially the [second-most-prominent-language](http://english.peopledaily.com.cn/90001/90776/90882/7438489.html) on the Web. [Arabic](http://www.internetworldstats.com/stats7.htm) has increased by a whopping 2500%, while English has only risen by 204%
If you are looking for ways to expand your market share by fully customizing your application for a particular language and region, in this article I will explain how you can do it with ABP framework.
### Source Code
You can find the source code of the application at [abpframework/abp-samples](https://github.com/abpframework/abp-samples/tree/master/AcmeBookStoreMultiLingual).
### Demo of the Final Application
At the end of this article, we will have created an application same as in the gif below.
![data-model](./result.gif)
## Development
In order to keep the article short and get rid of unrelated information in the article (like defining entities etc.), we'll be using the [BookStore](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) example, which is used in the "[Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)" documentation of ABP Framework and we will make the Book entity as multi-lingual. If you do not want to finish this tutorial, you can find the application [here](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore).
### Determining the data model
We need a robust, maintainable, and efficient data model to store content in multiple languages.
> I read many articles to determine the data model correctly, and as a result, I decided to use one of the many approaches that suit us.
> However, as in everything, there is a trade-off here. If you are wondering about the advantages and disadvantages of the model we will implement compared to other models, I recommend you to read [this article](https://vertabelo.com/blog/data-modeling-for-multiple-languages-how-to-design-a-localization-ready-system/).
![data-model](./data-model.png)
As a result of the tutorial, we already have the `Book` and `Author` entities, as an extra, we will just add the `BookTranslation`.
> In the article, we will make the Name property of the Book entity multi-lingual, but the article is independent of the Book entity, you can make the entity you want multi-lingual with similar codes according to your requirements.
#### Acme.BookStore.Domain.Shared
Create a folder named `MultiLingualObjects` and create the following interfaces in its contents.
We will use the `IObjectTranslation` interface to mark the translation of a multi-lingual entity:
```csharp
public interface IObjectTranslation
{
string Language { get; set; }
}
```
We will use the `IMultiLingualObject<TTranslation>` interface to mark multi-lingual entities:
```csharp
public interface IMultiLingualObject<TTranslation>
where TTranslation : class, IObjectTranslation
{
ICollection<TTranslation> Translations { get; set; }
}
```
#### Acme.BookStore.Domain
In the `Books` folder, create the `BookTranslation` class as follows:
```csharp
public class BookTranslation : Entity, IObjectTranslation
{
public Guid BookId { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public override object[] GetKeys()
{
return new object[] {BookId, Language};
}
}
```
`BookTranslation` contains the `Language` property, which contains a language code for translation and a reference to the multi-lingual entity. We also have the `BookId` foreign key to help us know which book is translated.
Implement `IMultiLingualObject` in the `Book` class as follows:
```csharp
public class Book : AuditedAggregateRoot<Guid>, IMultiLingualObject<BookTranslation>
{
public Guid AuthorId { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public ICollection<BookTranslation> Translations { get; set; }
}
```
Create a folder named `MultiLingualObjects` and add the following class inside of this folder:
```csharp
public class MultiLingualObjectManager : ITransientDependency
{
protected const int MaxCultureFallbackDepth = 5;
public async Task<TTranslation> FindTranslationAsync<TMultiLingual, TTranslation>(
TMultiLingual multiLingual,
string culture = null,
bool fallbackToParentCultures = true)
where TMultiLingual : IMultiLingualObject<TTranslation>
where TTranslation : class, IObjectTranslation
{
culture ??= CultureInfo.CurrentUICulture.Name;
if (multiLingual.Translations.IsNullOrEmpty())
{
return null;
}
var translation = multiLingual.Translations.FirstOrDefault(pt => pt.Language == culture);
if (translation != null)
{
return translation;
}
if (fallbackToParentCultures)
{
translation = GetTranslationBasedOnCulturalRecursive(
CultureInfo.CurrentUICulture.Parent,
multiLingual.Translations,
0
);
if (translation != null)
{
return translation;
}
}
return null;
}
protected TTranslation GetTranslationBasedOnCulturalRecursive<TTranslation>(
CultureInfo culture, ICollection<TTranslation> translations, int currentDepth)
where TTranslation : class, IObjectTranslation
{
if (culture == null ||
culture.Name.IsNullOrWhiteSpace() ||
translations.IsNullOrEmpty() ||
currentDepth > MaxCultureFallbackDepth)
{
return null;
}
var translation = translations.FirstOrDefault(pt => pt.Language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase));
return translation ?? GetTranslationBasedOnCulturalRecursive(culture.Parent, translations, currentDepth + 1);
}
}
```
With `MultiLingualObjectManager`'s `FindTranslationAsync` method, we get the translated version of the book according to `CurrentUICulture`. If no translation of culture is found, we return null.
> Every thread in .NET has `CurrentCulture` and `CurrentUICulture` objects.
#### Acme.BookStore.EntityFrameworkCore
In the `OnModelCreating` method of the `BookStoreDbContext` class, configure the `BookTranslation` as follows:
```csharp
builder.Entity<BookTranslation>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "BookTranslations",
BookStoreConsts.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new {x.BookId, x.Language});
});
```
> I haven't explicitly set up a one-to-many relationship between `Book` and `BookTranslation` here, but the entity framework will do it for us.
After that, you can just run the following command in a command-line terminal to add a new database migration (in the directory of the `EntityFrameworkCore` project):
```bash
dotnet ef migrations add Added_BookTranslation
```
This will add a new migration class to your project. You can then run the following command (or run the `.DbMigrator` application) to apply changes to the database:
```bash
dotnet ef database update
```
Add the following code to the `ConfigureServices` method of the `BookStoreEntityFrameworkCoreModule`:
```csharp
Configure<AbpEntityOptions>(options =>
{
options.Entity<Book>(bookOptions =>
{
bookOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Translations);
});
});
```
Now we can use `WithDetailsAsync` without any parameters on `BookAppService` knowing that `Translations` will be included.
#### Acme.BookStore.Application.Contracts
Implement `IObjectTranslation` in the `BookDto` class as follows:
```csharp
public class BookDto : AuditedEntityDto<Guid>, IObjectTranslation
{
public Guid AuthorId { get; set; }
public string AuthorName { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public string Language { get; set; }
}
```
`Language` property is required to understand which language the translated book name belongs to in the UI.
Create the `AddBookTranslationDto` class in the `Books` folder as follows:
```csharp
public class AddBookTranslationDto : IObjectTranslation
{
[Required]
public string Language { get; set; }
[Required]
public string Name { get; set; }
}
```
Add the `AddTranslationsAsync` method to the `IBookAppService` as follows:
```csharp
public interface IBookAppService :
ICrudAppService<
BookDto,
Guid,
PagedAndSortedResultRequestDto,
CreateUpdateBookDto>
{
Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync();
Task AddTranslationsAsync(Guid id, AddBookTranslationDto input); // added this line
}
```
#### Acme.BookStore.Application
Now, we need to implement the `AddTranslationsAsync` method in `BookAppService` and include `Translations` in the `Book` entity, for this you can change the `BookAppService` as follows:
```csharp
[Authorize(BookStorePermissions.Books.Default)]
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto>, //Used to create/update a book
IBookAppService //implement the IBookAppService
{
private readonly IAuthorRepository _authorRepository;
public BookAppService(
IRepository<Book, Guid> repository,
IAuthorRepository authorRepository)
: base(repository)
{
_authorRepository = authorRepository;
GetPolicyName = BookStorePermissions.Books.Default;
GetListPolicyName = BookStorePermissions.Books.Default;
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Create;
}
public override async Task<BookDto> GetAsync(Guid id)
{
//Get the IQueryable<Book> from the repository
var queryable = await Repository.WithDetailsAsync(); // this line changed
//Prepare a query to join books and authors
var query = from book in queryable
join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
where book.Id == id
select new { book, author };
//Execute the query and get the book with author
var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
if (queryResult == null)
{
throw new EntityNotFoundException(typeof(Book), id);
}
var bookDto = ObjectMapper.Map<Book, BookDto>(queryResult.book);
bookDto.AuthorName = queryResult.author.Name;
return bookDto;
}
public override async Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
//Get the IQueryable<Book> from the repository
var queryable = await Repository.WithDetailsAsync(); // this line changed
//Prepare a query to join books and authors
var query = from book in queryable
join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
select new {book, author};
//Paging
query = query
.OrderBy(NormalizeSorting(input.Sorting))
.Skip(input.SkipCount)
.Take(input.MaxResultCount);
//Execute the query and get a list
var queryResult = await AsyncExecuter.ToListAsync(query);
//Convert the query result to a list of BookDto objects
var bookDtos = queryResult.Select(x =>
{
var bookDto = ObjectMapper.Map<Book, BookDto>(x.book);
bookDto.AuthorName = x.author.Name;
return bookDto;
}).ToList();
//Get the total count with another query
var totalCount = await Repository.GetCountAsync();
return new PagedResultDto<BookDto>(
totalCount,
bookDtos
);
}
public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
{
var authors = await _authorRepository.GetListAsync();
return new ListResultDto<AuthorLookupDto>(
ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
);
}
public async Task AddTranslationsAsync(Guid id, AddBookTranslationDto input)
{
var queryable = await Repository.WithDetailsAsync();
var book = await AsyncExecuter.FirstOrDefaultAsync(queryable, x => x.Id == id);
if (book.Translations.Any(x => x.Language == input.Language))
{
throw new UserFriendlyException($"Translation already available for {input.Language}");
}
book.Translations.Add(new BookTranslation
{
BookId = book.Id,
Name = input.Name,
Language = input.Language
});
await Repository.UpdateAsync(book);
}
private static string NormalizeSorting(string sorting)
{
if (sorting.IsNullOrEmpty())
{
return $"book.{nameof(Book.Name)}";
}
if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
{
return sorting.Replace(
"authorName",
"author.Name",
StringComparison.OrdinalIgnoreCase
);
}
return $"book.{sorting}";
}
}
```
Create the `MultiLingualBookObjectMapper` class as follows:
```csharp
public class MultiLingualBookObjectMapper : IObjectMapper<Book, BookDto>, ITransientDependency
{
private readonly MultiLingualObjectManager _multiLingualObjectManager;
private readonly ISettingProvider _settingProvider;
public MultiLingualBookObjectMapper(
MultiLingualObjectManager multiLingualObjectManager,
ISettingProvider settingProvider)
{
_multiLingualObjectManager = multiLingualObjectManager;
_settingProvider = settingProvider;
}
public BookDto Map(Book source)
{
var translation = AsyncHelper.RunSync(() =>
_multiLingualObjectManager.FindTranslationAsync<Book, BookTranslation>(source));
return new BookDto
{
Id = source.Id,
AuthorId = source.AuthorId,
Type = source.Type,
Name = translation?.Name ?? source.Name,
PublishDate = source.PublishDate,
Price = source.Price,
Language = translation?.Language ?? AsyncHelper.RunSync(() => _settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage)),
CreationTime = source.CreationTime,
CreatorId = source.CreatorId,
LastModificationTime = source.LastModificationTime,
LastModifierId = source.LastModifierId
};
}
public BookDto Map(Book source, BookDto destination)
{
return default;
}
}
```
To map the multi-lingual `Book` entity to `BookDto`, we implement custom mapping using the `IObjectMapper<TSource, TDestination>` interface. If no translation is found, default values are returned.
So far we have created the entire infrastructure. We don't need to change anything in the UI, if there is a translation according to the language chosen by the user, the list view will change. However, I want to create a simple modal where we can add new translations to an existing book in order to see what we have done.
#### Acme.BookStore.Web
Create a new razor page named `AddTranslationModal` in the `Books` folder as below.
**View**
```html
@page
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Web.Pages.Books.AddTranslationModal
@{
Layout = null;
}
<form asp-page="/Books/AddTranslationModal">
<abp-modal>
<abp-modal-header>Translations</abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id"></abp-input>
<abp-select asp-for="@Model.TranslationViewModel.Language" asp-items="Model.Languages" class="form-select">
<option selected value="">Pick a language</option>
</abp-select>
<abp-input asp-for="TranslationViewModel.Name"></abp-input>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel | AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</form>
```
**Model**
```csharp
public class AddTranslationModal : BookStorePageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid Id { get; set; }
public List<SelectListItem> Languages { get; set; }
[BindProperty]
public BookTranslationViewModel TranslationViewModel { get; set; }
private readonly IBookAppService _bookAppService;
private readonly ILanguageProvider _languageProvider;
public AddTranslationModal(
IBookAppService bookAppService,
ILanguageProvider languageProvider)
{
_bookAppService = bookAppService;
_languageProvider = languageProvider;
}
public async Task OnGetAsync()
{
Languages = await GetLanguagesSelectItem();
TranslationViewModel = new BookTranslationViewModel();
}
public async Task<IActionResult> OnPostAsync()
{
await _bookAppService.AddTranslationsAsync(Id, ObjectMapper.Map<BookTranslationViewModel, AddBookTranslationDto>(TranslationViewModel));
return NoContent();
}
private async Task<List<SelectListItem>> GetLanguagesSelectItem()
{
var result = await _languageProvider.GetLanguagesAsync();
return result.Select(
languageInfo => new SelectListItem
{
Value = languageInfo.CultureName,
Text = languageInfo.DisplayName + " (" + languageInfo.CultureName + ")"
}
).ToList();
}
public class BookTranslationViewModel
{
[Required]
[SelectItems(nameof(Languages))]
public string Language { get; set; }
[Required]
public string Name { get; set; }
}
}
```
Then, we can open the `BookStoreWebAutoMapperProfile` class and define the required mapping as follows:
```csharp
CreateMap<AddTranslationModal.BookTranslationViewModel, AddBookTranslationDto>();
```
Finally, change the content of `index.js` in the `Books` folder as follows:
```javascript
$(function () {
var l = abp.localization.getResource('BookStore');
var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');
var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal');
var addTranslationModal = new abp.ModalManager(abp.appPath + 'Books/AddTranslationModal'); // added this line
var dataTable = $('#BooksTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
serverSide: true,
paging: true,
order: [[1, "asc"]],
searching: false,
scrollX: true,
ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),
columnDefs: [
{
title: l('Actions'),
rowAction: {
items:
[
{
text: l('Edit'),
visible: abp.auth.isGranted('BookStore.Books.Edit'),
action: function (data) {
editModal.open({ id: data.record.id });
}
},
{
text: l('Add Translation'), // added this action
visible: abp.auth.isGranted('BookStore.Books.Edit'),
action: function (data) {
addTranslationModal.open({ id: data.record.id });
}
},
{
text: l('Delete'),
visible: abp.auth.isGranted('BookStore.Books.Delete'),
confirmMessage: function (data) {
return l(
'BookDeletionConfirmationMessage',
data.record.name
);
},
action: function (data) {
acme.bookStore.books.book
.delete(data.record.id)
.then(function() {
abp.notify.info(
l('SuccessfullyDeleted')
);
dataTable.ajax.reload();
});
}
}
]
}
},
{
title: l('Name'),
data: "name"
},
{
title: l('Author'),
data: "authorName"
},
{
title: l('Type'),
data: "type",
render: function (data) {
return l('Enum:BookType:' + data);
}
},
{
title: l('PublishDate'),
data: "publishDate",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString();
}
},
{
title: l('Price'),
data: "price"
},
{
title: l('CreationTime'),
data: "creationTime",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_SHORT);
}
}
]
})
);
createModal.onResult(function () {
dataTable.ajax.reload();
});
editModal.onResult(function () {
dataTable.ajax.reload();
});
$('#NewBookButton').click(function (e) {
e.preventDefault();
createModal.open();
});
});
```
## Conclusion
With a multi-lingual application, you can expand your market share, but if not designed well, may your application will be unusable. So, I've tried to explain how to design a sustainable multi-lingual entity in this article.
### Source Code
You can find source code of the example solution used in this article [here](https://github.com/abpframework/abp-samples/tree/master/AcmeBookStoreMultiLingual).

BIN
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/data-model.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/result.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

4
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md

@ -1,9 +1,13 @@
# How to Add Custom Properties to the User Entity
> **Note:** If your application is greater than version 4.3.3, please follow [this article](../2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md).
## Introduction
In this step-by-step article, I will explain how you can customize the user entity class, which is available in every web application you create using the ABP framework, according to your needs. When you read this article, you will learn how to override the services of built-in modules, extend the entities, extend data transfer objects and customize the user interface in the applications you develop using the ABP framework.
> **Note:** This article is not about customizing the `Login` page. If you have such a need, please follow [this article](../2020-05-09-Customize-the-Login-Page-for-MVC-Razor-Page-Applications/POST.md).
You can see the screenshots below which we will reach at the end of the article.
![custom-identity-user-list](./custom-identity-user-list.png)

154
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md

@ -0,0 +1,154 @@
# How to Add Custom Properties to the User Entity
> **Note:** If your application is less than version 4.4.x, please follow [this article](../2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md).
## Introduction
In this step-by-step article, I will explain how you can customize the user entity class, which is available in every web application you create using the ABP framework, according to your needs. When you read this article, you will learn how to override the services of built-in modules, extend the entities, extend data transfer objects and customize the user interface in the applications you develop using the ABP framework.
> **Note:** This article is not about customizing the `Login` page. If you have such a need, please follow [this article](../2020-05-09-Customize-the-Login-Page-for-MVC-Razor-Page-Applications/POST.md).
You can see the screenshots below which we will reach at the end of the article.
![custom-identity-user-list](./custom-identity-user-list.png)
![new-user](./new-user.png)
## Preparing the Project
### Startup template and the initial run
Abp Framework offers startup templates to get into the work faster. We can create a new startup template using Abp CLI:
`abp new CustomizeUserDemo`
> In this article, I will go through the MVC application, but it will work also in the [Angular](https://docs.abp.io/en/abp/latest/Getting-Started?UI=NG&DB=EF&Tiered=No), [Blazor Server](https://docs.abp.io/en/abp/latest/Getting-Started?UI=BlazorServer&DB=EF&Tiered=No), and [Blazor WebAssembly](https://docs.abp.io/en/abp/latest/Getting-Started?UI=Blazor&DB=EF&Tiered=No) application.
After the download is finished, we can run **CustomizeUserDemo.DbMigrator** project to create the database migrations and seed the initial data (admin user, role, etc). Then we can run `CustomizeUserDemo.Web` to see that our application is working.
> Default admin username is **admin** and password is **1q2w3E\***
![initial-project](./initial-project.png)
In this article, we will go through a scenario together and find the solutions to our questions through this scenario. However, since the scenario is not a real-life scenario, it may be strange, please don't get too about this issue :)
## Step-1
Create the Users folder in the **CustomizeUserDemo.Domain.Shared** project, create the class `UserConsts` inside the folder and update the class you created as below:
```csharp
public static class UserConsts
{
public const string TitlePropertyName = "Title";
public const string ReputationPropertyName = "Reputation";
public const int MaxTitleLength = 64;
public const double MaxReputationValue = 1_000;
public const double MinReputationValue = 1;
}
```
## Step-2
Update the `CustomizeUserDemoEfCoreEntityExtensionMappings` class in the **CustomizeUserDemo.EntityFramework** project in the EntityFrameworkCore folder as below:
```csharp
public static class CustomizeUserDemoEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
CustomizeUserDemoGlobalFeatureConfigurator.Configure();
CustomizeUserDemoModuleExtensionConfigurator.Configure();
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
UserConsts.TitlePropertyName,
(_, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue("");
propertyBuilder.HasMaxLength(UserConsts.MaxTitleLength);
}
).MapEfCoreProperty<IdentityUser, int>(
UserConsts.ReputationPropertyName,
(_, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(UserConsts.MinReputationValue);
}
);
});
}
}
```
This class can be used to map these extra properties to table fields in the database. Please read [this](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities) article to improve your understanding of what we are doing.
So far, we have added our extra features to the `User` entity and matched these features with the `ef core`.
Now we need to add migration to see what has changed in our database. This for, open the Package Manager Console (PMC) under the menu Tools > NuGet Package Manager.
![nuget-package-manager](./nuget-package-manager.png)
Select the **CustomizeUserDemo.EntityFramework** as the **default project** and execute the following command:
```bash
Add-Migration "Updated-User-Entity"
```
![added-new-migration](./added-new-migration.png)
This will create a new migration class inside the `Migrations` folder of the **CustomizeUserDemo.EntityFrameworkCore** project.
> If you are using another IDE than the Visual Studio, you can use `dotnet-ef` tool as [documented here](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#create-a-migration).
Finally, run the **CustomizeUserDemo.DbMigrator** project to update the database.
When we updated the database, you can see that the `Title` and `Reputation` columns are added to the `Users` table.
![user-table](./user-table.png)
## Step-3
Open the `CustomizeUserDemoModuleExtensionConfigurator` in the **CustomizeUserDemo.Domain.Shared** project, and change the contents of the `ConfigureExtraProperties` method as shown below:
```csharp
private static void ConfigureExtraProperties()
{
ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>(
UserConsts.TitlePropertyName,
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(UserConsts.MaxTitleLength)
);
}
);
user.AddOrUpdateProperty<int>(
UserConsts.ReputationPropertyName,
options =>
{
options.DefaultValue = UserConsts.MinReputationValue;
options.Attributes.Add(
new RangeAttribute(UserConsts.MinReputationValue, UserConsts.MaxReputationValue)
);
}
);
});
});
}
```
That's it. Now let's run the application and look at the Identity user page. You can also try to edit and recreate a record if you want, it will work even though we haven't done anything extra. Here is the magic code behind ABP framework.
If there is a situation you want to add, you can click the contribute button or make a comment. Also, if you like the article, don't forget to share it :)
Happy coding :)

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

452
docs/en/Deploy-azure-app-service.md

@ -0,0 +1,452 @@
# Deploying ABP Project to Azure App Service
In this document, you will learn how to create and deploy your first ABP web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. ABP web apps are cross-platform and can be hosted on Linux, Windows or MacOS.
****Prerequisites****
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
- A GitHub account [Create an account for free](http://github.com/).
## Creating a new ABP application
Create a repository on [GitHub.com](https://github.com/) (keep all settings as default).
Open the command prompt and clone the repository into a folder on your computer
```bash
git clone https://github.com/your-username/your-repository-name.git
```
Check your dotnet version. It should be at least 3.1.x
```bash
dotnet --version
```
Install or update the [ABP CLI](https://docs.abp.io/en/abp/latest/cli) with the following command:
```bash
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
```
Open the command prompt in the *GitHub repository folder* and create a new ABP Blazor solution with the command below:
```bash
abp new YourAppName -u blazor
```
## Running the application
Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations:
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project:
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project:
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project and you should see the landing page.
Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
## Committing to GitHub
Before the GitHub commit, you have to delete the line "**/wwwroot/libs/*" at *.gitignore* file.
![azdevops-23](images/azdevops-23.png)
Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository:
```bash
git add .
git commit -m initialcommit
git push
```
## Configuring Azure database connection string
Create a SQL database on Azure and change the connection string in all the *appsettings.json* files.
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *SQL Database*
* Click the **Create** button in the *SQL Database window*
* Create a new resource group. Name it *rg[YourAppName]*
* Enter *[YourAppName]Db* as database name
* Create a new Server and name it *[yourappname]server*
* Enter a serveradmin login and passwords. Click the **OK** button
* Select your *Location*
* Check *Allow Azure services to access server*
* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
* Click the **Review + create** button. Click **Create**
* Click **Go to resource** and click **SQL server** when the SQL Database is created
* Click **Networking** under Security left side menu
* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
* Select **Allow Azure and resources to access this seerver** and save
* Go to your **SQL database**, click **Connection strings** and copy the connection string
* Copy/paste the *appsettings.json* files of the *[YourAppName].HttpApi.Host* and the *[YourAppName].DbMigrator* project
* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
## Running DB Migrations
Open the command prompt in the *[YourAppName].DbMigrator* project again and enter the command below to apply the database migrations:
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project and enter the command below to check your API is working:
```bash
dotnet run
```
Stop the *[YourAppName].HttpApi.Host* by pressing CTRL+C.
## Committing to GitHub
Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Setting up the Build pipeline in AzureDevops and publish the Build Artifacts
* Sign in Azure DevOps
* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
* Select **Public visibility** and click the **Create project** button
* Click the **Pipelines** button to continue
* Click the **Create Pipeline** button
Select GitHub in the Select your repository window
![azdevops-1](images/azdevops-1.png)
* Enter the Connection name. *[YourAppName]GitHubConnection* and click **Authorize using OAuth**
* Select your **GitHub** [YourAppName]repo and click Continue
* Search for **ASP.NET** in the ***Select a template*** window
![azdevops-2](images/azdevops-2.png)
* Select the ASP.NET Core template and click the **Apply** button
* Add the below commands block as a first step in the pipeline
```
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
```
![azdevops-18](images/azdevops-18.png)
* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
![azdevops-3](images/azdevops-3.png)
* Add the below commands block to the end of the pipeline
```
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
condition: succeededOrFailed()
```
![azdevops-4](images/azdevops-4.png)
```
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'NuGet.config'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
publishLocation: 'Container'
condition: succeededOrFailed()
```
* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
* When the Build pipeline has finished. Click **1 published; 1 consumed**
## Creating a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
* Search for Web App in the *Search the Marketplace* field
* Click the **Create** button in the Web App window
* Select rg[YourAppName] in the *Resource Group* dropdown
* Enter [YourAppName]API in the *Name input* field
* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
* Enter [YourAppName]API in the *Name input* field
* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
* Select Windows as *Operating System*
* Select the same *Region* as in the SQL server you created in Part 3
![azdevops-5](images/azdevops-5.png)
* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
![azdevops-6](images/azdevops-6.png)
* Click the **Review + create** button. Click the **Create** button
* Click **Go to resource** when the Web App has been created
## Creating a release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
* Click the **New pipeline** button in the *No release pipelines found* window
* Select *Azure App Service deployment* and click the **Apply** button
![azdevops-7](images/azdevops-7.png)
* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
* Click **+ Add an artifact** in the *Pipeline* tab
* Select the **Build** icon as *Source type* in the *Add an artifact* window
* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
![azdevops-8](images/azdevops-8.png)
* Click the **Continuous deployment trigger (thunderbolt icon)**
* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
![azdevops-9](images/azdevops-9.png)
* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
* Select your subscription in the *Azure subscription* dropdown.
![azdevops-10](images/azdevops-10.png)
* Click **Authorize** and enter your credentials in the next screens
* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
* Click the **Deploy Azure App Service** task
* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
![azdevops-11](images/azdevops-11.png)
* Click the **Save** icon in the top menu and click **OK**
* Click **Create release** in the top menu. Click **Create** to create a release
* Click the *Pipeline* tab and wait until the Deployment succeeds
![azdevops-12](images/azdevops-12.png)
* Open a browser and navigate to the URL of your Web App
```
https://[YourAppName]api.azurewebsites.net
```
![azdevops-13](images/azdevops-13.png)
## Creating a Web App in Azure Portal to deploy [YourAppName].Blazor project
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *Web App* in the *Search the Marketplace* field
* Click the **Create** button in the *Web App* window
* Select *rg[YourAppName]* in the *Resource Group* dropdown
* Enter *[YourAppName]Blazor* in the *Name* input field
* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
* Select *Windows* as *Operating System*
* Select the same region as the SQL server you created in Part 3
* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
![azdevops-14](images/azdevops-14.png)
* Click the **Review + create** button. Click **Create** button
* Click **Go to resource** when the Web App has been created
* Copy the URL of the Blazor Web App for later use
```
https://[YourAppName]blazor.azurewebsites.net
```
## Changing the Web App configuration for the Azure App Service
Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
![azdevops-19](images/azdevops-19.png)
![azdevops-20](images/azdevops-20.png)
![azdevops-21](images/azdevops-21.png)
## Adding an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
* Click the **+ Add** link and add a **New Stage**
![azdevops-15](images/azdevops-15.png)
* Select *Azure App Service deployment* and click the **Apply** button
* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
* Select your subscription in the *Azure subscription* dropdown
* Select your Blazor Web App in the *App service name* dropdown
* Click the **Deploy Azure App Service task**
* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
![azdevops-16](images/azdevops-16.png)
* Click **Save** in the top menu and click the **OK** button after
* Click **Create release** in the top menu and click the **Create** button
![azdevops-17](images/azdevops-17.png)
![azdevops-22](images/azdevops-22.png)

425
docs/en/Deployment/Deploy-Azure-App-Service.md

@ -0,0 +1,425 @@
# Deploy Abp Webapp to Azure App Service
> In this document, you'll learn how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. Abp web apps are cross-platform and can be hosted on Linux and Windows.
****Prerequisites****
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
- A GitHub account [Create an account for free](http://github.com/).
## Create a new ABP Framework application
Create a repository on [GitHub.com](https://github.com/) (keep all the default settings)
Open the command prompt and clone the repository into a folder on your computer
```bash
git clone https://github.com/your-username/your-repository-name.git
```
Check your dotnet version. It should be at least 3.1.x
```bash
dotnet --version
```
Install or update the *ABP CLI* using a command line window
```bash
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
```
Open the command prompt in the *GitHub repository folder* and create a *new abp Blazor solution* with the command below
```bash
abp new YourAppName -u blazor
```
Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project. You should get the *ABP Framework Welcome window*
Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
Before the github commit, you have to delete "**/wwwroot/libs/*" at *.gitignore* file.
![azdevops-23](../images/azdevops-23.png)
Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Create a SQL Database on Azure and change the connection string in the appsettings.json files
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *SQL Database*
* Click the **Create** button in the *SQL Database window*
* Create a new resource group. Name it *rg[YourAppName]*
* Enter *[YourAppName]Db* as database name
* Create a new Server and name it *[yourappname]server*
* Enter a serveradmin login and passwords. Click the **OK** button
* Select your *Location*
* Check *Allow Azure services to access server*
* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
* Click the **Review + create** button. Click **Create**
* Click **Go to resource** and click **SQL server** when the SQL Database is created
* Click **Networking** under Security left side menu
* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
* Select **Allow Azure and resources to access this seerver** and save
* Go to your **SQL database**, click **Connection strings** and copy the connection string
* Copy/paste the appsettings.json files of the [YourAppName].HttpApi.Host and the [YourAppName].DbMigrator project
* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
Open the command prompt in the [YourAppName].DbMigrator project again and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the [YourAppName].HttpApi.Host project and enter the command below to check your API is working
```bash
dotnet run
```
Stop the [YourAppName].HttpApi.Host by entering CTRL+C
Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Set up the Build pipeline in AzureDevops and publish the Build Artifacts
* Sign in into Azure DevOps
* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
* Select **Public visibility** and click the **Create project** button
* Click the **Pipelines** button to continue
* Click the **Create Pipeline** button
Select GitHub in the Select your repository window
![azdevops-1](../images/azdevops-1.png)
* Enter the Connection name. [YourAppName]GitHubConnection and click **Authorize using OAuth**
* Select your **GitHub** [YourAppName]repo and click Continue
* Search for **ASP.NET** in the ***Select a template*** window
![azdevops-2](../images/azdevops-2.png)
* Select the ASP.NET Core template and click the **Apply** button
* Add the below commands block as a first step in the pipeline
```
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
```
![azdevops-18](../images/azdevops-18.png)
* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
![azdevops-3](../images/azdevops-3.png)
* Add the below commands block to the end of the pipeline
```
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
condition: succeededOrFailed()
```
![azdevops-4](../images/azdevops-4.png)
```
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'NuGet.config'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
publishLocation: 'Container'
condition: succeededOrFailed()
```
* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
* When the Build pipeline has finished. Click **1 published; 1 consumed**
## Create a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
* Search for Web App in the *Search the Marketplace* field
* Click the **Create** button in the Web App window
* Select rg[YourAppName] in the *Resource Group* dropdown
* Enter [YourAppName]API in the *Name input* field
* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
* Enter [YourAppName]API in the *Name input* field
* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
* Select Windows as *Operating System*
* Select the same *Region* as in the SQL server you created in Part 3
![azdevops-5](../images/azdevops-5.png)
* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
![azdevops-6](../images/azdevops-6.png)
* Click the **Review + create** button. Click the **Create** button
* Click **Go to resource** when the Web App has been created
## Create a Release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
* Click the **New pipeline** button in the *No release pipelines found* window
* Select *Azure App Service deployment* and click the **Apply** button
![azdevops-7](../images/azdevops-7.png)
* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
* Click **+ Add an artifact** in the *Pipeline* tab
* Select the **Build** icon as *Source type* in the *Add an artifact* window
* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
![azdevops-8](../images/azdevops-8.png)
* Click the **Continuous deployment trigger (thunderbolt icon)**
* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
![azdevops-9](../images/azdevops-9.png)
* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
* Select your subscription in the *Azure subscription* dropdown.
![azdevops-10](../images/azdevops-10.png)
* Click **Authorize** and enter your credentials in the next screens
* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
* Click the **Deploy Azure App Service** task
* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
![azdevops-11](../images/azdevops-11.png)
* Click the **Save** icon in the top menu and click **OK**
* Click **Create release** in the top menu. Click **Create** to create a release
* Click the *Pipeline* tab and wait until the Deployment succeeds
![azdevops-12](../images/azdevops-12.png)
* Open a browser and navigate to the URL of your Web App
```
https://[YourAppName]api.azurewebsites.net
```
![azdevops-13](../images/azdevops-13.png)
## Create a Web App in Azure Portal to deploy [YourAppName].Blazor project
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *Web App* in the *Search the Marketplace* field
* Click the **Create** button in the *Web App* window
* Select *rg[YourAppName]* in the *Resource Group* dropdown
* Enter *[YourAppName]Blazor* in the *Name* input field
* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
* Select *Windows* as *Operating System*
* Select the same region as the SQL server you created in Part 3
* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
![azdevops-14](../images/azdevops-14.png)
* Click the **Review + create** button. Click **Create** button
* Click **Go to resource** when the Web App has been created
* Copy the URL of the Blazor Web App for later use
```
https://[YourAppName]blazor.azurewebsites.net
```
## Change the Web App configuration for the Azure App Service
Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
![azdevops-19](../images/azdevops-19.png)
![azdevops-20](../images/azdevops-20.png)
![azdevops-21](../images/azdevops-21.png)
## Add an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
* Click the **+ Add** link and add a **New Stage**
![azdevops-15](../images/azdevops-15.png)
* Select *Azure App Service deployment* and click the **Apply** button
* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
* Select your subscription in the *Azure subscription* dropdown
* Select your Blazor Web App in the *App service name* dropdown
* Click the **Deploy Azure App Service task**
* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
![azdevops-16](../images/azdevops-16.png)
* Click **Save** in the top menu and click the **OK** button after
* Click **Create release** in the top menu and click the **Create** button
![azdevops-17](../images/azdevops-17.png)
![azdevops-22](../images/azdevops-22.png)

5
docs/en/Deployment/Index.md

@ -6,4 +6,7 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Deploy Abp Webapp to Azure App Service](Deploy-Azure-App-Service.md): Explains how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview).

12
docs/en/Modules/OpenIddict.md

@ -4,6 +4,14 @@
This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
## How to Install
This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
### The Source Code
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
## Relations to Other Modules
This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
@ -206,6 +214,10 @@ If you need to customize OpenIddict, you need to replace/delete/add new handlers
Please refer to:
https://documentation.openiddict.com/guides/index.html#events-model
## Migrating Guide
[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)
## Sponsor
Please consider sponsoring this project: https://github.com/sponsors/kevinchalet

11
docs/en/Modules/Setting-Management.md

@ -118,7 +118,13 @@ The order of the providers are important. Providers are executed in the reverse
## Setting Management UI
Setting Mangement module provided the email setting UI by default, and it is extensible; You can add your tabs to this page for your application settings.
Setting Mangement module provided the email setting UI by default.
![EmailSettingUi](../images/setting-management-email-ui.png)
> You can click the Send test email button to send a test email to check your email settings.
Setting it is extensible; You can add your tabs to this page for your application settings.
### MVC UI
@ -302,5 +308,4 @@ export class AppComponent {
Navigate to `/setting-management` route to see the changes:
![Custom Settings Tab](../images/custom-settings.png)
![Custom Settings Tab](../images/custom-settings.png)

3
docs/en/UI/Angular/Theming.md

@ -16,10 +16,11 @@ In order to accomplish these goals, ABP Framework;
### Current Themes
Currently, two themes are **officially provided**:
Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/angular) and [lite](../../Themes/LeptonXLite/Angular.md) choices.
## Overall

3
docs/en/UI/AspNetCore/Overall.md

@ -32,10 +32,11 @@ ABP Framework provides a complete [Theming](Theming.md) system with the followin
### Current Themes
Currently, two themes are **officially provided**:
Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/mvc) and [lite](../../Themes/LeptonXLite/AspNetCore.md) choices.
There are also some community-driven themes for the ABP Framework (you can search on the web).

8
docs/en/UI/AspNetCore/Page-Header.md

@ -23,6 +23,8 @@ Page Title can be set as shown in the example below:
### Breadcrumb
> **The [Basic Theme](Basic-Theme.md) currently doesn't implement the breadcrumbs.**
>
> The [LeptonX Lite Theme](../../Themes/LeptonXLite/AspNetCore.md) supports breadcrumbs.
Breadcrumb items can be added to the `PageLayout.Content.BreadCrumb`.
@ -48,11 +50,13 @@ Any item that you add is inserted between Home and Current Page items. You can a
### The Selected Menu Item
> **The [Basic Theme](Basic-Theme.md) currently doesn't implement the selected menu item since it is not applicable to the top menu which is the only option for the Basic Theme for now.**
>
> The [LeptonX Lite Theme](../../Themes/LeptonXLite/AspNetCore.md) supports selected menu item.
You can set the Menu Item name related to this page:
````csharp
```csharp
PageLayout.Content.MenuItemName = "BookStore.Books";
````
```
Menu item name should match a unique menu item name defined using the [Navigation / Menu](Navigation-Menu.md) system. In this case, it is expected from the theme to make the menu item "active" in the main menu.

6
docs/en/UI/AspNetCore/Toolbars.md

@ -8,6 +8,12 @@ There is only one **standard toolbar** named "Main" (defined as a constant: `Sta
In the screenshot above, there are two items added to the main toolbar: Language switch component & user menu. You can add your own items here.
Also, [LeptonX Lite Theme](../../Themes/LeptonXLite/AspNetCore.md) has 2 different toolbars for desktop and mobile views which defined as constants: `LeptonXLiteToolbars.Main`, `LeptonXLiteToolbars.MainMobile`.
| LeptonXLiteToolbars.Main | LeptonXLiteToolbars.MainMobile |
| :---: | :---: |
| ![leptonx](../../images/leptonxlite-toolbar-main-example.png) | ![leptonx](../../images/leptonxlite-toolbar-mainmobile-example.png) |
## Example: Add a Notification Icon
In this example, we will add a **notification (bell) icon** to the left of the language switch item. A item in the toolbar should be a **view component**. So, first, create a new view component in your project:

3
docs/en/UI/Blazor/Overall.md

@ -75,10 +75,11 @@ ABP Framework provides a complete [Theming](Theming.md) system with the followin
### Current Themes
Currently, two themes are **officially provided**:
Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has both [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/blazor) and [lite](../../Themes/LeptonXLite/Blazor.md) choices.
### Base Libraries

2
docs/en/UI/Blazor/Page-Header.md

@ -16,6 +16,8 @@ Once you add the `PageHeader` component to your page, you can control the relate
## Breadcrumb
> **The [Basic Theme](Basic-Theme.md) currently doesn't implement the breadcrumbs.**
>
> The [LeptonX Lite Theme](../../Themes/LeptonXLite/Blazor.md) supports breadcrumbs.
Breadcrumbs can be added using the `BreadcrumbItems` property.

4
docs/en/UI/Blazor/Page-Layout.md

@ -42,6 +42,10 @@ Menu item name can be set on runtime too.
}
```
![leptonx selected menu item](../../images/leptonx-selected-menu-item-example.gif)
> Be aware, The [Basic Theme](../Blazor/Basic-Theme.md) currently doesn't support the selected menu item since it is not applicable to the top menu.
## BreadCrumbs

6
docs/en/UI/Blazor/Toolbars.md

@ -8,6 +8,12 @@ There is only one **standard toolbar** named "Main" (defined as a constant: `Sta
In the screenshot above, there are two items added to the main toolbar: Language switch component & user menu. You can add your own items here.
Also, [LeptonX Lite Theme](../../Themes/LeptonXLite/Blazor.md) has 2 different toolbars for desktop and mobile views which defined as constants: `LeptonXLiteToolbars.Main`, `LeptonXLiteToolbars.MainMobile`.
| LeptonXLiteToolbars.Main | LeptonXLiteToolbars.MainMobile |
| :---: | :---: |
| ![leptonx](../../images/leptonxlite-toolbar-main-example.png) | ![leptonx](../../images/leptonxlite-toolbar-mainmobile-example.png) |
## Example: Add a Notification Icon
In this example, we will add a **notification (bell) icon** to the left of the language switch item. A item in the toolbar should be a **Razor Component**. So, first, create a new razor component in your project (the location of the component doesn't matter):

8
docs/en/docs-nav.json

@ -43,6 +43,10 @@
"text": "WPF Application",
"path": "Startup-Templates/WPF.md"
},
{
"text": "MAUI",
"path": "Startup-Templates/MAUI.md"
},
{
"text": "Empty Web Project",
"path": "Getting-Started-AspNetCore-Application.md"
@ -1278,6 +1282,10 @@
{
"text": "Deploying to a Clustered Environment",
"path": "Deployment/Clustered-Environment.md"
},
{
"text": "Deploy Abp Webapp to Azure App Service",
"path": "Deployment/Deploy-Azure-App-Service.md"
}
]
},

BIN
docs/en/images/azdevops-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

BIN
docs/en/images/azdevops-10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
docs/en/images/azdevops-11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

BIN
docs/en/images/azdevops-12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
docs/en/images/azdevops-13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

BIN
docs/en/images/azdevops-14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
docs/en/images/azdevops-15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
docs/en/images/azdevops-16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

BIN
docs/en/images/azdevops-17.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

BIN
docs/en/images/azdevops-18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
docs/en/images/azdevops-19.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

BIN
docs/en/images/azdevops-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

BIN
docs/en/images/azdevops-20.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

BIN
docs/en/images/azdevops-21.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

BIN
docs/en/images/azdevops-22.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
docs/en/images/azdevops-23.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

BIN
docs/en/images/azdevops-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
docs/en/images/azdevops-4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

BIN
docs/en/images/azdevops-5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

BIN
docs/en/images/azdevops-6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

BIN
docs/en/images/azdevops-7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
docs/en/images/azdevops-8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

BIN
docs/en/images/azdevops-9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

BIN
docs/en/images/breadcrumbs-example.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
docs/en/images/leptonx-selected-menu-item-example.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

BIN
docs/en/images/leptonxlite-toolbar-main-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/images/leptonxlite-toolbar-mainmobile-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/images/setting-management-email-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

1
docs/zh-Hans/CLI.md

@ -123,6 +123,7 @@ abp new Acme.BookStore
* `module`: [Module template](Startup-Templates/Module.md). 其他选项:
* `--no-ui`: 不包含UI.仅创建服务模块(也称为微服务 - 没有UI).
* **`console`**: [Console template](Startup-Templates/Console.md).
* **`maui`**: [Maui template](Startup-Templates/MAUI.md).
* **`app-nolayers`**: 应用程序单层模板
* `--ui` 或者 `-u`: 指定ui框架.默认`mvc`框架.其他选项:
* `mvc`: ASP.NET Core MVC.

8
docs/zh-Hans/Modules/Setting-Management.md

@ -87,7 +87,13 @@ namespace Demo
## Setting Management UI.
设置管理模块默认提供了邮件设置页面并且它是可扩展的; 你可以为你的应用程序设置添加设置标签到设置页面.
设置管理模块默认提供了邮件设置页面.
![EmailSettingUi](../images/setting-management-email-ui.png)
> 你可以点击发送测试邮件按钮发送一封测试邮件来检查你的邮件设置.
设置UI是可扩展的; 你可以为你的应用程序设置添加设置标签到设置页面.
### MVC UI

4
docs/zh-Hans/docs-nav.json

@ -61,6 +61,10 @@
{
"text": "WPF",
"path": "Startup-Templates/WPF.md"
},
{
"text": "MAUI",
"path": "Startup-Templates/MAUI.md"
}
]
},

BIN
docs/zh-Hans/images/setting-management-email-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj

@ -26,7 +26,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
<ItemGroup>

37
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs

@ -1,11 +1,14 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
namespace Volo.Abp.AspNetCore.Auditing;
@ -57,28 +60,46 @@ public class AspNetCoreAuditLogContributor : AuditLogContributor, ITransientDepe
public override void PostContribute(AuditLogContributionContext context)
{
if (context.AuditInfo.HttpStatusCode != null)
{
return;
}
var httpContext = context.ServiceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
if (httpContext == null)
{
return;
}
if (context.AuditInfo.HttpStatusCode == null)
if (context.AuditInfo.Exceptions.Any())
{
context.AuditInfo.HttpStatusCode = httpContext.Response.StatusCode;
var httpExceptionStatusCodeFinder = context.ServiceProvider.GetRequiredService<IHttpExceptionStatusCodeFinder>();
foreach (var auditInfoException in context.AuditInfo.Exceptions)
{
var statusCode = httpExceptionStatusCodeFinder.GetStatusCode(httpContext, auditInfoException);
context.AuditInfo.HttpStatusCode = (int) statusCode;
}
if (context.AuditInfo.HttpStatusCode != null)
{
return;
}
}
context.AuditInfo.HttpStatusCode = httpContext.Response.StatusCode;
}
protected virtual string BuildUrl(HttpContext httpContext)
{
//TODO: Add options to include/exclude query, schema and host
var uriBuilder = new UriBuilder();
uriBuilder.Scheme = httpContext.Request.Scheme;
uriBuilder.Host = httpContext.Request.Host.Host;
uriBuilder.Path = httpContext.Request.Path.ToString();
uriBuilder.Query = httpContext.Request.QueryString.ToString();
var uriBuilder = new UriBuilder
{
Scheme = httpContext.Request.Scheme,
Host = httpContext.Request.Host.Host,
Path = httpContext.Request.Path.ToString(),
Query = httpContext.Request.QueryString.ToString()
};
return uriBuilder.Uri.AbsolutePath;
}

2
framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj

@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
</Project>

34
framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs

@ -15,18 +15,18 @@ namespace Volo.Abp.Caching.StackExchangeRedis;
[DisableConventionalRegistration]
public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
{
protected static readonly string SetScript;
protected static readonly string AbsoluteExpirationKey;
protected static readonly string SlidingExpirationKey;
protected static readonly string DataKey;
protected static readonly long NotPresent;
private static readonly FieldInfo RedisDatabaseField;
private static readonly MethodInfo ConnectMethod;
private static readonly MethodInfo ConnectAsyncMethod;
private static readonly MethodInfo MapMetadataMethod;
private static readonly MethodInfo GetAbsoluteExpirationMethod;
private static readonly MethodInfo GetExpirationInSecondsMethod;
private readonly static FieldInfo SetScriptField;
private readonly static FieldInfo RedisDatabaseField;
private readonly static MethodInfo ConnectMethod;
private readonly static MethodInfo ConnectAsyncMethod;
private readonly static MethodInfo MapMetadataMethod;
private readonly static MethodInfo GetAbsoluteExpirationMethod;
private readonly static MethodInfo GetExpirationInSecondsMethod;
protected IDatabase RedisDatabase => GetRedisDatabase();
private IDatabase _redisDatabase;
@ -36,9 +36,11 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
static AbpRedisCache()
{
var type = typeof(RedisCache);
RedisDatabaseField = type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic);
SetScriptField = type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic);
ConnectMethod = type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic);
ConnectAsyncMethod = type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic);
@ -51,9 +53,6 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
GetExpirationInSecondsMethod =
type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic);
SetScript = type.GetField("SetScript", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null)
.ToString();
AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)
?.GetValue(null).ToString();
@ -83,14 +82,14 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
ConnectMethod.Invoke(this, Array.Empty<object>());
}
protected virtual Task ConnectAsync(CancellationToken token = default)
protected virtual async Task ConnectAsync(CancellationToken token = default)
{
if (GetRedisDatabase() != null)
{
return Task.CompletedTask;
return;
}
return (Task)ConnectAsyncMethod.Invoke(this, new object[] { token });
await (Task)ConnectAsyncMethod.Invoke(this, new object[] { token });
}
public byte[][] GetMany(
@ -283,7 +282,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
for (var i = 0; i < itemArray.Length; i++)
{
tasks[i] = RedisDatabase.ScriptEvaluateAsync(SetScript, new RedisKey[] { Instance + itemArray[i].Key },
tasks[i] = RedisDatabase.ScriptEvaluateAsync(GetSetScript(), new RedisKey[] { Instance + itemArray[i].Key },
new RedisValue[]
{
absoluteExpiration?.Ticks ?? NotPresent,
@ -333,4 +332,9 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return _redisDatabase;
}
private string GetSetScript()
{
return SetScriptField?.GetValue(this).ToString();
}
}

2
framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
<ItemGroup>

8
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -85,7 +85,13 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
ConfigureNpmPackagesForTheme(projectArgs);
await RunGraphBuildForMicroserviceServiceTemplate(projectArgs);
await CreateInitialMigrationsAsync(projectArgs);
await RunInstallLibsForWebTemplateAsync(projectArgs);
var skipInstallLibs = commandLineArgs.Options.ContainsKey(Options.SkipInstallingLibs.Long) || commandLineArgs.Options.ContainsKey(Options.SkipInstallingLibs.Short);
if (!skipInstallLibs)
{
await RunInstallLibsForWebTemplateAsync(projectArgs);
}
await ConfigurePwaSupportForAngular(projectArgs);
OpenRelatedWebPage(projectArgs, template, isTiered, commandLineArgs);

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs

@ -709,6 +709,12 @@ public abstract class ProjectCreationCommandBase
public const string Long = "create-solution-folder";
}
public static class SkipInstallingLibs
{
public const string Short = "sib";
public const string Long = "skip-installing-libs";
}
public static class Tiered
{
public const string Long = "tiered";

32
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs

@ -285,14 +285,12 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
{"Domain.Shared", "MyCompanyName.MyProjectName.Domain.Shared.csproj"},
{"Application", "MyCompanyName.MyProjectName.Application.csproj"},
{"Application.Contracts", "MyCompanyName.MyProjectName.Application.Contracts.csproj"},
{"Blazor.WebAssembly", "MyCompanyName.MyProjectName.Blazor.csproj"},
{"Blazor.Server", "MyCompanyName.MyProjectName.Blazor.Server.csproj"},
{"HttpApi", "MyCompanyName.MyProjectName.HttpApi.csproj"},
{"HttpApi.Client", "MyCompanyName.MyProjectName.HttpApi.Client.csproj"},
{"Web.Host", "MyCompanyName.MyProjectName.Web.Host.csproj"},
{"Web", "MyCompanyName.MyProjectName.Web.csproj"},
{"HttpApi.Client", "MyCompanyName.MyProjectName.HttpApi.Client.csproj"}
};
AddUiProjectToProjects(projects, context);
foreach (var project in projects)
{
AddLeptonThemeManagementReference(context, project);
@ -314,6 +312,28 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
AddLeptonThemeManagementReference(context, microserviceServiceProject);
}
}
private void AddUiProjectToProjects(Dictionary<string, string> projects, ProjectBuildContext context)
{
if (projects.IsNullOrEmpty())
{
return;
}
switch (context.BuildArgs.UiFramework)
{
case UiFramework.Mvc:
projects["Web.Host"] = "MyCompanyName.MyProjectName.Web.Host.csproj";
projects["Web"] = "MyCompanyName.MyProjectName.Web.csproj";
break;
case UiFramework.Blazor:
projects["Blazor.WebAssembly"] = "MyCompanyName.MyProjectName.Blazor.csproj";
break;
case UiFramework.BlazorServer:
projects["Blazor.Server"] = "MyCompanyName.MyProjectName.Blazor.csproj";
break;
}
}
private void AddLeptonThemeManagementReference(ProjectBuildContext context, KeyValuePair<string, string> projectInfo)
{

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs

@ -14,6 +14,8 @@ public class TemplateCodeDeleteStep : ProjectBuildPipelineStep
file.Name.EndsWith(".json") ||
file.Name.EndsWith(".gitignore") ||
file.Name.EndsWith(".yml") ||
file.Name.EndsWith(".yaml") ||
file.Name.EndsWith(".md") ||
file.Name.EndsWith(".ps1") ||
file.Name.EndsWith(".html") ||
file.Name.EndsWith(".ts") ||

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs

@ -130,6 +130,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo
steps.Add(new RemoveFolderStep("/apps/blazor"));
steps.Add(new RemoveProjectFromTyeStep("blazor"));
steps.Add(new RemoveProjectFromTyeStep("blazor-server"));
context.Symbols.Add("ui:angular");
break;
case UiFramework.Blazor:
@ -147,7 +149,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo
null,
"/apps/blazor/src/MyCompanyName.MyProjectName.Blazor.Server"));
steps.Add(new RemoveProjectFromTyeStep("blazor-server"));
context.Symbols.Add("ui:blazor");
break;
case UiFramework.BlazorServer:
@ -169,7 +172,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo
steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.Blazor.Server",
"MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RenameProjectInTyeStep("blazor-server", "blazor"));
context.Symbols.Add("ui:blazor-server");
break;
case UiFramework.Mvc:
@ -186,6 +190,8 @@ public abstract class MicroserviceTemplateBase : TemplateInfo
steps.Add(new RemoveProjectFromTyeStep("blazor-server"));
steps.Add(new RemoveFolderStep("/apps/angular"));
context.Symbols.Add("ui:mvc");
break;
}

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -489,6 +489,7 @@ public class SolutionModuleAdder : ITransientDependency
args.Options.Add("t", newProTemplate ? ModuleProTemplate.TemplateName : ModuleTemplate.TemplateName);
args.Options.Add("v", version);
args.Options.Add("o", Path.Combine(modulesFolderInSolution, module.Name));
args.Options.Add("sib", true.ToString());
await NewCommand.ExecuteAsync(args);
}

2
framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj

@ -18,7 +18,7 @@
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>

10
framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs

@ -21,6 +21,16 @@ public class AbpConfigurationBuilderOptions
/// </summary>
public string FileName { get; set; } = "appsettings";
/// <summary>
/// Whether the file is optional, Default value: true.
/// </summary>
public bool Optional { get; set; } = true;
/// <summary>
/// Whether the configuration should be reloaded if the file changes, Default value: true.
/// </summary>
public bool ReloadOnChange { get; set; } = true;
/// <summary>
/// Environment name. Generally used "Development", "Staging" or "Production".
/// </summary>

4
framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/ConfigurationHelper.cs

@ -18,11 +18,11 @@ public static class ConfigurationHelper
var builder = new ConfigurationBuilder()
.SetBasePath(options.BasePath)
.AddJsonFile(options.FileName + ".json", optional: true, reloadOnChange: true);
.AddJsonFile(options.FileName + ".json", optional: options.Optional, reloadOnChange: options.ReloadOnChange);
if (!options.EnvironmentName.IsNullOrEmpty())
{
builder = builder.AddJsonFile($"{options.FileName}.{options.EnvironmentName}.json", optional: true, reloadOnChange: true);
builder = builder.AddJsonFile($"{options.FileName}.{options.EnvironmentName}.json", optional: options.Optional, reloadOnChange: options.ReloadOnChange);
}
if (options.EnvironmentName == "Development")

6
framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj

@ -15,10 +15,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftPackageVersion)" />

21
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs

@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.DependencyInjection;
public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDependency
[ExposeServices(typeof(IAbpLazyServiceProvider))]
public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyServiceProvider, ITransientDependency
{
protected IDictionary<Type, object> CachedServices { get; set; }
protected IServiceProvider ServiceProvider { get; set; }
public AbpLazyServiceProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{
ServiceProvider = serviceProvider;
CachedServices = new Dictionary<Type, object>();
}
public virtual T LazyGetRequiredService<T>()
@ -23,7 +17,7 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetRequiredService(Type serviceType)
{
return CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetRequiredService(serviceType));
return GetService(serviceType);
}
public virtual T LazyGetService<T>()
@ -33,7 +27,7 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetService(Type serviceType)
{
return CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetService(serviceType));
return GetService(serviceType);
}
public virtual T LazyGetService<T>(T defaultValue)
@ -53,6 +47,9 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory)
{
return CachedServices.GetOrAdd(serviceType, () => factory(ServiceProvider));
return CachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => factory(ServiceProvider))
).Value;
}
}

16
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs

@ -5,21 +5,21 @@ namespace Volo.Abp.DependencyInjection;
public abstract class CachedServiceProviderBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<Type, Lazy<object>> _cachedServices;
protected IServiceProvider ServiceProvider { get; }
protected ConcurrentDictionary<Type, Lazy<object>> CachedServices { get; }
protected CachedServiceProviderBase(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_cachedServices = new ConcurrentDictionary<Type, Lazy<object>>();
_cachedServices.TryAdd(typeof(IServiceProvider), new Lazy<object>(() => _serviceProvider));
ServiceProvider = serviceProvider;
CachedServices = new ConcurrentDictionary<Type, Lazy<object>>();
CachedServices.TryAdd(typeof(IServiceProvider), new Lazy<object>(() => ServiceProvider));
}
public virtual object GetService(Type serviceType)
{
return _cachedServices.GetOrAdd(
return CachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => _serviceProvider.GetService(serviceType))
_ => new Lazy<object>(() => ServiceProvider.GetService(serviceType))
).Value;
}
}
}

2
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/pt-BR.json

@ -9,7 +9,7 @@
"DisplayName:Abp.Mailing.Smtp.Password": "Senha",
"DisplayName:Abp.Mailing.Smtp.Domain": "Domínio",
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "Habilitar SSL",
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Use credenciais padrão",
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Usar credenciais padrão",
"Description:Abp.Mailing.DefaultFromAddress": "O endereço de origem padrão",
"Description:Abp.Mailing.DefaultFromDisplayName": "O nome de exibição padrão",
"Description:Abp.Mailing.Smtp.Host": "O nome ou endereço IP do host usado para transações SMTP.",

2
framework/src/Volo.Abp.ExceptionHandling/Volo.Abp.ExceptionHandling.csproj

@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
</Project>

6
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json

@ -1,10 +1,10 @@
{
"culture": "pt-BR",
"texts": {
"Languages": "línguas",
"Languages": "Idiomas",
"AreYouSure": "Você tem certeza?",
"Cancel": "Cancelar",
"Clear": "Claro",
"Clear": "Limpar",
"Yes": "Sim",
"No": "Não",
"Ok": "OK",
@ -16,7 +16,7 @@
"SuccessfullyDeleted": "Excluído com sucesso",
"Edit": "Editar",
"Refresh": "Atualizar",
"Language": "Língua",
"Language": "Idioma",
"LoadMore": "Carregar mais",
"ProcessingWithThreeDot": "Processando...",
"LoadingWithThreeDot": "Carregando...",

2
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json

@ -24,6 +24,8 @@
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "Este campo deve ser do tipo palavra ou matriz com comprimento mínimo de '{0}'.",
"ThisFieldIsNotAValidPhoneNumber.": "Número de telefone inválido.",
"ThisFieldMustBeBetween{0}And{1}": "Este campo deve estar entre {0} e {1}.",
"ThisFieldMustBeGreaterThanOrEqual{0}": "Este campo deve ser maior ou igual a {0}.",
"ThisFieldMustBeLessOrEqual{0}": "Este campo deve ser menor ou igual a {0}.",
"ThisFieldMustMatchTheRegularExpression{0}": "Este campo deve ser compatível com a expressão regular: '{0}'.",
"ThisFieldIsRequired.": "Campo Obrigatório.",
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "Campo com no máximo {0} caracteres.",

2
framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj

@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
<ItemGroup>

2
framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
<ItemGroup>

2
framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj

@ -12,7 +12,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring.Aliyun\Volo.Abp.BlobStoring.Aliyun.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<ProjectReference Include="..\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj" />
</ItemGroup>

50
framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AbpLazyServiceProvider_Tests.cs

@ -0,0 +1,50 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing.Utils;
using Xunit;
namespace Volo.Abp.DependencyInjection;
public class AbpLazyServiceProvider_Tests
{
[Fact]
public void LazyServiceProvider_Should_Cache_Services()
{
using (var application = AbpApplicationFactory.Create<TestModule>())
{
application.Initialize();
var lazyServiceProvider = application.ServiceProvider.GetRequiredService<IAbpLazyServiceProvider>();
var transientTestService1 = lazyServiceProvider.LazyGetRequiredService<TransientTestService>();
var transientTestService2 = lazyServiceProvider.LazyGetRequiredService<TransientTestService>();
transientTestService1.ShouldBeSameAs(transientTestService2);
var testCounter = application.ServiceProvider.GetRequiredService<ITestCounter>();
testCounter.GetValue(nameof(TransientTestService)).ShouldBe(1);
}
}
[DependsOn(typeof(AbpTestBaseModule))]
private class TestModule : AbpModule
{
public TestModule()
{
SkipAutoServiceRegistration = true;
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddType<TransientTestService>();
}
}
private class TransientTestService : ITransientDependency
{
public TransientTestService(ITestCounter counter)
{
counter.Increment(nameof(TransientTestService));
}
}
}

2
framework/test/Volo.Abp.Sms.Aliyun.Tests/Volo.Abp.Sms.Aliyun.Tests.csproj

@ -12,7 +12,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Sms.Aliyun\Volo.Abp.Sms.Aliyun.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
</ItemGroup>

2
framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj

@ -16,7 +16,7 @@
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
</Project>

2
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.csproj

@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
</Project>

18
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/AbpAccountApplicationContractsModule.cs

@ -3,6 +3,9 @@ using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.ObjectExtending.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
@ -13,6 +16,8 @@ namespace Volo.Abp.Account;
)]
public class AbpAccountApplicationContractsModule : AbpModule
{
private readonly static OneTimeRunner OneTimeRunner = new OneTimeRunner();
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
@ -33,4 +38,17 @@ public class AbpAccountApplicationContractsModule : AbpModule
options.MapCodeNamespace("Volo.Account", typeof(AccountResource));
});
}
public override void PostConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToApi(
IdentityModuleExtensionConsts.ModuleName,
IdentityModuleExtensionConsts.EntityNames.User,
getApiTypes: new[] { typeof(ProfileDto) },
updateApiTypes: new[] { typeof(UpdateProfileDto) }
);
});
}
}

2
modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj

@ -25,7 +25,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
</Project>

3
modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorAutoMapperProfile.cs

@ -10,10 +10,11 @@ public class AbpAccountBlazorAutoMapperProfile : Profile
public AbpAccountBlazorAutoMapperProfile()
{
CreateMap<ProfileDto, PersonalInfoModel>()
.MapExtraProperties()
.Ignore(x => x.PhoneNumberConfirmed)
.Ignore(x => x.EmailConfirmed);
CreateMap<PersonalInfoModel, UpdateProfileDto>()
.Ignore(x => x.ExtraProperties);
.MapExtraProperties();
}
}

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

Loading…
Cancel
Save