Browse Source

Merge branch 'dev' into pr/14861

pull/14861/head
maliming 4 years ago
parent
commit
cec6e51992
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 14
      .github/pull_request_template.md
  2. 16
      .github/workflows/auto-pr.yml
  3. 1
      .github/workflows/image-compression.yml
  4. 31
      .github/workflows/spellcheck.yml
  5. 4
      .gitignore
  6. 4
      Directory.Build.props
  7. 2
      README.md
  8. 45
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  9. 25
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  10. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
  11. 20
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  12. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
  13. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
  14. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  15. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  16. 16
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  17. 151
      cSpell.json
  18. 2
      common.props
  19. 7
      docs/en/API/Application-Configuration.md
  20. 33
      docs/en/API/Application-Localization.md
  21. 318
      docs/en/Application-Startup.md
  22. 4
      docs/en/Audit-Logging.md
  23. 22
      docs/en/Background-Workers-Hangfire.md
  24. 75
      docs/en/Blog-Posts/2023-01-04 v7_0_Release_Stable/POST.md
  25. 207
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/POST.md
  26. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/community-talks-conver-image.png
  27. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/cover-image.png
  28. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/ndc-london.png
  29. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/saas-impersonation-1.png
  30. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/saas-impersonation-2.png
  31. BIN
      docs/en/Blog-Posts/2023-02-08 v7_1_Preview/suite-blazor-wasm-nolayers.png
  32. 2
      docs/en/CLI.md
  33. 13
      docs/en/Caching.md
  34. 5
      docs/en/Community-Articles/2022-11-25-JSON-columns/post.md
  35. 71
      docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md
  36. 208
      docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md
  37. 231
      docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md
  38. BIN
      docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png
  39. 68
      docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/Post.md
  40. BIN
      docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/login-with-weixin.jpg
  41. BIN
      docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/offical-account.jpg
  42. 105
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/how-can-I-contribute-to-open-source-projects-on-github.md
  43. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/branch-image.png
  44. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/clone-image.png
  45. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/commit-image.png
  46. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/developer-list.png
  47. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/fork-project-image.png
  48. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/fork-project-profile.png
  49. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/git-push-image.png
  50. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/github-desktop-change.png
  51. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/github-desktop-pull-request.png
  52. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/open-pull-request-image.png
  53. BIN
      docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/pull-request-image.png
  54. 86
      docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/POST.md
  55. BIN
      docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/images/new.gif
  56. BIN
      docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/images/old.gif
  57. 2
      docs/en/Customizing-Application-Modules-Extending-Entities.md
  58. 98
      docs/en/Dependency-Injection.md
  59. 2
      docs/en/Deployment/Clustered-Environment.md
  60. 104
      docs/en/Deployment/Configuring-Production.md
  61. 45
      docs/en/Deployment/Distributed-Microservice.md
  62. 5
      docs/en/Deployment/Index.md
  63. 22
      docs/en/Deployment/Optimizing-Production.md
  64. 91
      docs/en/Distributed-Event-Bus.md
  65. 23
      docs/en/Entities.md
  66. 131
      docs/en/Entity-Cache.md
  67. 20
      docs/en/Entity-Framework-Core.md
  68. 2
      docs/en/Getting-Started-AspNetCore-Application.md
  69. 14
      docs/en/Getting-Started-React-Native.md
  70. 10
      docs/en/Getting-Started-Running-Solution.md
  71. 2
      docs/en/Getting-Started-Setup-Environment.md
  72. 2
      docs/en/Getting-Started.md
  73. 80
      docs/en/Integration-Services.md
  74. 6
      docs/en/JSON.md
  75. 10
      docs/en/KB/Windows-Path-Too-Long-Fix.md
  76. 110
      docs/en/Migration-Guides/Abp-7_0.md
  77. 19
      docs/en/Migration-Guides/Abp-7_1.md
  78. 230
      docs/en/Migration-Guides/IdentityServer4-Step-by-Step.md
  79. 1
      docs/en/Migration-Guides/Index.md
  80. 2
      docs/en/Migration-Guides/OpenIddict-Angular.md
  81. 2
      docs/en/Migration-Guides/OpenIddict-Blazor-Server.md
  82. 8
      docs/en/Migration-Guides/OpenIddict-Blazor.md
  83. 2
      docs/en/Migration-Guides/OpenIddict-Mvc.md
  84. 2
      docs/en/Migration-Guides/OpenIddict-Step-by-Step.md
  85. 1
      docs/en/Module-Development-Basics.md
  86. 4
      docs/en/Module-Entity-Extensions.md
  87. 2
      docs/en/Modules/Account.md
  88. 2
      docs/en/Modules/Audit-Logging.md
  89. 2
      docs/en/Modules/Background-Jobs.md
  90. 6
      docs/en/Modules/Cms-Kit/Blogging.md
  91. 25
      docs/en/Modules/Cms-Kit/Comments.md
  92. 6
      docs/en/Modules/Cms-Kit/Global-Resources.md
  93. 7
      docs/en/Modules/Cms-Kit/Index.md
  94. 18
      docs/en/Modules/Cms-Kit/Menus.md
  95. 8
      docs/en/Modules/Cms-Kit/Pages.md
  96. 8
      docs/en/Modules/Cms-Kit/Ratings.md
  97. 8
      docs/en/Modules/Cms-Kit/Reactions.md
  98. 8
      docs/en/Modules/Cms-Kit/Tags.md
  99. 575
      docs/en/Modules/Database-Tables.md
  100. 42
      docs/en/Modules/Docs.md

14
.github/pull_request_template.md

@ -0,0 +1,14 @@
### Description
Resolves #xxxx (write the related issue number if available)
TODO: Describe what this PR has changed, add screenshot or animated GIF if available, write if it is a breaking change, and how to fix the breaking changes for existing applications if so.
### Checklist
- [ ] I fully tested it as developer / designer and created unit / integration tests
- [ ] I documented it (or no need to document or I will create a separate documentation issue)
### How to test it?
Please describe how this can be tested by the test engineers if it is not already explicit - or remove this section if no need to description.

16
.github/workflows/auto-pr.yml

@ -1,13 +1,13 @@
name: Merge branch dev with rel-7.0
name: Merge branch dev with rel-7.1
on:
push:
branches:
- rel-7.0
- rel-7.1
permissions:
contents: read
jobs:
merge-dev-with-rel-6-0:
merge-dev-with-rel-7-1:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
@ -18,13 +18,13 @@ jobs:
ref: dev
- name: Reset promotion branch
run: |
git fetch origin rel-7.0:rel-7.0
git reset --hard rel-7.0
git fetch origin rel-7.1:rel-7.1
git reset --hard rel-7.1
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-7-0/${{github.run_number}}
title: Merge branch dev with rel-7.0
body: This PR generated automatically to merge dev with rel-7.0. Please review the changed files before merging to prevent any errors that may occur.
branch: auto-merge/rel-7-1/${{github.run_number}}
title: Merge branch dev with rel-7.1
body: This PR generated automatically to merge dev with rel-7.1. Please review the changed files before merging to prevent any errors that may occur.
reviewers: ${{github.actor}}
token: ${{ github.token }}

1
.github/workflows/image-compression.yml

@ -6,6 +6,7 @@ on:
- "**.jpeg"
- "**.png"
- "**.webp"
- "**.gif"
types:
- opened
- synchronize

31
.github/workflows/spellcheck.yml

@ -1,31 +0,0 @@
name: Documentation Checks
on:
push:
branches:
- dev
paths:
# This ensures the check will only be run when something changes in the docs content
- "docs/en/**/*"
pull_request:
branches:
- dev
paths:
- "docs/en/**/*"
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
spellcheck:
name: "Docs: Spellcheck (En)"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
name: Check out the code
- uses: actions/setup-node@v1
name: Setup node
with:
node-version: "16"
- run: npm install -g cspell
name: Install cSpell
- run: cspell --config ./cSpell.json "docs/en/**/*.md" --no-progress # Update for path to the markdown files
name: Run cSpell

4
.gitignore

@ -326,4 +326,6 @@ deploy/_run_all_log.txt
# No commit yarn.lock files in the subfolders of templates directory
templates/**/yarn.lock
templates/**/yarn.lock
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Logs/logs.txt
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Properties/launchSettings.json

4
Directory.Build.props

@ -5,7 +5,7 @@
<MicrosoftAspNetCorePackageVersion>7.0.0</MicrosoftAspNetCorePackageVersion>
<!-- All Microsoft EntityFrameworkCore packages -->
<MicrosoftEntityFrameworkCorePackageVersion>7.0.0</MicrosoftEntityFrameworkCorePackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>7.0.1</MicrosoftEntityFrameworkCorePackageVersion>
<!-- All Microsoft packages -->
<MicrosoftPackageVersion>7.0.0</MicrosoftPackageVersion>
@ -17,7 +17,7 @@
<NSubstitutePackageVersion>4.3.0</NSubstitutePackageVersion>
<!-- Shouldly https://www.nuget.org/packages/Shouldly -->
<ShouldlyPackageVersion>4.0.3</ShouldlyPackageVersion>
<ShouldlyPackageVersion>4.1.0</ShouldlyPackageVersion>
<!-- xunit https://www.nuget.org/packages/xUnit -->
<xUnitPackageVersion>2.4.1</xUnitPackageVersion>

2
README.md

@ -1,6 +1,6 @@
# ABP Framework
![build and test](https://img.shields.io/github/workflow/status/abpframework/abp/build%20and%20test/dev?style=flat-square)
![build and test](https://img.shields.io/github/actions/workflow/status/abpframework/abp/build-and-test.yml?branch=dev&style=flat-square)
[![codecov](https://codecov.io/gh/abpframework/abp/branch/dev/graph/badge.svg?token=jUKLCxa6HF)](https://codecov.io/gh/abpframework/abp)
[![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
[![NuGet (with prereleases)](https://img.shields.io/nuget/vpre/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)

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

@ -409,6 +409,49 @@
"OverallDiscountPrice": "Overall Discount Price",
"OverallDiscountText": "Overall Discount Text",
"SelectReport": "- Select Report -",
"NoDataAvailable": "No data available"
"NoDataAvailable": "No data available",
"StatisticsOfCachedContents": "Statistics of cached NuGet package contents for nuget.abp.io",
"Compact": "Compact",
"EditSettings": "Edit Settings",
"CurrentEstimatedSize": "Current Estimated Size",
"CurrentEntryCount": "Current Entry Count",
"TotalHits": "Total Hits",
"TotalMisses": "Total Misses",
"NoResponseFrom": "No response from",
"ContentCacheSlidingExpirationByDay": "Content Cache Sliding Expiration By Day",
"MaxDaysForCaching": "Max Days For Caching",
"Enabled": "Enabled",
"Menu:NugetPackagesContentCache": "NuGet Cache",
"NugetPackagesContentCache": "NuGet Content Cache",
"SlidingExpritionByDayInfo": "Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration.",
"MaxDaysForCachingInfo": "Gets or sets an absolute expiration time, relative to now.",
"CurrentEstimatedSizeInfo": "Indicates an estimated sum of all the NuGet packages' content size currently in the memory cache",
"CurrentEntryCountInfo": "Indicates the number of instances currently in the memory cache.",
"TotalHitsInfo": "Indicates the total number of cache misses. A cache hit occurs when a file is requested from a cache and the cache is able to fulfill that request.",
"TotalMissesInfo": "Indicates the total number of cache hits. A cache miss is when the cache does not contain the requested content.",
"Permission:VersionHistory": "Version History",
"Caches": "Caches",
"VersionHistories": "Version History",
"Version": "Version",
"PublishDate": "Publish Date",
"IsStableVersion": "Stable Version",
"IsActive": "Active",
"NewVersion": "New Version",
"VersionHistoryDeletionConfirmationMessage": "Are you sure you want to delete this version?",
"CreateAbpConsultantLogoInfo": "Maximum file size: <b>1MB</b> <br/> Supported file types: <b>jpg, jpeg, png, SVG, WebP</b>",
"UrlCode": "Url Code",
"Clear": "Clear",
"Permission:AbpConsultant": "ABP Consultant",
"Menu:AbpConsultants": "ABP Consultants",
"CreateAbpConsultant": "Create ABP Consultant",
"UrlCodeIsNotAvailable": "Url code is used by another ABP Consultant.",
"AbpConsultants": "ABP Consultants",
"AbpConsultant": "ABP Consultant",
"AbpConsultantEdit": "Edit ABP Consultant",
"AbpConsultantCreate": "Create ABP Consultant",
"WhoWeAreItem": "Who We Are Item",
"FieldIsRequired": "{0} is required.",
"FieldIsNotValid": " {0} is not valid.",
"InterestedLicenseType": "Interested License Type"
}
}

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

@ -26,6 +26,8 @@
"Volo.AbpIo.Domain:030009": "User not found!",
"Volo.AbpIo.Domain:030010": "To purchase the trial license, you first need to activate your trial license!",
"Volo.AbpIo.Domain:030011": "You cannot delete a trial license when it is purchased!",
"Volo.AbpIo.Domain:030012": "A user is entitled to have only 1 free trial period. You already used your trial license.",
"Volo.AbpIo.Domain:030013": "A user with an active license cannot start a trial license.",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"WantToLearn?": "Want to learn?",
@ -183,6 +185,27 @@
"BlackFriday": "<strong>BLACK</strong> <span>FRIDAY</span>",
"ValidForExistingCustomers": "Also valid for the <br> existing customers!",
"CampaignBetweenDates": "From {0} <br>to {1}",
"SaveUpTo": "<span>SAVE</span> UP TO<strong>${0}K</strong>"
"SaveUpTo": "<span>SAVE</span> UP TO<strong>${0}K</strong>",
"ImplementingDDD": "Implementing Domain Driven Design",
"ExploreTheEBook": "Explore the E-Book",
"ExploreTheBook": "Explore the Book",
"ConsultantType": "Consultancy Type",
"Expert": "ABP Expert",
"Partner": "ABP Partner",
"Industry": "Industry",
"Location": "Location",
"Contact": "Contact",
"Partner_Year": "Partnership Year",
"Info": "Info",
"SpokenLanguages": "Spoken Languages",
"SocialMedia": "Social Media",
"Activity": "Activity",
"Type": "Type",
"Contribution": "Contribution",
"WhoWeAre": "Who We Are",
"Icons": "Icons",
"Url": "Url",
"Icon": "Icon",
"RecentActivities": "Recent Activities"
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json

@ -711,7 +711,7 @@
"WatchTakeCloserLookVideo": "شاهد فيديو \"Take a closer look at the code generation: ABP Suite\"!",
"ConfirmedEmailAddressRequiredToStartTrial": "يجب أن يكون لديك عنوان بريد إلكتروني مؤكد لبدء ترخيص تجريبي.",
"EmailVerificationMailNotSent": "تعذر إرسال بريد التحقق من البريد الإلكتروني.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\">انقر هنا للحصول على بريد إلكتروني للتأكيد</a> إذا لم تكن قد حصلت عليه من قبل.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\" class=\"text-decoration-underline\">انقر هنا للحصول على بريد إلكتروني للتأكيد</a> إذا لم تكن قد حصلت عليه من قبل.",
"WhichLicenseTypeYouAreInterestedIn": "ما نوع الرخصة المهتم بها؟",
"DontTakeOurWordForIt": "لا تأخذ كلمتنا لذلك...",
"ReadAbpCommercialUsersWantYouToKnow": "اقرأ ما يريد مستخدمو ABP التجاري أن تعرفه",

20
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json

@ -196,7 +196,7 @@
"ChangingDevelopers": "Can I change the registered developers of my organization in the future?",
"ChangingDevelopersExplanation": "In addition to adding new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.",
"WhatHappensWhenLicenseEnds": "What happens when my license period ends?",
"WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits:",
"WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires;",
"WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue developing your existing applications forever.",
"WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MINOR version (except RC or Preview versions). For example: if you are using v3.2.0 of a module, you can still get updates for v3.2.x (v3.2.1, v3.2.5... etc.) of that module. But you cannot get updates for the next major or minor version (like v3.3.0, v3.3.3, 4.x.x.. etc.). For example, when your license expired, the latest release was v4.4.3, and later, it published both 4.4.4 version and 4.5.0 version, you would be able to access the v4.4.X but you wouldn't be access the v4.5.X.",
"WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.",
@ -369,7 +369,6 @@
"FreeTrial": "Free Trial",
"AcceptsMarketingCommunications": " Yes, I`d like to receive ABP Commercial marketing communications.",
"PurposeOfUsage": "Purpose of usage",
"Industry": "Industry",
"Choose": "- Choose -",
"CompanyOrganizationName": "Company / Organization name",
"CompanySize": "Company size",
@ -735,7 +734,7 @@
"WatchTakeCloserLookVideo": "Watch the \"Take a closer look at the code generation: ABP Suite\" Video!",
"ConfirmedEmailAddressRequiredToStartTrial": "You should have a confirmed email address in order to start a trial license.",
"EmailVerificationMailNotSent": "Email verification mail couldn't send.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\">Click here to get a verification email</a> if you haven't got it before.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\" class=\"text-decoration-underline\">Click here to get a verification email</a> if you haven't got it before.",
"WhichLicenseTypeYouAreInterestedIn": "Which license type you are interested in?",
"DontTakeOurWordForIt": "Don't take our word for it...",
"ReadAbpCommercialUsersWantYouToKnow": "Read what ABP Commercial users want you to know",
@ -806,6 +805,19 @@
"SupportPolicyFaqTitle": "What is your support policy?",
"SupportPolicyFaqExplanation": "We do support only the active and the previous major version. We do not guarantee a patch release for the 3rd and older major versions. For example, if the active version is 7.0.0, we will release patch releases for both 6.x.x and 7.x.x. Besides, we provide support only for ABP Framework and ABP Commercial related issues. That means no support is given for the 3rd party applications, cloud services and other peripheral libraries used by ABP products. We will use commercially reasonable efforts to provide our customers with technical support during \"Volosoft Bilisim A.S\"s official business hours. On the other hand, we do not commit to a service-level agreement (SLA) response time, but we will try to respond to the technical issues as quickly as possible within our official working hours. Unless a special agreement is made with the customer, we only provide support at https://support.abp.io. We also have private email support, which is only available to Enterprise License holders.",
"TotalDevelopers": "Total {0} developer(s)",
"CustomPurchaseExplanation": "Tailored to your specific needs"
"CustomPurchaseExplanation": "Tailored to your specific needs",
"WhereDidYouHearAboutUs": "Where did you hear about us?",
"Twitter": "Twitter",
"Facebook": "Facebook",
"Youtube": "YouTube",
"Google": "Google",
"Github": "GitHub",
"Friend": " From a friend",
"Other": "Other",
"WhereDidYouHearAboutUs_explain": "Specify ...",
"DeletingMemberWarningMessage": "\"{0}\" will be removed from the developer list. If you want, you can assign this empty seat to another developer later.",
"AdditionalInfo": "If the developer seats are above your requirements, you can reduce them. You can email at <a href=\"mailto:info@abp.io\">info@abp.io</a> to remove some of your developer seats. Clearing unused developer seats will reduce the license renewal cost. If you want, you can re-purchase additional developer seats within your active license period. Note that, since there are {0} developers in this license package, you cannot reduce this number.",
"LinkExpiredErrorMessage": "The link you are trying to access is expired.",
"ExpirationDate": "Expiration Date"
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json

@ -735,7 +735,7 @@
"WatchTakeCloserLookVideo": "Tekintse meg a „Nézze meg közelebbről a kódgenerálást: ABP Suite” videót!",
"ConfirmedEmailAddressRequiredToStartTrial": "A próbalicenc elindításához rendelkeznie kell egy megerősített e-mail címmel.",
"EmailVerificationMailNotSent": "Nem sikerült elküldeni az ellenőrző e-mailt.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\">Kattintson ide, ha megerősítő e-mailt szeretne kapni,</a> ha még nem kapta meg.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\" class=\"text-decoration-underline\">Kattintson ide, ha megerősítő e-mailt szeretne kapni,</a> ha még nem kapta meg.",
"WhichLicenseTypeYouAreInterestedIn": "Melyik licenctípus érdekli?",
"DontTakeOurWordForIt": "Ne fogadd el a szavunkat...",
"ReadAbpCommercialUsersWantYouToKnow": "Olvassa el, hogy az ABP Commercial felhasználói mit szeretnének tudni",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json

@ -744,7 +744,7 @@
"WatchTakeCloserLookVideo": "\"Kod üretimine daha yakından bakın: ABP Suite\" videosunu izleyin!",
"ConfirmedEmailAddressRequiredToStartTrial": "Deneme lisansı başlatmak için onaylanmış bir e -posta adresiniz olmalı.",
"EmailVerificationMailNotSent": "E-posta doğrulama postası gönderilemedi.",
"GetConfirmationEmail": "Daha önce bir onay e-postası almadıysanız <a href=\"javascript:void(0);\" id=\"{0}\"> almak için buraya tıklayın.</a>",
"GetConfirmationEmail": "Daha önce bir onay e-postası almadıysanız <a href=\"javascript:void(0);\" id=\"{0}\" class=\"text-decoration-underline\"> almak için buraya tıklayın.</a>",
"WhichLicenseTypeYouAreInterestedIn": "Hangi lisans türüyle ilgileniyorsunuz?",
"BlackFridayDiscount": "Kara Cuma İndirimi"
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json

@ -735,7 +735,7 @@
"WatchTakeCloserLookVideo": "观看“详细了解ABP Suite 的代码生成”视频!",
"ConfirmedEmailAddressRequiredToStartTrial": "你应该有一个确认的电子邮件地址,以便开始试用许可证。",
"EmailVerificationMailNotSent": "电子邮件验证邮件不能发送。",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\">点击这里获取确认邮件</a> 如果你还没有收到。",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\" class=\"text-decoration-underline\">点击这里获取确认邮件</a> 如果你还没有收到。",
"WhichLicenseTypeYouAreInterestedIn": "你感兴趣的许可证类型是什么?",
"DontTakeOurWordForIt": "不要相信我们的话......",
"ReadAbpCommercialUsersWantYouToKnow": "阅读 ABP Commercial 用户希望您了解到的内容",

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

@ -13,7 +13,6 @@
"Status": "Status",
"ContentSource": "Content Source",
"Details": "Details",
"Url": "Url",
"Title": "Title",
"CreationTime": "Creation time",
"Save": "Save",

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

@ -409,25 +409,13 @@
"SeeTheScreenshot": "See the screenshot",
"ApplicationModuleExplanation1": "Creates a reusable, fully layered application module solution.",
"ApplicationModuleExplanation2": "You can use this option to create modules for your modular application.",
"Expert": "Expert",
"Expert_": "Expert",
"Partner": "Partner",
"Partner_": "Partnership",
"WebSite": "Web Site",
"Industry": "Industry",
"Location": "Location",
"Contact": "Contact",
"ConsultantType": "Consultancy Type",
"Expert_Year": "Expertise Year",
"Partner_Year": "Partnership Year",
"SpokenLanguages": "Spoken Languages",
"SocialMedia": "Social Media",
"CompanyInfo": "Company Info",
"WhoWeAre": "Who We Are",
"RecentActivities": "Recent Activities",
"Date": "Date",
"Activity": "Activity",
"Type": "Type",
"Contribution": "Contribution"
"WhoWeAre_Partner": "Who We Are",
"WhoWeAre_Expert": "About Me"
}
}

151
cSpell.json

@ -1,151 +0,0 @@
{
"version": "0.2",
"language": "en",
"words": [
"ABP's",
"abpframework",
"Antiforgery",
"appsettings",
"aspnet",
"aspnetcore",
"Autofac",
"automagically",
"Blazor",
"CQRS",
"crossfade",
"Dapr",
"Datagrid's",
"Datatable",
"datepicker",
"dismissable",
"dockerized",
"entrypoints",
"findability",
"hoverable",
"Iddict",
"IntelliCode",
"Keysize",
"Linq",
"Microservices",
"middlewares",
"Minifier",
"multitenancy",
"multitenant",
"Navs",
"Newtonsoft",
"Npgsql",
"oidc",
"overridable",
"Parameterless",
"Passwordless",
"PKCE",
"preconfigured",
"proxying",
"redirections",
"scrollbars",
"signin",
"Templating",
"textboxes",
"toolset",
"unsubscription",
"Xunit"
],
"ignoreWords": [
"Aliyun",
"Allibone",
"Blazorise",
"Boutwell",
"Cmskit",
"connectionstrings",
"Devart",
"Formik",
"Halil",
"Hanselman",
"hikalkan",
"Ibrahim",
"İbrahim",
"Kalkan",
"Kirti",
"Kommunity",
"Kulkarni",
"Luxon",
"malihu",
"Malik",
"Masis",
"Minio",
"NGXS",
"NSWAG",
"Scriban",
"Serilog",
"Shoudly",
"Shouldly",
"Sweetalert",
"Syncfusion",
"Telerik",
"Timeago",
"Toastr",
"Volo",
"Volosoft",
"Xeevis"
],
"patterns": [
{
"name": "Markdown links",
"pattern": "\\((.*)\\)",
"description": ""
},
{
"name": "Markdown code blocks",
"pattern": "/^(\\s*`{3,}).*[\\s\\S]*?^\\1/gmx",
"description": "Taken from the cSpell example at https://cspell.org/configuration/patterns/#verbose-regular-expressions"
},
{
"name": "Inline code blocks",
"pattern": "\\`([^\\`\\r\\n]+?)\\`",
"description": "https://stackoverflow.com/questions/41274241/how-to-capture-inline-markdown-code-but-not-a-markdown-code-fence-with-regex"
},
{
"name": "Link contents",
"pattern": "\\<a(.*)\\>",
"description": ""
},
{
"name": "Snippet references",
"pattern": "-- snippet:(.*)",
"description": ""
},
{
"name": "Snippet references 2",
"pattern": "\\<\\[sample:(.*)",
"description": "another kind of snippet reference"
},
{
"name": "Multi-line code blocks",
"pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm"
},
{
"name": "HTML Tags",
"pattern": "<[^>]*>",
"description": "Reference: https://stackoverflow.com/questions/11229831/regular-expression-to-remove-html-tags-from-a-string"
},
{
"name": "Markdown Image",
"pattern": "!\\[(.*)\\]\\((.*)\\)"
}
],
"ignoreRegExpList": [
"Markdown links",
"Markdown code blocks",
"Inline code blocks",
"Link contents",
"Snippet references",
"Snippet references 2",
"Multi-line code blocks",
"HTML Tags",
"Markdown Image"
],
"ignorePaths": [
"**/*Release/Post.md",
"**/*Preview/POST.md"
]
}

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>7.1.0</Version>
<Version>7.2.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

7
docs/en/API/Application-Configuration.md

@ -2,13 +2,14 @@
ABP Framework provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, the list of some fundamental information at this endpoint:
* [Localization](../Localization.md) values, supported and the current language of the application.
* Available and granted [policies](../Authorization.md) (permissions) for the current user.
* Granted [policies](../Authorization.md) (permissions) for the current user.
* [Setting](../Settings.md) values for the current user.
* Info about the [current user](../CurrentUser.md) (like id and user name).
* Info about the current [tenant](../Multi-Tenancy.md) (like id and name).
* [Time zone](../Timing.md) information for the current user and the [clock](../Timing.md) type of the application.
> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint.
## HTTP API
If you navigate to the `/api/abp/application-configuration` URL of an ABP Framework based web application or HTTP Service, you can access the configuration as a JSON object. This endpoint is useful to create the client of your application.
@ -19,5 +20,5 @@ For ASP.NET Core MVC (Razor Pages) applications, the same configuration values a
See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI.
Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint.
Other UI types provide services native to the related platform. For example, see the [Angular UI settings documentation](../UI/Angular/Settings.md) to learn how to use the setting values exposes by this endpoint.

33
docs/en/API/Application-Localization.md

@ -0,0 +1,33 @@
# Application Localization Endpoint
ABP Framework provides a pre-built and standard endpoint that returns all the [localization](../Localization.md) resources and texts defined in the server.
> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint.
## HTTP API
`/api/abp/application-localization` is the main URL of the HTTP API that returns the localization data as a JSON string. I accepts the following query string parameters:
* `cultureName` (required): A culture code to get the localization data, like `en` or `en-US`.
* `onlyDynamics` (optional, default: `false`): Can be set to `true` to only get the dynamically defined localization resources and texts. If your client-side application shares the same localization resources with the server (like ABP's Blazor and MVC UIs), you can set `onlyDynamics` to `true`.
**Example request:**
````
/api/abp/application-localization?cultureName=en
````
## Script
For [ASP.NET Core MVC (Razor Pages)](../UI/AspNetCore/Overall.md) applications, the same localization data is also available on the JavaScript side. `/Abp/ApplicationLocalizationScript` is the URL of the script that is auto-generated based on the HTTP API above.
**Example request:**
````
/Abp/ApplicationLocalizationScript?cultureName=en
````
See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI.
Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint.

318
docs/en/Application-Startup.md

@ -0,0 +1,318 @@
## ABP Application Startup
You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application or how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you.
> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or if you need to modify how the ABP Framework starts.
While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NPM packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only need to install a single NuGet package and make a few small changes.
This document is for who wants to better understand how the ABP Framework is initialized and configured on startup.
## Installing to a Console Application
A .NET Console application is the minimalist .NET application. So, it is best to show the installing of the ABP Framework to a console application as a minimalist example.
If you [create a new console application with Visual Studio](https://learn.microsoft.com/en-us/dotnet/core/tutorials/with-visual-studio) (for .NET 7.0 or later), you will see the following solution structure (I named the solution as `MyConsoleDemo`):
![app-startup-console-initial](images/app-startup-console-initial.png)
This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code.
The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using the [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio:
````bash
Install-Package Volo.Abp.Core
````
Alternatively, you can use a command-line terminal in the root folder of the project (the folder containing the `MyConsoleDemo.csproj` file, for this example):
````bash
dotnet add package Volo.Abp.Core
````
After adding the NuGet package, we should create a root [module class](Module-Development-Basics.md) for our application. We can create the following class in the project:
````csharp
using Volo.Abp.Modularity;
namespace MyConsoleDemo
{
public class MyConsoleDemoModule : AbpModule
{
}
}
````
This is an empty class deriving from the `AbpModule` class. It is the main class that you will control your application's dependencies with, and implement your configuration and startup/shutdown logic. For more information, please check the [Modularity](Module-Development-Basics.md) document.
As the second and the last step, change the `Program.cs` as shown in the following code block:
````csharp
using MyConsoleDemo;
using Volo.Abp;
// 1: Create the ABP application container
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
// 2: Initialize/start the ABP Framework (and all the modules)
await application.InitializeAsync();
Console.WriteLine("ABP Framework has been started...");
// 3: Stop the ABP Framework (and all the modules)
await application.ShutdownAsync();
````
That's all. Now, ABP Framework is installed, integrated, started and stopped in your application. From now, you can install [ABP packages](https://abp.io/packages) to your application whenever you need them.
## Installing a Framework Package
If you want to send emails from your .NET application, you can use .NET's standard [SmtpClient class](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient). ABP also provides an `IEmailSender` service that simplifies [sending emails](Emailing.md) and configuring the email settings in a central place. If you want to use it, you should install the [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) NuGet package to your project:
````bash
dotnet add package Volo.Abp.Emailing
````
Once you add a new ABP package/module, you also need to specify the module dependency from your module class. So, change the `MyConsoleDemoModule` class as shown below:
````csharp
using Volo.Abp.Emailing;
using Volo.Abp.Modularity;
namespace MyConsoleDemo
{
[DependsOn(typeof(AbpEmailingModule))] // Added the module dependency
public class MyConsoleDemoModule : AbpModule
{
}
}
````
I've just added a `[DependsOn]` attribute to declare that I want to use the ABP Emailing Module (`AbpEmailingModule`). Now, I can use the `IEmailSender` service in my `Program.cs`:
````csharp
using Microsoft.Extensions.DependencyInjection;
using MyConsoleDemo;
using Volo.Abp;
using Volo.Abp.Emailing;
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
await application.InitializeAsync();
// Sending emails using the IEmailSender service
var emailsender = application.ServiceProvider.GetRequiredService<IEmailSender>();
await emailsender.SendAsync(
to: "info@acme.com",
subject: "Hello World",
body: "My message body..."
);
await application.ShutdownAsync();
````
> If you run that application, you get a runtime error indicating that the email sending settings haven't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it.
That's all. Install an ABP NuGet package, add the module dependency (using the `[DependsOn]` attribute) and use any service inside the NuGet package.
The [ABP CLI](CLI.md) already has a special command to perform the addition of an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command:
````bash
abp add-package Volo.Abp.Emailing
````
We suggest you to use the `abp add-package` command instead of manually doing it.
## AbpApplicationFactory
`AbpApplicationFactory` is the main class that creates an ABP application container. It provides a single static `CreateAsync` (and `Create` if you can't use asynchronous programming) method with multiple overloads. Let's investigate these overloads to understand where you can use them.
The first overload gets a generic module class parameter as we've used before in this document:
````csharp
AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
````
The generic class parameter should be the root module class of your application. All the other modules are resolved as dependencies of that module.
The second overload gets the module class as a `Type` parameter, instead of the generic parameter. So, the previous code block could be re-written as shown below:
````csharp
AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule));
````
Both overloads work exactly the same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime.
If you create one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to setup the [dependency injection](Dependency-Injection.md) system internally. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system.
The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also sets up the dependency injection system internally.
We can change the `Program.cs` as shown below to externally manage the dependency injection setup:
````csharp
using Microsoft.Extensions.DependencyInjection;
using MyConsoleDemo;
using Volo.Abp;
// 1: Manually created the IServiceCollection
IServiceCollection services = new ServiceCollection();
// 2: Pass the IServiceCollection externally to the ABP Framework
using var application = await AbpApplicationFactory
.CreateAsync<MyConsoleDemoModule>(services);
// 3: Manually built the IServiceProvider object
IServiceProvider serviceProvider = services.BuildServiceProvider();
// 4: Pass the IServiceProvider externally to the ABP Framework
await application.InitializeAsync(serviceProvider);
Console.WriteLine("ABP Framework has been started...");
await application.ShutdownAsync();
````
In this example, we've used .NET's standard dependency injection container. The `services.BuildServiceProvider()` call creates the standard container. However, ABP provides an alternative extension method, `BuildServiceProviderFromFactory()`, that properly works even if you are using another dependency injection container:
````csharp
IServiceProvider serviceProvider = services.BuildServiceProviderFromFactory();
````
> You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework.
Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as in the following code block:
````csharp
using var application = await AbpApplicationFactory
.CreateAsync(typeof(MyConsoleDemoModule), services);
````
> All of the `CreateAsync` method overloads have `Create` counterparts. If your application type can not utilize asynchronous programming (that means you can't use the `await` keyword), then you can use the `Create` method instead of the `CreateAsync` method.
### AbpApplicationCreationOptions
All of the `CreateAsync` overloads can get an optional `Action<AbpApplicationCreationOptions>` parameter to configure the options that are used on the application creation. See the following example:
````csharp
using var application = await AbpApplicationFactory
.CreateAsync<MyConsoleDemoModule>(options =>
{
options.ApplicationName = "MyApp";
});
````
We've passed a lambda method to configure the `ApplicationName` option. Here's a list of all standard options:
* `ApplicationName`: A human-readable name for the application. It is a unique value for an application.
* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with an internal service provider, you can use this option to configure how the application configuration is built.
* `Environment`: Environment name for the application.
* `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins.
* `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class.
#### The ApplicationName option
As defined above, the `ApplicationName` option is a human-readable name for the application. It is a unique value for an application.
`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) that are saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`.
The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name.
There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file:
````json
{
"ApplicationName": "Services.Ordering"
}
````
Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below:
````csharp
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options =>
{
options.ApplicationName = "Services.Ordering";
});
````
#### IApplicationInfoAccessor
If you need to access the `ApplicationName` later in your solution, you can inject the `IApplicationInfoAccessor` service and get the value from its `ApplicationName` property.
`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other.
## IAbpApplication
`AbpApplicationFactory` returns an `IAbpApplication` object from its `CreateAsync` (or `Create`) method. `IAbpApplication` is the main container for an ABP application. It is also registered to the [dependency injection](Dependency-Injection.md) system, so you can inject `IAbpApplication` in your services to use its properties and methods.
Here's a list of `IAbpApplication` properties you may want to know:
* `StartupModuleType`: Gets the root module of the application that was used while creating the application container (on the `AbpApplicationFactory.CreateAsync` method).
* `Services`: A list of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialization (you can actually add, but it won't have any effect).
* `ServiceProvider`: A reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes.
* `Modules`: A read-only list of all the modules loaded into the current application. Alternatively, you can inject the `IModuleContainer` service if you need to access the module list in your application code.
The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface, so you can get the `ApplicationName` and `InstanceId` values from it. However, if you only need to access these properties, inject and use the `IApplicationInfoAccessor` service instead.
`IAbpApplication` is disposable. Always dispose of it before exiting your application.
## IAbpHostEnvironment
Sometimes, while creating an application, we need to get the current hosting environment and take actions according to that. In such cases, we can use some services such as [IWebHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.iwebhostenvironment?view=aspnetcore-7.0) or [IWebAssemblyHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.webassembly.hosting.iwebassemblyhostenvironment) provided by .NET, in the final application.
However, we can not use these services in a class library, which is used by the final application. ABP Framework provides the `IAbpHostEnvironment` service, which allows you to get the current environment name whenever you want. `IAbpHostEnvironment` is used by the ABP Framework in several places to perform specific actions by the environment. For example, ABP Framework reduces the cache duration on the **Development** environment for some services.
`IAbpHostEnvironment` obtains the current environment name by the following order:
1. Gets and sets the environment name if it's specified in the `AbpApplicationCreationOptions`.
2. Tries to obtain the environment name from the `IWebHostEnvironment` or `IWebAssemblyHostEnvironment` services for ASP.NET Core & Blazor WASM applications if the environment name isn't specified in the `AbpApplicationCreationOptions`.
3. Sets the environment name as **Production**, if the environment name is not specified or can not be obtained from the services.
You can configure the `AbpApplicationCreationOptions` [options class](Options.md) while creating the ABP application and set an environment name to its `Environment` property. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `Environment` option as shown below:
```csharp
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options =>
{
options.Environment = Environments.Staging; //or directly set as "Staging"
});
```
Then, whenever you need to get the current environment name or check the environment, you can use the `IAbpHostEnvironment` interface:
```csharp
public class MyDemoService
{
private readonly IAbpHostEnvironment _abpHostEnvironment;
public MyDemoService(IAbpHostEnvironment abpHostEnvironment)
{
_abpHostEnvironment = abpHostEnvironment;
}
public void MyMethod()
{
var environmentName = _abpHostEnvironment.EnvironmentName;
if (_abpHostEnvironment.IsDevelopment()) { /* ... */ }
if (_abpHostEnvironment.IsStaging()) { /* ... */ }
if (_abpHostEnvironment.IsProduction()) { /* ... */ }
if (_abpHostEnvironment.IsEnvironment("custom-environment")) { /* ... */ }
}
}
```
## .NET Generic Host & ASP.NET Core Integrations
`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create an ABP application container that is well-integrated to these systems.
The [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document clearly explains how you can create an ABP application container in an ASP.NET Core application.
You can also [create a console application](Startup-Templates/Console) to see how it is integrated with .NET Generic Host.
> Most of the times, you will directly create ABP applications using the ABP CLI's `new` command. So, you don't need to care about these integration details.
## See Also
* [Dependency injection](Dependency-Injection.md)
* [Modularity](Module-Development-Basics.md)

4
docs/en/Audit-Logging.md

@ -40,12 +40,14 @@ Here, a list of the options you can configure:
* `HideErrors` (default: `true`): Audit log system hides and write regular [logs](Logging.md) if any error occurs while saving the audit log objects. If saving the audit logs is critical for your system, set this to `false` to throw exception in case of hiding the errors.
* `IsEnabledForAnonymousUsers` (default: `true`): If you want to write audit logs only for the authenticated users, set this to `false`. If you save audit logs for anonymous users, you will see `null` for `UserId` values for these users.
* `AlwaysLogOnException` (default: `true`): If you set to true, it always saves the audit log on an exception/error case without checking other options (except `IsEnabled`, which completely disables the audit logging).
* `IsEnabledForIntegrationService` (default: `false`): Audit Logging is disabled for [integration services](Integration-Services.md) by default. Set this property as `true` to enable it.
* `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests.
* `DisableLogActionInfo` (default: `false`):If you set to true, Will no longer log `AuditLogActionInfo`.
* `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications.
* `ApplicationName`: If multiple applications are saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. If you don't set, it will set from the `IApplicationInfoAccessor.ApplicationName` value, which is the entry assembly name by default.
* `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters.
* `EntityHistorySelectors`: A list of selectors those are used to determine if an entity type is selected for saving the entity change. See the section below for details.
* `Contributors`: A list of `AuditLogContributor` implementations. A contributor is a way of extending the audit log system. See the "Audit Log Contributors" section below.
* `AlwaysLogSelectors`: A list of selectors to save the audit logs for the matched criteria.
### Entity History Selectors

22
docs/en/Background-Workers-Hangfire.md

@ -53,7 +53,7 @@ public class MyLogWorker : HangfireBackgroundWorkerBase
CronExpression = Cron.Daily();
}
public override Task DoWorkAsync()
public override Task DoWorkAsync(CancellationToken cancellationToken = default)
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
@ -68,14 +68,7 @@ public class MyLogWorker : HangfireBackgroundWorkerBase
### UnitOfWork
For use with `UnitOfWorkAttribute`, you need to define an interface for worker:
```csharp
public interface IMyLogWorker : IHangfireBackgroundWorker
{
}
[ExposeServices(typeof(IMyLogWorker))]
public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker
{
public MyLogWorker()
@ -84,11 +77,13 @@ public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker
CronExpression = Cron.Daily();
}
[UnitOfWork]
public override Task DoWorkAsync()
public override Task DoWorkAsync(CancellationToken cancellationToken = default)
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
using (var uow = LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>().Begin())
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
}
}
}
```
@ -105,9 +100,6 @@ public class MyModule : AbpModule
ApplicationInitializationContext context)
{
await context.AddBackgroundWorkerAsync<MyLogWorker>();
//If the interface is defined
//await context.AddBackgroundWorkerAsync<IMyLogWorker>();
}
}
````

75
docs/en/Blog-Posts/2023-01-04 v7_0_Release_Stable/POST.md

@ -0,0 +1,75 @@
# ABP.IO Platform 7.0 Final Has Been Released!
[ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 7.0 versions have been released today.
## What's New With 7.0?
Since all the new features are already explained in detail in the [7.0 RC Announcement Post](https://blog.abp.io/abp/ABP.IO-Platform-7.0-RC-Has-Been-Published), I will not repeat all the details again. See the [RC Blog Post](https://blog.abp.io/abp/ABP.IO-Platform-7.0-RC-Has-Been-Published) for all the features and enhancements.
## Getting Started with 7.0
### Creating New Solutions
You can create a new solution with the ABP Framework version 7.0 by either using the `abp new` command or generating the CLI command on the [get started page](https://abp.io/get-started).
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for more.
### How to Upgrade an Existing Solution
#### Install/Update the ABP CLI
First of all, install the ABP CLI or upgrade to the latest version.
If you haven't installed it yet:
```bash
dotnet tool install -g Volo.Abp.Cli
```
To update an existing installation:
```bash
dotnet tool update -g Volo.Abp.Cli
```
#### Upgrading Existing Solutions with the ABP Update Command
[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command:
```bash
abp update
```
Run this command in the root folder of your solution.
## Migration Guides
There are breaking changes in this version that may affect your application. Please see the following migration documents, if you are upgrading from v6.x:
* [ABP Framework 6.x to 7.0 Migration Guide](https://docs.abp.io/en/abp/7.0/Migration-Guides/Abp-7_0)
* [ABP Commercial 6.x to 7.0 Migration Guide](https://docs.abp.io/en/commercial/7.0/migration-guides/v7_0)
## Community News
### Highlights from .NET 7.0?
Our team has closely followed the ASP.NET Core and Entity Framework Core 7.0 releases, read Microsoft's guides and documentation and adapt the changes to our ABP.IO Platform. We are proud to say that we've shipped the ABP 7.0 RC.1 based on .NET 7.0 just after Microsoft's .NET 7.0 release.
In addition to the ABP's .NET 7.0 upgrade, our team has created 13 great articles to highlight the important features coming with ASP.NET Core 7.0 and Entity Framework Core 7.0.
You can read [this post](https://volosoft.com/Blog/Highlights-for-ASP.NET-Entity-Framework-Core-NET-7.0) to see the list of all articles.
### New ABP Community Posts
In addition to [the 13 articles to highlight .NET 7.0 features written by our team]((https://volosoft.com/Blog/Highlights-for-ASP.NET-Entity-Framework-Core-NET-7.0)), here are some of the recent posts added to the [ABP Community](https://community.abp.io/):
* [liangshiwei](https://github.com/realLiangshiwei) has created a new community article, that shows [How to Use the Weixin Authentication for MVC / Razor Page Applications](https://community.abp.io/posts/how-to-use-the-weixin-authentication-for-mvc-razor-page-applications-a33e0wti).
* [Jasen Fici](https://community.abp.io/posts/deploying-abp.io-to-an-azure-appservice-ma8kukdp) has created a new article: [Deploying abp.io to an Azure AppService](https://community.abp.io/posts/deploying-abp.io-to-an-azure-appservice-ma8kukdp).
Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://community.abp.io/articles/submit) to the ABP Community.
## About the Next Version
The next feature version will be 7.1. You can follow the [release planning here](https://github.com/abpframework/abp/milestones).
Please [submit an issue](https://github.com/abpframework/abp/issues/new) if you have any problems with this version.

207
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/POST.md

@ -0,0 +1,207 @@
# ABP.IO Platform 7.1 RC Has Been Released
Today, we are happy to release the [ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) version **7.1 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version.
Try this version and provide feedback for a more stable version of ABP v7.1! Thanks to all of you.
## Get Started with the 7.1 RC
Follow the steps below to try version 7.1.0 RC today:
1) **Upgrade** the ABP CLI to version `7.1.0-rc.1` using a command line terminal:
````bash
dotnet tool update Volo.Abp.Cli -g --version 7.1.0-rc.1
````
**or install** it if you haven't before:
````bash
dotnet tool install Volo.Abp.Cli -g --version 7.1.0-rc.1
````
2) Create a **new application** with the `--preview` option:
````bash
abp new BookStore --preview
````
See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options.
> You can also use the [Get Started](https://abp.io/get-started) page to generate a CLI command to create a new application.
You can use any IDE that supports .NET 7.x, like [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/).
## Migrating to 7.1
This version doesn't introduce any breaking changes. However, Entity Framework developers may need to add a new code-first database migration to their projects since we made some improvements to the existing entities of some application modules.
## What's New with ABP Framework 7.1?
In this section, I will introduce some major features released in this version. In addition to these features, so many enhancements have been made in this version too.
Here is a brief list of the titles explained in the next sections:
* Blazor WASM option added to Application Single Layer Startup Template
* Introducing the `IHasEntityVersion` interface and `EntitySynchronizer` base class
* Introducing the `DeleteDirectAsync` method for the `IRepository` interface
* Introducing the `IAbpHostEnvironment` interface
* Improvements on the eShopOnAbp project
* Others
### Blazor WASM option added to Application Single Layer Startup Template
We've created the [Application (Single Layer) Startup Template](https://docs.abp.io/en/abp/7.1/Startup-Templates/Application-Single-Layer) in v5.2 with three UI types: Angular, Blazor Server and MVC. At the moment, we didn't provide UI option for Blazor, because it required 3 projects at least (server-side, client-side and shared library between these two projects).
In this version, we've added the Blazor WASM option to the **Application (Single Layer) Startup Template**. It still contains three projects (`blazor`, `host`, and `contracts`) but hosted by a single `host` project.
You can use the following CLI command to create an `app-nolayers` template with the Blazor UI as the UI option:
```bash
abp new TodoApp -t app-nolayers -u blazor --version 7.1.0-rc.1
```
> You can check the [Quick Start documentation](https://docs.abp.io/en/abp/7.1/Tutorials/Todo/Single-Layer/Index?UI=Blazor&DB=EF) for a quick start with this template.
### Introducing the `IHasEntityVersion` interface and `EntitySynchronizer` base class
Entity synchronization is an important concept, especially in distributed applications and module development. If we have an entity that is related to other modules, we need to align/sync their data once the entity changes and versioning entity changes can also be good, so we can know whether they're synced or not.
In this version, [@gdlcf88](https://github.com/gdlcf88) made a great contribution to the ABP Framework and introduced the `IHasEntityVersion` interface which adds **auto-versioning** to entity classes and `EntitySynchronizer` base class to **automatically sync an entity's properties from a source entity**.
You can check the issue and documentation from the following links for more info:
- [Issue: Entity synchronizers and a new EntityVersion audit property](https://github.com/abpframework/abp/issues/14196)
- [Versioning Entities](https://docs.abp.io/en/abp/7.1/Entities#versioning-entities)
- [Distributed Event Bus - Entity Synchronizer](https://docs.abp.io/en/abp/7.1/Distributed-Event-Bus#entity-synchronizer)
> Note: The entities of some modules from the ABP Framework have implemented the `IHasEntityVersion` interface. Therefore, if you are upgrading your application from an earlier version, you need to create a new migration and apply it to your database.
### Introducing the `DeleteDirectAsync` method for the `IRepository` interface
EF 7 introduced a new [`ExecuteDeleteAsync`](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) method that deletes entities without involving the change tracker into the process. Therefore, it's much faster.
We've added the `DeleteDirectAsync` method to the `IRepository<>` interface to take the full power of EF 7. It deletes all entities that fit the given predicate. It directly deletes entities from the database, without fetching them. Therefore, some features (like **soft-delete**, **multi-tenancy**, and **audit logging)** won't work, so use this method carefully when you need it. And use the `DeleteAsync` method if you need those features.
### Introducing the `IAbpHostEnvironment` interface
Sometimes, while creating an application, we need to get the current hosting environment and take actions according to that. In such cases, we can use some services such as [IWebHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.iwebhostenvironment?view=aspnetcore-7.0) or [IWebAssemblyHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.webassembly.hosting.iwebassemblyhostenvironment) provided by .NET, in the final application.
However, we can not use these services in a class library, which is used by the final application. ABP Framework provides the `IAbpHostEnvironment` service, which allows you to get the current environment name whenever you want. `IAbpHostEnvironment` is used by the ABP Framework in several places to perform specific actions by the environment. For example, ABP Framework reduces the cache duration on the **Development** environment for some services.
**Usage:**
```csharp
public class MyService
{
private readonly IAbpHostEnvironment _abpHostEnvironment;
public MyService(IAbpHostEnvironment abpHostEnvironment)
{
_abpHostEnvironment = abpHostEnvironment;
}
public void MyMethod()
{
//getting the current environment name
var environmentName = _abpHostEnvironment.EnvironmentName;
//check for the current environment
if (_abpHostEnvironment.IsDevelopment()) { /* ... */ }
}
}
```
You can inject the `IAbpHostEnvironment` into your service and get the current environment by using its `EnvironmentName` property. You can also check the current environment by using its extension methods such as `IsDevelopment()`.
> Check the [ABP Application Startup](https://docs.abp.io/en/abp/7.1/Application-Startup) documentation for more information.
### Improvements on the eShopOnAbp project
K8s and Docker configurations have been made within this version (Dockerfiles and helm-charts have been added and image build scripts have been updated). See [#14083](https://github.com/abpframework/abp/issues/14083) for more information.
### Others
* Referral Links have been added to the CMS Kit Comment Feature (optional). You can specify common referral links (such as "nofollow" and "noreferrer") for links in the comments. See [#15458](https://github.com/abpframework/abp/issues/15458) for more information.
* ReCaptcha verification has been added to the CMS Kit Comment Feature (optional). You can enable ReCaptcha support to enable protection against bots. See the [documentation](https://docs.abp.io/en/abp/7.1/Modules/Cms-Kit/Comments) for more information.
* In the development environment, it is a must to reduce cache durations for some points. We typically don't have to invalidate the cache manually or wait on it for a certain time to be invalidated. For that purpose, we have reduced the cache durations for some points on the development environment. See [#14842](https://github.com/abpframework/abp/pull/14842) for more information.
## What's New with ABP Commercial 7.1?
We've also worked on [ABP Commercial](https://commercial.abp.io/) to align the new features and changes made in the ABP Framework. The following sections introduce a few new features coming with ABP Commercial 7.1.
### Blazor WASM option added to Application Single Layer Pro Startup Template
The [**Application (Single Layer) Startup Template**](https://docs.abp.io/en/commercial/latest/startup-templates/application-single-layer/index) with Blazor UI is also available for ABP Commercial customers with this version as explained above.
You can use the following CLI command to create an `app-nolayers-pro` template with Blazor UI as the UI option:
```bash
abp new TodoApp -t app-nolayers-pro -u blazor --version 7.1.0-rc.1
```
You can also create an `app-nolayers-pro` template with Blazor UI via ABP Suite:
![](suite-blazor-wasm-nolayers.png)
### Suite - MAUI Blazor Code Generation
We provided a new UI option "MAUI Blazor" for the `app-pro` template in the previous version and it's possible to create a `maui-blazor` application with both ABP CLI and ABP Suite.
You can create an `app-pro` template with the MAUI Blazor as the UI option with the following ABP CLI command:
```bash
abp new Acme.BookStore -t app-pro -u maui-blazor
```
In this version, we implemented the code generation for MAUI Blazor. You can create and generate CRUD pages for this new UI option as you do in other UI types.
> Note: MAUI Blazor is currently only available with the `app-pro` template.
### SaaS Module - Allowing entering a username while impersonating the tenant
In the previous versions, we were able to impersonate a tenant from the [SaaS Module's Tenant Management UI](https://docs.abp.io/en/commercial/7.1/modules/saas#tenant-management). There was a constraint in this approach, which forced us to only impersonate the "admin" user. However, the tenant might change the admin user's username, or we may want to impersonate another user of the tenant.
Thus, with this version, we decided to allow the impersonation of the tenant by the specified username.
*You can click on the "Login with this tenant" action button:*
![](saas-impersonation-1.png)
*Then, Specify the admin name of the tenant:*
![](saas-impersonation-2.png)
## Community News
### New ABP Community Posts
* [Sergei Gorlovetsky](https://community.abp.io/members/Sergei.Gorlovetsky) has created two new community articles:
* [Why ABP Framework is one of the best tools for migration from legacy MS Access systems to latest Web app](https://community.abp.io/posts/why-abp-framework-is-one-of-the-best-tools-for-migration-from-legacy-ms-access-systems-to-latest-web-app-7l39eof0)
* [ABP Framework — 5 steps Go No Go Decision Tree](https://community.abp.io/posts/abp-framework-5-steps-go-no-go-decision-tree-2sy6r2st)
* [Onur Pıçakcı](https://github.com/onurpicakci) has created his first ABP community article that explains how to contribute to ABP Framework. You can read it 👉 [here](https://community.abp.io/posts/how-to-contribute-to-abp-framework-46dvzzvj).
* [Maliming](https://github.com/maliming) has created a new community article to show how to convert create/edit modals to a page. You can read it 👉 [here](https://community.abp.io/posts/converting-createedit-modal-to-page-4ps5v60m).
We thank you all. We thank all the authors for contributing to the [ABP Community platform](https://community.abp.io/).
### Volosoft Attended NDC London 2023
![](ndc-london.png)
Core team members of the ABP Framework, [Halil Ibrahim Kalkan](https://twitter.com/hibrahimkalkan) and [Alper Ebicoglu](https://twitter.com/alperebicoglu) attended [NDC London 2023](https://ndclondon.com/) from the 23rd to the 27th of January.
### Community Talks 2023.1: LeptonX Customization
![](community-talks-conver-image.png)
In this episode of ABP Community Talks, 2023.1; we'll talk about **LeptonX Customization**. We will dive into the details and show you how to customize the [LeptonX Theme](https://leptontheme.com/) with examples.
The event will be live on Thursday, February 16, 2023 (20:00 - 21:00 UTC).
> Register to listen and ask your questions now 👉 https://kommunity.com/volosoft/events/abp-community-talks-20231-leptonx-customization-03f9fd8c.
## Conclusion
This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://docs.abp.io/en/abp/7.1/Road-Map) documentation to learn about the release schedule and planned features for the next releases. Please try the ABP v7.1 RC and provide feedback to help us release a more stable version.
Thanks for being a part of this community!

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/community-talks-conver-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/ndc-london.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/saas-impersonation-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/saas-impersonation-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/Blog-Posts/2023-02-08 v7_1_Preview/suite-blazor-wasm-nolayers.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

2
docs/en/CLI.md

@ -138,6 +138,7 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample
* `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks:
* `mvc`: ASP.NET Core MVC.
* `angular`: Angular UI.
* `blazor`: Blazor UI.
* `blazor-server`: Blazor Server UI.
* `none`: Without UI.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
@ -353,6 +354,7 @@ abp generate-proxy -t csharp -url https://localhost:44302/
* `--type` or `-t`: The name of client type. Available clients:
* `csharp`: C#, work in the `*.HttpApi.Client` project directory. There are some additional options for this client:
* `--without-contracts`: Avoid generating the application service interface, class, enum and dto types.
* `--folder`: Folder name to place generated CSharp code in. Default value: `ClientProxies`.
* `ng`: Angular. There are some additional options for this client:
* `--api-name` or `-a`: The name of the API endpoint defined in the `/src/environments/environment.ts`. Default value: `default`.

13
docs/en/Caching.md

@ -10,7 +10,7 @@ ABP Framework extends the [ASP.NET Core distributed cache](https://docs.microsof
[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package of the caching system. You can install it a project using the add-package command of the [ABP CLI](CLI.md):
```
```bash
abp add-package Volo.Abp.Caching
```
@ -252,6 +252,14 @@ ABP's distributed cache interfaces provide methods to perform batch methods thos
> These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` ... methods (called once for each item).
## Caching Entities
ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database.
It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted.
> See the [Entity Cache](Entity-Cache.md) documentation for more information.
## Advanced Topics
### Unit Of Work Level Cache
@ -272,4 +280,5 @@ You can [replace](Dependency-Injection.md) this service by your own implementati
## See Also
* [Redis Cache](Redis-Cache.md)
* [Entity Cache](Entity-Cache.md)
* [Redis Cache](Redis-Cache.md)

5
docs/en/Community-Articles/2022-11-25-JSON-columns/post.md

@ -68,7 +68,7 @@ public class MyDbContext : AbpDbContext<MyDbContext>
builder.Entity<Person>(b =>
{
b.ToTable(MyProjectConsts.DbTablePrefix + "Persons", MyProjecConsts.DbSchema);
b.ToTable(MyProjectConsts.DbTablePrefix + "Persons", MyProjectConsts.DbSchema);
b.ConfigureByConvention();
b.OwnsOne(x=>x.ContactDetails, c =>
{
@ -125,6 +125,9 @@ As you can see, thanks to JSON Columns feature the **ContactDetails** row has JS
In this article, I've briefly introduced the JSON Columns feature that was shipped with EF Core 7. It's pretty straightforward to use JSON Columns in an ABP based application. You can see the examples above and give it a try!
### The Source Code
* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreJSONColumnDemo).
### References
* [https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns)

71
docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md

@ -0,0 +1,71 @@
# Bulk Operations with Entity Framework Core 7.0
Entity Framework tracks all the entity changes and applies those changes to the database one by one when the `SaveChanges()` method is called. There was no way to execute bulk operations in Entity Framework Core without a dependency.
As you know the [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library was doing it but it was not free.
There was no other solution until now. [Bulk Operations](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) are now available in Entity Framework Core with .NET 7.
With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with the Entity Framework Core Extensions library if you're familiar with it.
You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it.
It can be easily used with the DbContext.
```csharp
await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync();
```
## Using with ABP Framework
ABP Framework provides an abstraction over database operations and implements generic repository pattern. So, DbContext can't be accessed outside of [repositories](https://docs.abp.io/en/abp/latest/Repositories).
You can use the `ExecuteUpdate` and `ExecuteDelete` methods inside a repository.
```csharp
public class BookEntityFrameworkCoreRepository : EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
{
public BookEntityFrameworkCoreRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public async Task UpdateListingAsync()
{
var dbSet = await GetDbSetAsync();
await dbSet
.Where(x => x.IsListed && x.PublishedOn.Year <= 2022)
.ExecuteUpdateAsync(s => s.SetProperty(x => x.IsListed, x => false));
}
public async Task DeleteOldBooksAsync()
{
var dbSet = await GetDbSetAsync();
await dbSet
.Where(x => x.PublishedOn.Year <= 2000)
.ExecuteDeleteAsync();
}
}
```
There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0.
```csharp
public class MyDomainService : DomainService
{
protected IRepository<Book, Guid> BookRepository { get; }
public MyDomainService(IRepository<Book, Guid> bookRepository)
{
BookRepository = bookRepository;
}
public async Task CreateBooksAsync(List<Book> books)
{
// It'll use bulk inserting automatically.
await BookRepository.InsertManyAsync(books);
}
}
```
> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully.

208
docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md

@ -0,0 +1,208 @@
# Value generation for DDD guarded types with Entity Framework Core 7.0
In domain-driven design (DDD), *guarded keys* can improve the type safety of key properties. This is achieved by wrapping the key type in another type which is specific to the use of the key. In this article, I will explain the cases why you may need to use guarded types and discuss the advantages and limitations when implementing to an ABP application.
> You can find the source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo).
## The Problem
While developing an applications, there are many cases where we manually assign foreign keys that can be in guid type or integer type, etc. This manual assignment mistakes can cause miss-match of unique identifiers, such as **assigning a product ID to a category**, that can be hard to detect in the future.
Here is a very simplified sample of wrong assignment when trying to update a product category:
````csharp
public class ProductAppService : MyProductStoreAppService, IProductAppService
{
private readonly IRepository<Product, Guid> _productRepository;
public ProductAppService(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public async Task UpdateProductCategoryAsync(Guid productId, Guid categoryId)
{
var productToUpdate = await _productRepository.GetAsync(productId);
productToUpdate.CategoryId = productId; // Wrong assignment that causes error only at run-time
await _productRepository.UpdateAsync(productToUpdate);
}
}
````
While the sample demonstrates a very simple mistake, it is easier to come across similar mistakes when the business logic gets more complex especially when you are using methods with **multiple foreign key arguments**. The next section offers using guarded types to prevent these kind of problems as a solution to the problem.
## The Solution
Strongly-typed IDs (*guarded keys*) is a DDD approach to address this problem. One of the main problems with .NET users was handling the persisting these objects. With EFCore7, key properties can be guarded with type safety seamlessly.
To use guarded keys, update your aggregate root or entity unique identifier with a complex type to overcome *primitive obsession*:
````csharp
public readonly struct CategoryId
{
public CategoryId(Guid value) => Value = value;
public Guid Value { get; }
}
public readonly struct ProductId
{
public ProductId(Guid value) => Value = value;
public Guid Value { get; }
}
````
You can now use these keys for your aggregate roots or entities:
```csharp
public class Product : AggregateRoot<ProductId>
{
public ProductId Id { get; set; }
public string Name { get; set; }
public CategoryId CategoryId { get; set; }
private Product() { }
public Product(ProductId id, string name) : base(id)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
}
}
public class Category : AggregateRoot<CategoryId>
{
public CategoryId Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; } = new();
private Category() { }
public Category(CategoryId id, string name) : base(id)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
}
}
```
`ProductId` and `CategoryId` guarded key types shown in the sample use `Guid` key values, which means Guid values will be used in the mapped database tables. This is achieved by defining [value converters](https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions) for the types.
Override the `ConfigureConventions` method of your DbContext to use the value converters:
````csharp
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<ProductId>().HaveConversion<ProductIdConverter>();
configurationBuilder.Properties<CategoryId>().HaveConversion<CategoryIdConverter>();
}
private class ProductIdConverter : ValueConverter<ProductId, Guid>
{
public ProductIdConverter()
: base(v => v.Value, v => new(v))
{
}
}
private class CategoryIdConverter : ValueConverter<CategoryId, Guid>
{
public CategoryIdConverter()
: base(v => v.Value, v => new(v))
{
}
}
````
> The code here uses `struct` types. This means they have appropriate value-type semantics for use as keys. If `class` types are used instead, then they need to either override equality semantics or also specify a [value comparer](https://learn.microsoft.com/en-us/ef/core/modeling/value-comparers).
Now, you can use generic (or custom) repositories of ABP using the guarded type as the key for the repository:
```csharp
public class ProductStoreDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Category, CategoryId> _categoryRepository;
private readonly IRepository<Product, ProductId> _productRepository;
public ProductStoreDataSeedContributor(
IRepository<Category, CategoryId> categoryRepository,
IRepository<Product, ProductId> productRepository
)
{
_categoryRepository = categoryRepository;
_productRepository = productRepository;
}
// ...
}
```
You can also use `integer` as guarded type for your key properties and use [Sequence-based key generation for SQL Server](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#sequence-based-key-generation-for-sql-server) for value generation.
## Discussions
In this section, I will discuss the use cases of guarded types and limitations when implementing to an ABP application.
### Do I need to use guarded types?
If you are already following the best practices of [Domain-Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design), you are aware that **updates** and **creations** of an aggregate are done **over** the aggregate root itself as a whole unit. And entity state changes of an aggregate root should be done using the [domain services](https://docs.abp.io/en/abp/latest/Domain-Services). Domain services should already validate the entity.
**Example: Using domain service to update product:**
````csharp
public class ProductManager : DomainService
{
private readonly IRepository<Product, Guid> _productRepository;
public ProductManager(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public Task<Product> AssignCategory(Product product, Category category)
{
// ...
product.CategoryId = category.Id;
//..
}
}
````
In this sample, domain service validates that both **product** and the **category** entities, passed by the application layer, are valid objects since they are not key properties. However, manual assignment is already in place and more complex the domain logic, higher to miss out mistakes. At the end, it will depend on your tolerance level for developer errors comparing to the time you want to spend time on additional code base for guarded types.
### Limitations
One important limitation is automatic value generation when using `Guid` as guarded type for your key properties. The basic repository can not generate the unique identifier automatically by the time this article is written:
```csharp
public readonly struct ProductId
{
public ProductId(Guid value) => Value = value;
public Guid Value { get; }
}
```
you need to generate the unique identifier **manually**:
````csharp
var newProduct = await _productRepository.InsertAsync(
new Product(new ProductId(_guidGenerator.Create()), "New product")
);
````
## Conclusion
In this article, I tried to explain DDD guarded types for key properties and value generation for these properties using Entity Framework 7.0 and ABP.
Using strongly-typed key properties reduce the chance of unnoticed errors. Admittedly it increases the code complexity by adding new types to your solution with extra coding, especially if you are using classes as keys. Guarded types provide improved safety for your code base at the expense of additional code complexity as in many DDD concepts and patterns.
If you have a large team working on a large scale solution containing complex business logics where key assignments are abundant or if you are using methods with multiple foreign key arguments, I personally suggest using guarded types.
## The Source Code
* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo).
## See Also
* [What's new in EF Core 7.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew)
* [ABP Framework: Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design)

231
docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md

@ -0,0 +1,231 @@
# Rate Limiting with ASP.NET Core 7.0
Rate limiting is a way of controlling the traffic that a web application or API receives. In other words, rate limiting helps you control the amount of traffic each user has access to at any given time. This is extremely useful when you want to manage the load on your server or services, avoid going over your monthly data transfer limit and allow the system to continue to function and meet service level agreements, even when an increase in demand places an extreme load on resources.
In this article, we will look at what rate limiting is, why we need to use it, how the different rate limiting algorithms provided with .NET 7.0 work, and best practices for using rate limiting in your application.
## What is rate limiting?
Whether accidental or intentional, users may exhaust resources in a way that impacts others. When a number of requests are received on to resources for a long time, the server can run out of those resources. These resources can include memory, threads, connections, or anything else that is limited. To avoid this situation, set rate limiters. Rate limiters control the consumption of resources used by an instance of an application, a user, an individual tenant, or an entire service.
## Why do you need to use rate limiting?
A rate limiting system is crucial in any application where you have to control or throttle user requests or traffic. This is especially true in applications running on a cloud hosting platform because the user’s traffic can affect the whole server where the application is hosted.
Why do you need to implement rate limiting? Here are a few reasons:
- To ensure that a system continues to meet service level agreements (SLA).
- To prevent a single user, tenant, service, or so on from monopolizing the resources provided by an application.
- To help cost-optimize a system by limiting the maximum resource levels needed to keep it functioning.
## Rate limiter algorithms
The [`RateLimiterOptionsExtensions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.ratelimiting.ratelimiteroptionsextensions) class provides the following extension methods for rate limiting:
- **[Fixed window](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#fixed-window-limit)**: Fixed-window limits—such as 3,000 requests per hour or 10 requests per day—are easy to state, but they are subject to spikes at the edges of the window, as available quota resets. Consider, for example, a limit of 3,000 requests per hour, which still allows for a spike of all 3,000 requests to be made in the first minute of the hour, which might overwhelm the service.
- [**Sliding window**:](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#sliding-window-limit) Sliding windows have the benefits of a fixed window, but the rolling window of time smoothes out bursts. Systems such as Redis facilitate this technique with expiring keys.
- [**Token bucket**](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#token-bucket-limit): A token bucket maintains a rolling and accumulating budget of usage as a balance of tokens. A token bucket adds tokens at some rate. When a service request is made, the service attempts to withdraw a token (decrementing the token count) to fulfill the request. If there are no tokens in the bucket, the service has reached its limit and responds with backpressure.
- [**Concurrency**](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#concurrency-limiter): A concurrency limiter is the simplest form of rate limiting. It doesn’t look at time, just at number of concurrent requests.
In order to be a more realistic example, instead of making an example with each rate limiter algorithm, we will implement the following three algorithms in an **ABP-based** application.
1. We will add a `SlidingWindowLimiter` with a partition for all anonymous users.
2. We will add a `TokenBucketRateLimiter` with a partition for each authenticated user.
3. We will add a `ConcurrencyLimiter` with a partition for each Tenant.
**Note:** The following sample isn't meant for production code but is an example of how to use the limiters in ABP-based applications.
### Limiter with `OnRejected`, `RetryAfter`, and `GlobalLimiter`
#### Add rate limiter
Let's create the following method in the `MyProjectNameWebModule.cs` class in the `MyProjectName.Web` project.
**Note:** If the `**.Web` project is not in your application, you can do the same in the project where your application is hosted.
```csharp
private void ConfigureRateLimiters(ServiceConfigurationContext context)
{
context.Services.AddRateLimiter(limiterOptions =>
{
limiterOptions.OnRejected = (context, cancellationToken) =>
{
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
context.HttpContext.Response.Headers.RetryAfter =
((int) retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
}
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.HttpContext.RequestServices.GetService<ILoggerFactory>()?
.CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware")
.LogWarning("OnRejected: {RequestPath}", context.HttpContext.Request.Path);
return new ValueTask();
};
limiterOptions.AddPolicy("UserBasedRateLimiting", context =>
{
var currentUser = context.RequestServices.GetService<ICurrentUser>();
if (currentUser is not null && currentUser.IsAuthenticated)
{
return RateLimitPartition.GetTokenBucketLimiter(currentUser.UserName, _ => new TokenBucketRateLimiterOptions
{
TokenLimit = 10,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 3,
ReplenishmentPeriod = TimeSpan.FromMinutes(1),
TokensPerPeriod = 4,
AutoReplenishment = true
});
}
return RateLimitPartition.GetSlidingWindowLimiter("anonymous-user",
_ => new SlidingWindowRateLimiterOptions
{
PermitLimit = 2,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 1,
Window = TimeSpan.FromMinutes(1),
SegmentsPerWindow = 2
});
});
limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
var currentTenant = context.RequestServices.GetService<ICurrentTenant>();
if (currentTenant is not null && currentTenant.IsAvailable)
{
return RateLimitPartition.GetConcurrencyLimiter(currentTenant!.Name, _ => new ConcurrencyLimiterOptions
{
PermitLimit = 5,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 1
});
}
return RateLimitPartition.GetNoLimiter("host");
});
});
}
```
In the above example, the `TokenBucketLimiter` is used for each authenticated user, while the `SlidingWindowLimiter` is used for all anonymous users. Additionally, as a global limiter, the `ConcurrencyLimiter` is used for each tenant, while rate limiting is disabled for the host(tenant is not available). Also, for requests that are rejected when the limit is reached, sets the response status code to [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) and the response mentions when to retry (if available from the rate-limiting metadata).
Let's call the `ConfigureRateLimiters` method that we created in the `ConfigureServices` method.
The final version of the `ConfigureServices` method:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureBundles();
ConfigureUrls(configuration);
ConfigurePages(configuration);
ConfigureAuthentication(context);
ConfigureImpersonation(context, configuration);
ConfigureAutoMapper();
ConfigureVirtualFileSystem(hostingEnvironment);
ConfigureNavigationServices();
ConfigureAutoApiControllers();
ConfigureSwaggerServices(context.Services);
ConfigureExternalProviders(context);
ConfigureHealthChecks(context);
ConfigureCookieConsent(context);
ConfigureTheme();
Configure<PermissionManagementOptions>(options =>
{
options.IsDynamicPermissionStoreEnabled = true;
});
ConfigureRateLimiters(context); // added
}
```
#### Add RateLimiter middleware
Add the following line just before the `app.UseConfiguredEndpoints(...)` line to add the `RateLimiter` middleware to your ASP.NET Core request pipeline:
```csharp
app.UseRateLimiter();
```
#### Use rate limiter for all controllers
Let's edit the `ConfiguredEndpoints` middleware as follows:
```csharp
app.UseConfiguredEndpoints(endpoints =>
{
endpoints.MapRazorPages()
.DisableRateLimiting();
endpoints.MapControllers()
.RequireRateLimiting("UserBasedRateLimiting");
});
```
- **DisableRateLimiting:** It is used to disable the `ConcurrencyLimiter` for razor pages, which we set globally when the tenant is available.
- **RequireRateLimiting:** We have enabled the rate limiter, which we define according to whether the user is authenticated or not, for all controllers.
## `EnableRateLimiting` and `DisableRateLimiting` attributes
It's kind of unrealistic to always use rate limiting for all controllers or pages. Sometimes, we may want to throttle a particular endpoint or page. In such cases, we can use the `EnableRateLimiting` and `DisableRateLimiting` attributes. The `EnableRateLimiting` and `DisableRateLimiting` attributes can be applied to a controller, action method, or razor rage. Check [here](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#enableratelimiting-and-disableratelimiting-attributes) for more.
## Rate limit an HTTP handler
Rate limiting when sending an HTTP request can be a good practice, especially in service-to-service communication. Because, resources are consumed by apps that rely on them, and when an app makes too many requests for a single resource, it can lead to *resource contention*. Resource contention occurs when a resource is consumed by too many clients, and the resource is unable to serve all of the apps that are requesting it. This can result in a poor user experience, and in some cases, it can even lead to a denial of service (DoS) attack. Since there are similar codes, I will not mention an example in this article, but to avoid such situations, you can write your own HTTP handler as [here](https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter#implement-a-delegatinghandler-subclass).
## How does it work?
[System.Threading.RateLimiting](https://www.nuget.org/packages/System.Threading.RateLimiting) provides the primitives for writing rate limiters as well as providing a few commonly used algorithms built-in. The main type is the abstract base class [RateLimiter](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs).
```csharp
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
public abstract int GetAvailablePermits();
public abstract TimeSpan? IdleDuration { get; }
public RateLimitLease Acquire(int permitCount = 1);
public ValueTask<RateLimitLease> WaitAsync(int permitCount = 1, CancellationToken cancellationToken = default);
public void Dispose();
public ValueTask DisposeAsync();
}
```
`RateLimiter` contains `Acquire` and `WaitAsync` as the core methods for trying to gain permits for a resource that is being protected. Depending on the application, the protected resource may need to acquire more than 1 permits, so `Acquire` and `WaitAsync` both accept an optional `permitCount` parameter. `Acquire` is a synchronous method that will check if enough permits are available or not and return a `RateLimitLease` which contains information about whether you successfully acquired the permits or not. `WaitAsync` is similar to `Acquire` except that it can support queuing permit requests which can be de-queued at some point in the future when the permits become available, which is why it’s asynchronous and accepts an optional `CancellationToken` to allow canceling the queued request.
`RateLimitLease` has an `IsAcquired` property which is used to see if the permits were acquired. Additionally, the `RateLimitLease` may contain metadata such as a suggested retry-after period if the lease failed. Finally, the `RateLimitLease` is disposable and should be disposed when the code is done using the protected resource. The disposal will let the `RateLimiter` know to update its limits based on how many permits were acquired.
## Limitations
In most cases, the rate-limiting middleware provided with ASP.NET 7.0 will meet your requirements. However, if you would want to return statistics about your limits (e.g. [the way GitHub does](https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-http-headers)), you’ll find out that the ASP.NET rate limiting middleware does not support this. You won’t have access to the “number of requests remaining” or other metadata. Not in `OnRejected`, and definitely not if you want to return this data as headers on every request.
## Best practices for rate limiting
In order to use rate limiting properly, you need to have a solid understanding of the types of limiting available, as well as the data rate and data volume of your service. You also need to have a clear idea of how many users you expect to use your service as well as how they will interact with it. The best practices for rate limiting are as follows:
- Find the right rate limiter algorithm for your endpoint. I mean, the cost of an endpoint should be considered when selecting a limiter. The cost of an endpoint includes the resources used, for example, time, data access, CPU, and I/O.
- Set realistic limits. Once you’ve figured out all the above, you need to set realistic limits for each service. Then, before deploying an app using rate limiting to production, stress test the app to validate the rate limiters and options used. For example, create a [JMeter script](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) with a tool like [BlazeMeter](https://guide.blazemeter.com/hc/articles/207421695-Writing-your-first-JMeter-script) or [Apache JMeter HTTP(S) Test Script Recorder](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) and load the script to [Azure Load Testing](https://learn.microsoft.com/en-us/azure/load-testing/overview-what-is-azure-load-testing).
- In response to rate-limiting, intermittent, or non-specific errors, a client should generally retry the request after a delay. It is a best practice for this delay to increase exponentially after each failed request, which is referred to as *exponential backoff*. When many clients might be making schedule-based requests (such as fetching results every hour), additional random time (*jitter*) should be applied to the request timing, the backoff period, or both of them to ensure that these multiple client instances don't become periodic [thundering herd](https://www.wikiwand.com/en/Thundering_herd_problem), and cause a form of DDoS themselves.
## Conclusion
In this article, we’ve covered what rate limiting is, why you need to use it and the best practices for doing so. We’ve also looked at how to use three rate-limiting algorithms that are provided with .NET 7.0 on ABP-based applications and how rate-limiting works. Now that you’re familiar with the concept of rate limiting, it’s time to start implementing rate limiting in your application. This will allow you to control the traffic and ensure that your application is running smoothly without any issues.
## References
- https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0
- https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
- https://blog.maartenballiauw.be/post/2022/09/26/aspnet-core-rate-limiting-middleware.html
- https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter
- https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern
- https://learn.microsoft.com/en-us/azure/architecture/patterns/throttling
- https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet
- https://cloud.google.com/architecture/rate-limiting-strategies-techniques

BIN
docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 KiB

68
docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/Post.md

@ -0,0 +1,68 @@
# How to Use the Weixin Authentication for MVC / Razor Page Applications
This guide demonstrates how to integrate Weixin to an ABP application that enables users to sign in using OAuth 2.0 with credentials.
## Create a sandbox account
If you don't have a production account, you can create a sendbox account for testing: https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
In this article we will use the sandbox account.
> You should configure the callback domain name on the Weixin open platform
## AddWeixin
You need to install `AspNet.Security.OAuth.Weixin` package to your **.Web** project.
In your **.Web** project, locate your **ApplicationWebModule** and modify `ConfigureAuthentication` method with the following:
```csharp
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.AddAuthentication()
.AddWeixin(options =>
{
options.ClientId = configuration["Weixin:ClientId"];
options.ClientSecret = configuration["Weixin:ClientSecret"];
});
}
```
Updating `appsettings.json` to add `Weixin` section:
````json
"Weixin": {
"ClientId": "<your-app-id>",
"ClientSecret": "<your-app-secret>"
}
````
## Web page authorization
Now you can run the application to login with Weixin.
![login-with-weixin](login-with-weixin.jpg)
It will redirect to weixin platform to scan the QR code.
> The sandbox account lacks the necessary scope, so it may not work properly.
## Official account authorization
Updating `AddWeixin`:
```csharp
context.Services.AddAuthentication()
.AddWeixin(options =>
{
options.ClientId = configuration["Weixin:ClientId"];
options.ClientSecret = configuration["Weixin:ClientSecret"];
options.AuthorizationEndpoint = "https://open.weixin.qq.com/connect/oauth2/authorize";
});
```
Now you can use WeChat app to open the web application URL to login with weixin.
![offical-account](offical-account.jpg)

BIN
docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/login-with-weixin.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/offical-account.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

105
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/how-can-I-contribute-to-open-source-projects-on-github.md

@ -0,0 +1,105 @@
# How to Contribute to ABP Framework
## Introduction
In this article I will explain how you can contribute to the open source ABP Framework. You will not only learn about the ABP Framework, but also how to contribute to an open source project, what are the standard rules, some git operations, etc.
## What is Open Source?
Open source software is code designed to be publicly available. Anyone can view, use, modify and distribute the project and code. The fact that the code is open source makes it a natural community and open for improvement. This enables ideas and thoughts to spread rapidly.
## What is ABP Framework?
ABP Framework is a complete infrastructure for building modern web applications following the best practices and guidelines of software development. ABP Framework is completely free, [open source](https://github.com/abpframework) and community driven. ABP is a modular framework and Application Modules provide pre-built application functionalities.
## Before Contribution
Before making any changes and trying to push them to the target repository we need to create a new [issue](https://github.com/abpframework/abp/issues) if there are no issues with the work. If there is an existing issue, you can proceed through this issue. This way, no other developer will work on the same issue and your PR will have a better chance to be accepted.
Previous ABP Community Talk on this topic can be found [here](https://www.youtube.com/watch?v=Wz4Z-O-YoPg).
## GitHub Issues
You may want to fix a known bug or work on a planned enhancement. See the [issue list](https://github.com/abpframework/abp/issues) on GitHub.
## Feature Requests
If you have a feature idea for the framework or modules, create an issue on GitHub or attend an existing discussion. Then you can implement it if it's embraced by the community.
## How to Contribute to an Open Source Software?
There are some steps to contribute to OSS projects. You can follow the steps below.
## Step 1: Fork the Project
The first thing we need to do now is to fork the open source project. Forking will create a copy of the project in your own GitHub account. This will allow users to make changes to the code without affecting the original repository. Just press the fork key in the project.
![fork-image](images/fork-project-image.png)
After forking, it will create a new repo in your own GitHub profile.
![fork-image-profile](images/fork-project-profile.png)
## Step 2: Clone the Project
In order to develop the project, you need to clone it to your local. After clicking on the code button, select your preferred cloning method and copy the link. You can run the copied link on your local machine with the `git clone` command, but we will use GitHub Desktop. Press `Open with GitHub Desktop` and the repo will be installed on your local machine.
or alternatively use the `git clone https://github.com/username/abp.git` command
![clone-image](images/clone-image.png)
## Step 3: Create a New Branch
In this step, you need to create a new branch of your own before you start developing it. Open the repo on GitHub Desktop and create a new branch. When creating a new branch, be careful which branch you create it on.
or alternatively use the `git checkout -b new-branch` command
![branch-image](images/branch-image.png)
## Step 4: Development
Choose a suitable IDE to develop on the new branch you created. In order not to complicate things, we will create a `Developers.md` file and process it. Let's enter a sample text in the Developers file.
![developer-list](images/developer-list.png)
As you can see, all changes made to the repo are reflected directly on GitHub Desktop.
![github-desktop-change](images/github-desktop-change.png)
## Step 5: Commit
The commit operation is used to save the changes you have made. It is useful to commit after certain operations are done in the project. It is useful to write a short sentence describing what you've done for the changes made in each commit. Press the `Commit to <branch-name>` button to commit.
or alternatively use the `git add .` and `git commit -m "Added the Developer List"` command
![commit-image](images/commit-image.png)
## Step 6: Publish the Changes
The changes you have made so far are only visible on your local machine. You need to publish these changes to submit them to your forked repository. Please press the publish branch button to publish.
or alternatively use the `git push origin new-branch` command
![push-image](images/git-push-image.png)
## Step 7: Create a Pull Request
After the push, the pull request `Create Pull Request` button will appear on GitHub Desktop. Click it and create a pull request.
![github-desktop-pull-request](images/github-desktop-pull-request.png)
You can also make a pull request from the repo in your GitHub profile.
![compare-pull-request](images/pull-request-image.png)
Before creating the pull request, make sure that the branch you created is making changes to the correct branch. After briefly describing your changes in the title and description, click the `Create pull request` button. This will send a pull request to the original repository. If the pull request is approved and merged by the community, your changes will also appear in the main repository.
![open-pull-request](images/open-pull-request-image.png)
That's it! You have contributed your development to an open source project.
## Conclusion
In this article, I showed you how you could contribute to the ABP Framework, an open source and community driven project. Thank you for reading the article, I hope it was useful. See you soon!
## References
- https://opensource.guide/how-to-contribute/
- https://docs.abp.io/en/abp/latest/Contribution/Index

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/branch-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/clone-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/commit-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/developer-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/fork-project-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/fork-project-profile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/git-push-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/github-desktop-change.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/github-desktop-pull-request.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/open-pull-request-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
docs/en/Community-Articles/2023-01-30-How-To-Contribute-To-a-Open-Source-Software/images/pull-request-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

86
docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/POST.md

@ -0,0 +1,86 @@
# Converting Create/Edit Modal to Page
In this document we will explain how to convert BookStore's `Books` create & edit modals to regular razor pages.
## Before
![before](images/old.gif)
## Now
![after](images/new.gif)
## Index page
Repalce `abp-button(NewBookButton)` buttom with `<a class="btn btn-primary" href="/Books/CreateModal"><i class="fa fa-plus"></i> @L["NewBook"].Value</a>`.
## Index js file
Remove the related codes of `createModal` and `editModal`.
Change the `Edit row action` with `location.href = "/Books/EditModal?id=" + data.record.id;`
## Create/Edit Book page
Remove `Layout = null;` and add some custom style and javascript code to `CreateModal.cshtml` & `EditModal.cshtml`.
```csharp
@section styles {
<style>
.abp-view-modal .modal {
position: static;
display: block;
opacity: inherit !important;
}
.abp-view-modal .modal.fade .modal-dialog {
transition: inherit !important;
transform: inherit !important;;
}
.abp-view-modal .modal-header .btn-close {
display: none;
}
</style>
}
@section scripts {
<script>
$(".abp-view-modal form").abpAjaxForm().on('abp-ajax-success', function () {
location.href = "/Books";
});
</script>
}
```
Add a `div` element with `abp-view-modal` class to wrap the `abp-dynamic-form`, Set size of `abp-modal` to `ExtraLarge` and remove the `AbpModalButtons.Cancel` button from `abp-modal-footer`.
### CreateModal
```csharp
<div class="abp-view-modal">
<abp-dynamic-form abp-model="Book" asp-page="/Books/CreateModal">
<abp-modal static="true" size="ExtraLarge">
<abp-modal-header title="@L["NewBook"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
</div>
```
### EditModal
```csharp
<div class="abp-view-modal">
<abp-dynamic-form abp-model="Book" asp-page="/Books/EditModal">
<abp-modal size="ExtraLarge">
<abp-modal-header title="@L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
</div>
```
You can check this Git commit for details.
https://github.com/abpframework/abp-samples/commit/f3014e0ec422cb2d8816d0e00dd6ab9cc1adfc21

BIN
docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/images/new.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
docs/en/Community-Articles/2023-02-06-Converting-Create-Edit-Modal-To-Page/images/old.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

2
docs/en/Customizing-Application-Modules-Extending-Entities.md

@ -101,7 +101,7 @@ public class MyLocalIdentityUserChangeEventHandler :
````
* `EntityChangedEventData<T>` covers create, update and delete events for the given entity. If you need, you can subscribe to create, update and delete events individually (in the same class or different classes).
* This code will be executed **out of the local transaction**, because it listens the `EntityChanged` event. You can subscribe to the `EntityChangingEventData<T>` to perform your event handler in **the same local (in-process) transaction** if the current [unit of work](Unit-Of-Work.md) is transactional.
* This code will be executed in the **current unit of work**, the whole process becomes transactional.
> Reminder: This approach needs to change the `IdentityUser` entity in the same process contains the handler class. It perfectly works even for a clustered environment (when multiple instances of the same application are running on multiple servers).

98
docs/en/Dependency-Injection.md

@ -2,7 +2,7 @@
ABP's Dependency Injection system is developed based on Microsoft's [dependency injection extension](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) library (Microsoft.Extensions.DependencyInjection nuget package). So, it's documentation is valid in ABP too.
> While ABP has no core dependency to any 3rd-party DI provider, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with Autofac installed. See [Autofac integration](Autofac-Integration.md) document for more information.
> While ABP has no core dependency to any 3rd-party DI provider. However, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with [Autofac](https://autofac.org/) installed. See [Autofac integration](Autofac-Integration.md) document for more information.
## Modularity
@ -20,24 +20,24 @@ public class BlogModule : AbpModule
## Conventional Registration
ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` by overriding the `PreConfigureServices` method.
ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` in the constructor of your module class. Example:
````C#
public class BlogModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
public BlogModule()
{
SkipAutoServiceRegistration = true;
}
}
````
Once you skip auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example:
Once you skip the auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example:
````c#
public class BlogModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
public BlogModule()
{
SkipAutoServiceRegistration = true;
}
@ -105,9 +105,7 @@ Example:
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
public class TaxCalculator
{
}
````
``Dependency`` attribute has a higher priority than other dependency interfaces if it defines the ``Lifetime`` property.
@ -120,7 +118,6 @@ public class TaxCalculator
[ExposeServices(typeof(ITaxCalculator))]
public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency
{
}
````
@ -179,7 +176,11 @@ public class MyModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Replacing the IConnectionStringResolver service
context.Services.Replace(ServiceDescriptor.Transient<IConnectionStringResolver, MyConnectionStringResolver>());
context.Services.Replace(
ServiceDescriptor.Transient<
IConnectionStringResolver,
MyConnectionStringResolver
>());
}
}
````
@ -202,7 +203,7 @@ public class TaxAppService : ApplicationService
_taxCalculator = taxCalculator;
}
public void DoSomething()
public async Task DoSomethingAsync()
{
//...use _taxCalculator...
}
@ -227,7 +228,7 @@ public class MyService : ITransientDependency
Logger = NullLogger<MyService>.Instance;
}
public void DoSomething()
public async Task DoSomethingAsync()
{
//...use Logger to write logs...
}
@ -244,17 +245,21 @@ One restriction of property injection is that you cannot use the dependency in y
Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class.
#### DisablePropertyInjectionAttribute
#### DisablePropertyInjection Attribute
You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties.
You can use `[DisablePropertyInjection]` attribute on classes or their properties to disable property injection for the whole class or some specific properties.
````C#
// Disabling for all properties of the MyService class
[DisablePropertyInjection]
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public ITaxCalculator TaxCalculator { get; set; }
}
// Disabling only for the TaxCalculator property
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
@ -262,17 +267,21 @@ public class MyService : ITransientDependency
[DisablePropertyInjection]
public ITaxCalculator TaxCalculator { get; set; }
}
````
### Resolve Service from IServiceProvider
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject `IServiceProvider` into your class and use the ``GetService`` or the `GetRequiredService` method as shown below:
````C#
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
private readonly ITaxCalculator _taxCalculator;
public MyService(IServiceProvider serviceProvider)
{
_taxCalculator = serviceProvider.GetRequiredService<ITaxCalculator>();
}
}
````
@ -374,15 +383,15 @@ IEnumerable<IExternalLogger> services = _serviceProvider.GetServices<IExternalLo
### Releasing/Disposing Services
If you used a constructor or property injection, you don't need to be concerned about releasing the service's resources. However, if you have resolved a service from ``IServiceProvider``, you might, in some cases, need to take care about releasing the service resources.
If you used a constructor or property injection, you don't need to be concerned about releasing the service's resources. However, if you have resolved a service from ``IServiceProvider``, in some cases, you might need to take care about releasing the service resources.
ASP.NET Core releases all services at the end of a current HTTP request, even if you directly resolved from ``IServiceProvider`` (assuming you injected IServiceProvider). But, there are several cases where you may want to release/dispose manually resolved services:
ASP.NET Core releases all services at the end of a current HTTP request, even if you directly resolved from ``IServiceProvider`` (assuming you injected `IServiceProvider`). But, there are several cases where you may want to release/dispose manually resolved services:
* Your code is executed outside of AspNet Core request and the executer hasn't handled the service scope.
* Your code is executed outside of ASP.NET Core request and the executer hasn't handled the service scope.
* You only have a reference to the root service provider.
* You may want to immediately release & dispose services (for example, you may creating too many services with big memory usage and don't want to overuse memory).
* You may want to immediately release & dispose services (for example, you may creating too many services with big memory usages and don't want to overuse the memory).
In any case, you can use such a 'using' code block to safely and immediately release services:
In any case, you can create a service scope block to safely and immediately release services:
````C#
using (var scope = _serviceProvider.CreateScope())
@ -392,20 +401,55 @@ using (var scope = _serviceProvider.CreateScope())
}
````
Both services are released when the created scope is disposed (at the end of the using block).
Both services are released when the created scope is disposed (at the end of the `using` block).
### Cached Service Providers
ABP provides two special services to optimize resolving services from `IServiceProvider`. `ICachedServiceProvider` and `ITransientCachedServiceProvider` both inherits from the `IServiceProvider` interface and internally caches the resolved services, so you get the same service instance even if you resolve a service multiple times.
The main difference is the `ICachedServiceProvider` is itself registered as scoped, while the `ITransientCachedServiceProvider` is registered as transient to the dependency injection system.
The following example injects the `ICachedServiceProvider` service and resolves a service in the `DoSomethingAsync` method:
````csharp
public class MyService : ITransientDependency
{
private readonly ICachedServiceProvider _serviceProvider;
public MyService(ICachedServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task DoSomethingAsync()
{
var taxCalculator = _serviceProvider.GetRequiredService<ITaxCalculator>();
// TODO: Use the taxCalculator
}
}
````
With such a usage, you don't need to deal with creating service scopes and disposing the resolved services (as explained in the *Releasing/Disposing Services* section above). Because all the services resolved from the `ICachedServiceProvider` will be released once the service scope of the `MyService` instance is disposed. Also, you don't need to care about memory leaks (because of creating too many `ITaxCalculator` instances if we call `DoSomethingAsync` too many times), because only one `ITaxCalculator` instance is created, and it is reused.
Since `ICachedServiceProvider` and `ITransientCachedServiceProvider` extends the standard `IServiceProvider` interface, you can use all the extension method of the `IServiceProvider` interface on them. In addition, they provides some other methods to provide a default value or a factory method for the services that are not found (that means not registered to the dependency injection system). Notice that the default value (or the value returned from your factory method) is also cached and reused.
Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unless you need to create the service cache per usage. `ITransientCachedServiceProvider` guarantees that the created service instances are not shared with any other service, even they are in the same service scope. The services resolved from `ICachedServiceProvider` are shared with other services in the same service scope (in the same HTTP Request, for example), so it can be thought as more optimized.
> ABP Framework also provides the `IAbpLazyServiceProvider` service. It does exists for backward compatibility and works exactly same with the `ITransientCachedServiceProvider` service. So, use the `ITransientCachedServiceProvider` since the `IAbpLazyServiceProvider` might be removed in future ABP versions.
## Advanced Features
### IServiceCollection.OnRegistred Event
### IServiceCollection.OnRegistered Event
You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistred` method as shown below:
You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistered` method as shown below:
````csharp
public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
var type = ctx.ImplementationType;
//...
@ -421,7 +465,7 @@ public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
context.Services.OnRegistered(ctx =>
{
if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true))
{
@ -434,7 +478,7 @@ public class AppModule : AbpModule
This example simply checks if the service class has `MyLogAttribute` attribute and adds `MyLogInterceptor` to the interceptor list if so.
> Notice that `OnRegistred` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors.
> Notice that `OnRegistered` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors.
## 3rd-Party Providers

2
docs/en/Deployment/Clustered-Environment.md

@ -63,7 +63,7 @@ The [Database BLOB provider](../Blob-Storing-Database) is the easiest way since
> [ABP Commercial](https://commercial.abp.io/) startup solution templates come with the database BLOB provider as pre-installed, and stores BLOBs in the application's database.
Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOG storage providers.
Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOB storage providers.
## Configuring Background Jobs

104
docs/en/Deployment/Configuring-Production.md

@ -0,0 +1,104 @@
# Configuring Your Application for Production Environments
ABP Framework has a lot of options to configure and fine-tune its features. They are all explained in their own documents. Default values for these options are pretty well for most of the deployment environments. However, you may need to care about some options based on how you've structured your deployment environment. In this document, we will highlight these kind of options. So, it is highly recommended to read this document in order to not have unexpected behaviors in your system in production.
## Distributed Cache Prefix
ABP's [distributed cache infrastructure](../Caching.md) provides an option to set a key prefix for all of your data that is saved into your distributed cache provider. The default value of this option is not set (it is `null`). If you are using a distributed cache server that is shared by different applications, then you can set a prefix value to isolate an application's cache data from others.
````csharp
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "MyCrmApp";
});
````
That's all. ABP, then will add this prefix to all of your cache keys in your application as along as you use ABP's `IDistributedCache<TCacheItem>` or `IDistributedCache<TCacheItem,TKey>` services. See the [Caching documentation](../Caching.md) if you are new to distributed caching.
> **Warning**: If you use ASP.NET Core's standard `IDistributedCache` service, it's your responsibility to add the key prefix (you can get the value by injecting `IOptions<AbpDistributedCacheOptions>`). ABP can not do it.
> **Warning**: Even if you have never used distributed caching in your own codebase, ABP still uses it for some features. So, you should always configure this prefix if your caching server is shared among multiple systems.
> **Warning**: If you are building a microservice system, then you will have multiple applications that share the same distributed cache server. In such systems, all applications (or services) should normally use the same cache prefix, because you want all the applications to use the same cache data to have consistency between them.
> **Warning**: Some of ABP's startup templates are pre-configured to set a prefix value for the distributed cache. So, please check your application code if it is already configured.
## Distributed Lock Prefix
ABP's [distributed locking infrastructure](../Distributed-Locking.md) provides an option to set a prefix for all the keys you are using in the distributed lock server. The default value of this option is not set (it is `null`). If you are using a distributed lock server that is shared by different applications, then you can set a prefix value to isolate an application's lock from others.
````csharp
Configure<AbpDistributedLockOptions>(options =>
{
options.KeyPrefix = "MyCrmApp";
});
````
That's all. ABP, then will add this prefix to all of your keys in your application. See the [Distributed Locking documentation](../Distributed-Locking.md) if you are new to distributed locking.
> **Warning**: Even if you have never used distributed locking in your own codebase, ABP still uses it for some features. So, you should always configure this prefix if your distributed lock server is shared among multiple systems.
> **Warning**: If you are building a microservice system, then you will have multiple applications that share the same distributed locking server. In such systems, all applications (or services) should normally use the same lock prefix, because you want to globally lock your resources in your system.
> **Warning**: Some of ABP's startup templates are pre-configured to set a prefix value for distributed locking. So, please check your application code if it is already configured.
## Email Sender
ABP's [Email Sending](../Emailing.md) system abstracts sending emails from your application and module code and allows you to configure the email provider and settings in a single place.
Email service is configured to write email contents to the standard [application log](../Logging.md) in development environment. You should configure the email settings to be able to send emails to users in your production environment.
Please see the [Email Sending](../Emailing.md) document to learn how to configure its settings to really send emails.
> **Warning**: If you don't configure the email settings, you will get errors while trying to send emails. For example, the [Account module](../Modules/Account.md)'s *Password Reset* feature sends email to the users to reset their passwords if they forget it.
## SMS Sender
ABP's [SMS Sending abstraction](https://docs.abp.io/en/abp/latest/SMS-Sending) provides a unified interface to send SMS to users. However, its implementation is left to you. Because, typically a paid SMS service is used to send SMS, and ABP doesn't depend on a specific SMS provider.
So, if you are using the `ISmsSender` service, you must implement it yourself, as shown in the following code block:
````csharp
public class MySmsSender : ISmsSender, ITransientDependency
{
public async Task SendAsync(SmsMessage smsMessage)
{
// TODO: Send it using your provider...
}
}
````
> [ABP Commercial](https://commercial.abp.io/) provides a [Twilio SMS Module](https://docs.abp.io/en/commercial/latest/modules/twilio-sms) as a pre-built integration with the popular [Twilio](https://www.twilio.com/) platform.
## BLOB Provider
If you use ABP's [BLOB Storing](https://docs.abp.io/en/abp/latest/Blob-Storing) infrastructure, you should care about the BLOB provider in your production environment. For example, if you use the [File System](../Blob-Storing-File-System.md) provider and your application is running in a Docker container, you should configure a volume mapping for the BLOB storage path. Otherwise, your data will be lost when the container is restarted. Also, the File System is not a good provider for production if you have a [clustered deployment](Clustered-Environment.md) or a microservice system.
Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOB storage providers.
> **Warning**: Even if you don't directly use the BLOB Storage system, a module you are depending on may use it. For example, ABP Commercial's [File Management](https://docs.abp.io/en/commercial/latest/modules/file-management) module stores file contents, and the [Account](https://docs.abp.io/en/commercial/latest/modules/account) module stores user profile pictures in the BLOB Storage system. So, be careful with the BLOB Storing configuration in production. Note that ABP Commercial uses the [Database Provider](../Blob-Storing-Database.md) as a pre-configured BLOB storage provider, which works in production without any problem, but you may still want to use another provider.
## String Encryption
ABP's [`IStringEncryptionService` Service](../String-Encryption.md) simply encrypts and decrypts given strings based on a password phrase. You should configure the `AbpStringEncryptionOptions` options for the production with a strong password and keep it as a secret. You can also configure the other properties of those options class. See the following example:
````csharp
Configure<AbpStringEncryptionOptions>(options =>
{
options.DefaultPassPhrase = "gs5nTT042HAL4it1";
});
````
Note that ABP CLI automatically sets the password to a random value on a new project creation. However, it is stored in the `appsettings.json` file and is generally added to your source control. It is suggested to use [User Secrets](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) or [Environment Variables](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration) to set that value.
## Logging
ABP uses .NET's standard [Logging services](../Logging.md). So, it is compatible with any logging provider that works with .NET. ABP's startup solution templates come with [Serilog](https://serilog.net/) pre-installed and configured for you. It writes logs to file system and console with the initial configuration. File system is useful for development environment, but it is suggested you to use a different provider for your production environment, like Elasticsearch, database or any other provider that can properly work.
## The Swagger UI
ABP's startup solution templates come with [Swagger UI](https://swagger.io/) pre-installed. Swagger is a pretty standard and useful tool to discover and test your HTTP APIs on a built-in UI that is embedded into your application or service. It is typically used in development environment, but you may want to enable it on staging or production environments too.
While you will always secure your HTTP APIs with other techniques (like the [Authorization](../Authorization.md) system), allowing malicious software and people to easily discover your HTTP API endpoint details can be considered as a security problem for some systems. So, be careful while taking the decision of enabling or disabling Swagger for the production environment.
> You may also want to see the [ABP Swagger integration](../API/Swagger-Integration.md) document.

45
docs/en/Deployment/Distributed-Microservice.md

@ -0,0 +1,45 @@
# Deploying Distributed / Microservice Solutions
The ABP Framework is designed to consider distributed and microservice systems, where you have multiple applications and/or services communicating internally. All of its features are compatible with distributed scenarios. This document highlights some points you should care about when you deploy your distributed or microservice solution.
## Application Name & Instance Id
ABP provides the `IApplicationInfoAccessor` service that provides the following properties:
* `ApplicationName`: A human-readable name for an application. It is a unique value for an application.
* `InstanceId`: A random (GUID) value generated by the ABP Framework each time you start the application.
These values are used by the ABP Framework in several places to distinguish the application and the application instance (process) in the system. For example, the [audit logging](../Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`.
The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name.
There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](../Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file:
````json
{
"ApplicationName": "Services.Ordering"
}
````
Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below:
````csharp
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options =>
{
options.ApplicationName = "Services.Ordering";
});
````
## Using a Distributed Event Bus
ABP's [Distributed Event Bus](../Distributed-Event-Bus.md) system provides a standard interface to communicate with other applications and services. While the name is "distributed", the default implementation is in-process. That means, your applications / services can not communicate with each other unless you explicitly configure a distributed event bus provider.
If you are building a distributed system, then the applications should communicate through an external distributed messaging server. Please follow the [Distributed Event Bus](../Distributed-Event-Bus.md) document to learn how to install and configure your distributed event bus provider.
> **Warning**: Even if you don't use the distributed event bus directly in your application code, the ABP Framework and some of the modules you are using may use it. So, if you are building a distributed system, always configure a distributed event bus provider.
> **Info**: [Clustered deployment](Clustered-Environment.md) of a single application is not considered as a distributed system. So, if you only have a single application with multiple instances serving behind a load balancer, a real distributed messaging server may not be needed.
## See Also
* [Deploying to a Clustered Environment](Clustered-Environment.md)

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.
* [Configuring for Production](Configuring-Production.md): Notes for some essential configurations for production environments.
* [Optimization for Production](Optimizing-Production.md): Tips and suggestions for optimizing your application on production environments.
* [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 Distributed / Microservice Solutions](Distributed-Microservice.md): Deployment notes for solutions consisting of multiple applications and/or services.

22
docs/en/Deployment/Optimizing-Production.md

@ -0,0 +1,22 @@
# Optimizing Your Application for Production Environments
ABP Framework and the startup solution templates are configured well to get the maximum performance on production environments. However, there are still some points you need to pay attention to in order to optimize your system in production. In this document, we will mention some of these topics.
## Caching Static Contents
The following items are contents that can be cached in the client side (typically in the Browser) or in a CDN server:
* **Static images** can always be cached. Here, you should be careful that if you change an image, use a different file name, or use a versioning query-string parameter, so the browser (or CDN) understands it's been changed.
* **CSS and JavaScript files**. ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system always uses a query-string versioning parameter and a hash value in the files names of the CSS & JavaScript files for the [MVC (Razor Pages)](../UI/AspNetCore/Overall.md) UI. So, you can safely cache these files in the client side or in a CDN server.
* **Application bundle files** of an [Angular UI](../UI/Angular/Quick-Start.md) application.
* **[Application Localization Endpoint](../API/Application-Localization.md)** can be cached per culture (it already has a `cultureName` query string parameter) if you don't use dynamic localization on the server-side. ABP Commercial's [Language Management](https://commercial.abp.io/modules/Volo.LanguageManagement) module provides dynamic localization. If you're using it, you can't cache that endpoint forever. However, you can still cache it for a while. Applying dynamic localization text changes to the application can delay for a few minutes, even for a few hours in a real life scenario.
There may be more ways based on your solution structure and deployment environment, but these are the essential points you should consider to client-side cache in a production environment.
## Bundling & Minification for MVC (Razor Pages) UI
ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system automatically bundles, minifies and versions your CSS and JavaScript files in production environment. Normally, you don't need to do anything, if you haven't disabled it yourself in your application code. It is important to follow the [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) document and truly use the system to get the maximum optimization.
## Background Jobs
ABP's [Background Jobs](../Background-Jobs.md) system provides an abstraction with a basic implementation to enqueue jobs and execute them in a background thread. ABP's Default Background Job Manager may not be enough if you are adding too many jobs to the queue and want them to be executed in parallel by multiple servers with a high performance. If you need these, you should consider to configure a dedicated background job software, like [Hangfire](https://www.hangfire.io/). ABP has a pre-built [Hangfire integration](../Background-Jobs-Hangfire.md), so you can switch to Hangfire without changing your application code.

91
docs/en/Distributed-Event-Bus.md

@ -296,6 +296,74 @@ This example;
> Distributed event system use the [object to object mapping](Object-To-Object-Mapping.md) system to map `Product` objects to `ProductEto` objects. So, you need to configure the object mapping (`Product` -> `ProductEto`) too. You can check the [object to object mapping document](Object-To-Object-Mapping.md) to learn how to do it.
## Entity Synchronizer
In a distributed (or microservice) system, it is typical to subscribe to change events for an [entity](Entities.md) type of another service, so you can get notifications when the subscribed entity changes. In that case, you can use ABP's Pre-Defined Events as explained in the previous section.
If your purpose is to store your local copies of a remote entity, you typically subscribe to create, update and delete events of the remote entity and update your local database in your event handler. ABP provides a pre-built `EntitySynchronizer` base class to make that operation easier for you.
Assume that there is a `Product` entity (probably an aggregate root entity) in a Catalog microservice, and you want to keep copies of the products in your Ordering microservice, with a local `OrderProduct` entity. In practice, properties of the `OrderProduct` class will be a subset of the `Product` properties, because not all the product data is needed in the Ordering microservice (however, you can make a full copy if you need). Also, the `OrderProduct` entity may have additional properties that are populated and used in the Ordering microservice.
The first step to establish the synchronization is to define an ETO (Event Transfer Object) class in the Catalog microservice that is used to transfer the event data. Assuming the `Product` entity has a `Guid` key, your ETO can be as shown below:
````csharp
[EventName("product")]
public class ProductEto : EntityEto<Guid>
{
// Your Product properties here...
}
````
`ProductEto` can be put in a shared project (DLL) that is referenced by the Catalog and the Ordering microservices. Alternatively, you can put a copy of the `ProductEto` class in the Ordering microservice if you don't want to introduce a common project dependency between the services. In this case, the `EventName` attribute becomes critical to map the `ProductEto` classes across two services (you should use the same event name).
Once you define an ETO class, you should configure the ABP Framework to publish auto (create, update and delete) events for the `Product` entity, as explained in the previous section:
````csharp
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.AutoEventSelectors.Add<Product>();
options.EtoMappings.Add<Product, ProductEto>();
});
````
Finally, you should create a class in the Ordering microservice, that is derived from the `EntitySynchronizer` class:
````csharp
public class ProductSynchronizer : EntitySynchronizer<OrderProduct, Guid, ProductEto>
{
public ProductSynchronizer(
IObjectMapper objectMapper,
IRepository<OrderProduct, Guid> repository
) : base(objectMapper, repository)
{
}
}
````
The main point of this class is it subscribes to the create, update and delete events of the source entity and updates the local entity in the database. It uses the [Object Mapper](Object-To-Object-Mapping.md) system to create or update the `OrderProduct` objects from the `ProductEto` objects. So, you should also configure the object mapper to make it properly work. Otherwise, you should manually perform the object mapping by overriding the `MapToEntityAsync(TSourceEntityEto)` and `MapToEntityAsync(TSourceEntityEto,TEntity)` methods in your `ProductSynchronizer` class.
If your entity has a composite primary key (see the [Entities document](Entities.md)), then you should inherit from the `EntitySynchronizer<TEntity, TSourceEntityEto>` class (just don't use the `Guid` generic argument in the previous example) and implement `FindLocalEntityAsync` to find the entity in your local database using the `Repository`.
`EntitySynchronizer` is compatible with the *Entity Versioning* system (see the [Entities document](Entities.md)). So, it works as expected even if the events are received as disordered. If the entity's version in your local database is newer than the entity in the received event, then the event is ignored. You should implement the `IHasEntityVersion` interface for the entity and ETO classes (for this example, you should implement for the `Product`, `ProductEto` and `OrderProduct` classes).
If you want to ignore some type of change events, you can set `IgnoreEntityCreatedEvent`, `IgnoreEntityUpdatedEvent` and `IgnoreEntityDeletedEvent` in the constructor of your class. Example:
````csharp
public class ProductSynchronizer
: EntitySynchronizer<OrderProduct, Guid, ProductEto>
{
public ProductSynchronizer(
IObjectMapper objectMapper,
IRepository<OrderProduct, Guid> repository
) : base(objectMapper, repository)
{
IgnoreEntityDeletedEvent = true;
}
}
````
> Notice that the `EntitySynchronizer` can only create/update the entities after you use it. If you have an existing system with existing data, you should manually copy the data for one time, because the `EntitySynchronizer` starts to work.
## Transaction and Exception Handling
Distributed event bus works in-process (since default implementation is `LocalDistributedEventBus`) unless you configure an actual provider (e.g. [Kafka](Distributed-Event-Bus-Kafka-Integration.md) or [RabbitMQ](Distributed-Event-Bus-RabbitMQ-Integration.md)). In-process event bus always executes event handlers in the same [unit of work](Unit-Of-Work.md) scope that you publishes the events in. That means, if an event handler throws an exception, then the related unit of work (the database transaction) is rolled back. In this way, your application logic and event handling logic becomes transactional (atomic) and consistent. If you want to ignore errors in an event handler, you must use a `try-catch` block in your handler and shouldn't re-throw the exception.
@ -434,6 +502,29 @@ Here, the following properties are available on the `config` object:
* `HandlerSelector`: A predicate to filter the event handled types (classes implementing the `IDistributedEventHandler<TEvent>` interface) to be used for this configuration. This is especially useful if you want to ignore some event handler types from inbox processing, or want to define named inbox configurations and group event handlers within these configurations. See the *Named Configurations* section.
* `ImplementationType`: Type of the class that implements the database operations for the inbox. This is normally set when you call `UseDbContext` as shown before. See *Implementing a Custom Outbox/Inbox Database Provider* section for advanced usages.
#### AbpEventBusBoxesOptions
`AbpEventBusBoxesOptions` can be used to fine-tune how inbox and outbox systems work. For most of the systems, using the defaults would be more than enough, but you can configure it to optimize your system when it is needed.
Just like all the [options classes](Options.md), `AbpEventBusBoxesOptions` can be configured in the `ConfigureServices` method of your [module class](Module-Development-Basics.md) as shown in the following code block:
````csharp
Configure<AbpEventBusBoxesOptions>(options =>
{
// TODO: configure the options
});
````
`AbpEventBusBoxesOptions` has the following properties to be configured:
* `BatchPublishOutboxEvents`: Can be used to enable or disable batch publishing events to the message broker. Batch publishing works if it is supported by the distributed event bus provider. If not supported, events are sent one by one as the fallback logic. Keep it as enabled since it has a great performance gain wherever possible. Default value is `true` (enabled).
* `PeriodTimeSpan`: The period of the inbox and outbox message processors to check if there is a new event in the database. Default value is 2 seconds (`TimeSpan.FromSeconds(2)`).
* `CleanOldEventTimeIntervalSpan`: The event inbox system periodically checks and deletes the old processed events from the inbox in the database. You can set this value to determine the check period. Default value is 6 hours (`TimeSpan.FromHours(6)`).
* `WaitTimeToDeleteProcessedInboxEvents`: Inbox events are not deleted from the database for a while even if they are successfully processed. This is for a system to prevent multiple process of the same event (if the event broker sends it twice). This configuration value determines the time to keep the processed events. Default value is 2 hours (`TimeSpan.FromHours(2)`).
* `InboxWaitingEventMaxCount`: The maximum number of events to query at once from the inbox in the database. Default value is 1000.
* `OutboxWaitingEventMaxCount`: The maximum number of events to query at once from the outbox in the database. Default value is 1000.
* `DistributedLockWaitDuration`: ABP uses [distributed locking](Distributed-Locking.md) to prevent concurrent access to the inbox and outbox messages in the database, when running multiple instance of the same application. If an instance of the application can not obtain the lock, it tries after a duration. This is the configuration of that duration. Default value is 15 seconds (`TimeSpan.FromSeconds(15)`).
### Skipping Outbox
`IDistributedEventBus.PublishAsync` method provides an optional parameter, `useOutbox`, which is set to `true` by default. If you bypass outbox and immediately publish an event, you can set it to `false` for a specific event publishing operation.

23
docs/en/Entities.md

@ -316,6 +316,29 @@ All these base classes also have non-generic versions to take `AuditedEntity` an
All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser<TUser>` and `FullAuditedAggregateRootWithUser<TKey, TUser>`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern). Also, if you add navigation properties to the AppUser class that comes with the startup template, consider to handle (ignore/map) it on the migration dbcontext (see [the EF Core migration document](Entity-Framework-Core-Migrations.md)).
## Caching Entities
ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database.
It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted.
> See the [Entity Cache](Entity-Cache.md) documentation for more information.
## Versioning Entities
ABP defines the `IHasEntityVersion` interface for automatic versioning of your entities. It only provides a single `EntityVersion` property, as shown in the following code block:
````csharp
public interface IHasEntityVersion
{
int EntityVersion { get; }
}
````
If you implement the `IHasEntityVersion` interface, ABP automatically increases the `EntityVersion` value whenever you update your entity. The initial `EntityVersion` value will be `0`, when you first create an entity and save to the database.
> ABP can not increase the version if you directly execute SQL `UPDATE` commands in the database. It is your responsibility to increase the `EntityVersion` value in that case. Also, if you are using the aggregate pattern and change sub-collections of an aggregate root, it is your responsibility if you want to increase the version of the aggregate root object.
## Extra Properties
ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API.

131
docs/en/Entity-Cache.md

@ -0,0 +1,131 @@
# Entity Cache
ABP Framework provides an entity caching system that works on top of the [distributed caching](Caching.md) system. It does the following operations on behalf of you:
* Gets the entity from the database (by using the [repositories](Repositories.md)) in its first call and then gets it from the cache in subsequent calls.
* Automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached.
## Caching Entity Objects
`IEntityCache<TEntityCacheItem, TKey>` is a simple service provided by the ABP Framework for caching entities. Assume that you have a `Product` entity as shown below:
```csharp
public class Product : AggregateRoot<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public float Price { get; set; }
public int StockCount { get; set; }
}
```
If you want to cache this entity, you should first configure the [dependency injection](Dependency-Injection.md) system to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md):
```csharp
context.Services.AddEntityCache<Product, Guid>();
```
Now you can inject the `IEntityCache<Product, Guid>` service wherever you need:
```csharp
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IEntityCache<Product, Guid> _productCache;
public ProductAppService(IEntityCache<Product, Guid> productCache)
{
_productCache = productCache;
}
public async Task<ProductDto> GetAsync(Guid id)
{
var product = await _productCache.GetAsync(id);
return ObjectMapper.Map<Product, ProductDto>(product);
}
}
```
> Note that we've used the `ObjectMapper` service to map from `Product` to `ProductDto`. You should configure that [object mapping](Object-To-Object-Mapping.md) to make that example service properly works.
That's all. The cache name (in the distributed cache server) will be full name (with namespace) of the `Product` class. You can use the `[CacheName]` attribute to change it. Please refer to the [caching document](Caching.md) for details.
## Using a Cache Item Class
In the previous section, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable to JSON (and deserializable from JSON). Sometimes that might not be possible or you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity.
Assume that we've created a `ProductDto` class as shown below:
```csharp
public class ProductDto : EntityDto<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public float Price { get; set; }
public int StockCount { get; set; }
}
```
Now, we can register the entity cache services to [dependency injection](Dependency-Injection.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md) with three generic parameters, as shown below:
```csharp
context.Services.AddEntityCache<Product, ProductDto, Guid>();
```
Since the entity cache system will perform the [object mapping](Object-To-Object-Mapping.md) (from `Product` to `ProductDto`), we should configure the object map. Here, an example configuration with [AutoMapper](https://automapper.org/):
```csharp
public class MyMapperProfile : Profile
{
public MyMapperProfile()
{
CreateMap<Product, ProductDto>();
}
}
```
Now, you can inject the `IEntityCache<ProductDto, Guid>` service wherever you want:
```csharp
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IEntityCache<ProductDto, Guid> _productCache;
public ProductAppService(IEntityCache<ProductDto, Guid> productCache)
{
_productCache = productCache;
}
public async Task<ProductDto> GetAsync(Guid id)
{
return await _productCache.GetAsync(id);
}
}
```
Notice that the `_productCache.GetAsync` method already returns a `ProductDto` object, so we could directly return it from out application service.
## Configuration
All of the `context.Services.AddEntityCache()` methods get an optional `DistributedCacheEntryOptions` parameter where you can easily configure the caching options:
```csharp
context.Services.AddEntityCache<Product, ProductDto, Guid>(
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30)
}
);
```
> The default cache duration is **2 minutes** with the `AbsoluteExpirationRelativeToNow` configuration.
## Additional Notes
* Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as explained before.
* Entity Caching System is designed as **read-only**. You should use the standard [repository](Repositories.md) methods to manipulate the entity if you need. If you need to manipulate (update) the entity, do not get it from the entity cache. Instead, read it from the repository, change it and update using the repository.
## See Also
* [Distributed caching](Caching.md)
* [Entities](Entities.md)
* [Repositories](Repositories.md)

20
docs/en/Entity-Framework-Core.md

@ -834,7 +834,7 @@ One advantage of using an interface for a DbContext is then it will be replaceab
Once you properly define and use an interface for DbContext, then any other implementation can use the following ways to replace it:
**ReplaceDbContextAttribute**
#### ReplaceDbContext Attribute
```csharp
[ReplaceDbContext(typeof(IBookStoreDbContext))]
@ -844,7 +844,7 @@ public class OtherDbContext : AbpDbContext<OtherDbContext>, IBookStoreDbContext
}
```
**ReplaceDbContext option**
#### ReplaceDbContext Option
````csharp
context.Services.AddAbpDbContext<OtherDbContext>(options =>
@ -856,6 +856,22 @@ context.Services.AddAbpDbContext<OtherDbContext>(options =>
In this example, `OtherDbContext` implements `IBookStoreDbContext`. This feature allows you to have multiple DbContext (one per module) on development, but single DbContext (implements all interfaces of all DbContexts) on runtime.
#### Replacing with Multi-Tenancy
It is also possible to replace a DbContext based on the [multi-tenancy](Multi-Tenancy.md) side. `ReplaceDbContext` attribute and `ReplaceDbContext` method can get a `MultiTenancySides` option with a default value of `MultiTenancySides.Both`.
**Example:** Replace DbContext only for tenants, using the `ReplaceDbContext` attribute
````csharp
[ReplaceDbContext(typeof(IBookStoreDbContext), MultiTenancySides.Tenant)]
````
**Example:** Replace DbContext only for the host side, using the `ReplaceDbContext` method
````csharp
options.ReplaceDbContext<IBookStoreDbContext>(MultiTenancySides.Host);
````
### Split Queries
ABP enables [split queries](https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries) globally by default for better performance. You can change it as needed.

2
docs/en/Getting-Started-AspNetCore-Application.md

@ -1,4 +1,4 @@
# Getting Started With an ABP and AspNet Core MVC Web Application
# Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application
This tutorial explains how to start ABP from scratch with minimal dependencies. You generally want to start with the **[startup template](Getting-Started-AspNetCore-MVC-Template.md)**.

14
docs/en/Getting-Started-React-Native.md

@ -95,6 +95,20 @@ A React Native application running on an Android emulator or a physical phone **
Run the backend application as described in the [getting started document](Getting-Started.md).
> You should turn off the "Https Restriction" if you're using OpenIddict as a central identity management solution. Because the IOS Simulator doesn't support self-signed certificates and OpenIddict is set to only work with HTTPS by default.
## How to disable the Https-only settings of OpenIddict
Go to MyProjectNameHttpApiHostModule.cs under the host project. Add put these codes under the `PreConfigureServices` function.
```csharp
#if DEBUG
PreConfigure<OpenIddictServerBuilder>(options => {
options.UseAspNetCore()
.DisableTransportSecurityRequirement();
});
#endif
```
## How to Configure & Run the React Native Application

10
docs/en/Getting-Started-Running-Solution.md

@ -113,11 +113,11 @@ This is the HTTP API that is used by the web application.
3. Lastly, ensure that the {{if UI=="MVC"}}`.Web`{{else}}`.Blazor`{{end}} project is the startup project and run the application which will open a **welcome** page in your browser
![mvc-tiered-app-home](images/bookstore-home.png)
![mvc-tiered-app-home](images/bookstore-home-2.png)
Click to the **login** button which will redirect you to the *authentication server* to login to the application:
![bookstore-login](images/bookstore-login.png)
![bookstore-login](images/bookstore-login-2.png)
{{ else # Tiered != "Yes" }}
@ -125,7 +125,7 @@ Ensure that the {{if UI=="MVC"}}`.Web`{{else}}`.Blazor`{{end}} project is the st
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
![bookstore-login](images/bookstore-login.png)
![bookstore-login](images/bookstore-login-2.png)
{{ end # Tiered }}
@ -171,7 +171,7 @@ Ensure that the `.Blazor` project is the startup project and run the application
Once the application starts, click to the **Login** link on to header, which redirects you to the authentication server to enter a username and password:
![bookstore-login](images/bookstore-login.png)
![bookstore-login](images/bookstore-login-2.png)
{{ else if UI == "NG" }}
@ -191,7 +191,7 @@ yarn start
It may take a longer time for the first build. Once it finishes, it opens the Angular UI in your default browser with the [localhost:4200](http://localhost:4200/) address.
![bookstore-login](images/bookstore-login.png)
![bookstore-login](images/bookstore-login-2.png)
{{ end }}

2
docs/en/Getting-Started-Setup-Environment.md

@ -19,7 +19,7 @@ First things first! Let's setup your development environment before creating the
The following tools should be installed on your development machine:
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development.
{{ if UI != "Blazor" }}
* [Node v16 or v18](https://nodejs.org/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[1](#f-yarn)</sup> or npm v6+ (already installed with Node)

2
docs/en/Getting-Started.md

@ -11,8 +11,6 @@
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Contents
This tutorial explains how to **create and run** a new web application using the ABP Framework. Follow the steps below;
1. [Setup your development environment](Getting-Started-Setup-Environment.md)

80
docs/en/Integration-Services.md

@ -0,0 +1,80 @@
# Integration Services
The *Integration Service* concept was created to distinguish the [application services](Application-Services.md) that are built for inter-module (or inter-microservice) communication from the application services that are intended to be consumed from a UI or a client application.
The following figure shows a few microservices behind an API Gateway that is consumed by a UI application and 3rd-party client applications:
![integration-services](images/integration-services.png)
HTTP requests coming from out of the API Gateway can be called as *external request*, while the HTTP requests performed between microservices can be considered as *internal requests*. The application services that are designed to respond to these external requests are called as *integration services*, because their purpose is to integrate microservices in the system, rather than respond to user requests.
## Marking an Application Service as Integration Service
Assume that you have an application service named `ProductAppService`, and you want to use that application service as an integration service. In that case, you can use the `[IntegrationService]` attribute on top of the application service class as shown below:
```csharp
[IntegrationService]
public class ProductAppService : ApplicationService, IProductAppService
{
// ...
}
```
If your application service has an interface, like `IProductService` in this example, you can use it on the service interface:
```csharp
[IntegrationService]
public interface IProductAppService : IApplicationService
{
// ...
}
```
> If you've used the `[IntegrationService]` on top of your service interface, it is *not needed* to use on the service class too.
That's all. From now, ABP will handle your application service as integration service and implement the followings by convention:
* If you are using the [Auto API Controllers](API/Auto-API-Controllers.md) feature in your application, the URL prefix will be `/integration-api` instead of `/api` for your integration services. Thus, you can distinguish internal and external service communications and take additional actions, such as preventing REST API calls for integration services out of API Gateway.
* Audit logging is disabled by default for the integration services. See the next section if you want to enable it.
## Configuration
### Enabling/Disabling the Audit Logging
Audit Logging is disabled by default for integration services but it can be enabled by configuring the `AbpAuditingOptions` [options class](Options.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md):
```csharp
Configure<AbpAuditingOptions>(options =>
{
options.IsEnabledForIntegrationService = true;
});
```
> Please refer to the [audit logging document](Audit-Logging.md) for other options and details.
### Filtering Auto API Controllers
You can filter integration services (or non-integration services) while creating [Auto API Controllers](API/Auto-API-Controllers.md), using the `ApplicationServiceTypes` option of the `ConventionalControllerSetting` by configuring the `AbpAspNetCoreMvcOptions` as shown below:
```csharp
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(
typeof(MyApplicationModule).Assembly,
conventionalControllerSetting =>
{
conventionalControllerSetting.ApplicationServiceTypes =
ApplicationServiceTypes.IntegrationServices;
});
});
```
Tip: You can call the `options.ConventionalControllers.Create` multiple times to configure regular application services and integration services with different options.
> Please refer to the [Auto API Controllers document](API/Auto-API-Controllers.md) for more information about the Auto API Controller system.
## See Also
* [Application Services](Application-Services.md)
* [Auto API Controllers](API/Auto-API-Controllers.md)
* [Audit Logging](Audit-Logging.md)

6
docs/en/JSON.md

@ -66,3 +66,9 @@ Add [Volo.Abp.Json.Newtonsoft](https://www.nuget.org/packages/Volo.Abp.Json.Newt
#### AbpNewtonsoftJsonSerializerOptions
- **JsonSerializerSettings(`Newtonsoft.Json.JsonSerializerSettings`)**: Global options for Newtonsoft library operations. See [here](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonSerializerSettings.htm) for reference.
## Configuring JSON options in ASP.NET Core
You can change the JSON behavior in ASP.NET Core by configuring [JsonOptions](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.jsonoptions) or
[MvcNewtonsoftJsonOptions](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.mvcnewtonsoftjsonoptions)(if you use `Newtonsoft.Json`)

10
docs/en/KB/Windows-Path-Too-Long-Fix.md

@ -0,0 +1,10 @@
# How to Fix "Filename too long" Error on Windows
If you encounter the "filename too long" or "unzip" error on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
If you face long path errors related to Git, try the following command to enable long paths in Windows.
```
git config --system core.longpaths true
```
See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path

110
docs/en/Migration-Guides/Abp-7_0.md

@ -101,3 +101,113 @@ See https://github.com/abpframework/abp/pull/13644 for more info.
We've already done this for our themes.
See https://github.com/abpframework/abp/pull/13845 for more info.
## Replaced `BlogPostPublicDto` with `BlogPostCommonDto`
- In the CMS Kit Module, `BlogPostPublicDto` has been moved to `Volo.CmsKit.Common.Application.Contracts` from `Volo.CmsKit.Public.Application.Contracts` and renamed to `BlogPostCommonDto`.
- See the [PR#13499](https://github.com/abpframework/abp/pull/13499) for more information.
> You can ignore this if you don't use CMS Kit Module.
## Data migration environment
Please call `AddDataMigrationEnvironment` method in the migration project.
```cs
using (var application = await AbpApplicationFactory.CreateAsync<MyMigratorModule>(options =>
{
//...
options.AddDataMigrationEnvironment();
}))
{
//...
}
```
```cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDataMigrationEnvironment();
// Call AddDataMigrationEnvironment before AddApplicationAsync
await builder.AddApplicationAsync<MyMigratorModule>();
//...
```
See https://github.com/abpframework/abp/pull/13985 for more info.
## Devart.Data.Oracle.EFCore
The `Devart.Data.Oracle.EFCore` package do not yet support EF Core 7.0, If you use `AbpEntityFrameworkCoreOracleDevartModule(Volo.Abp.EntityFrameworkCore.Oracle.Devart)` may not work as expected, We will release new packages as soon as they are updated.
See https://github.com/abpframework/abp/issues/14412 for more info.
# Changes on Angular Apps
## Added a new package `@abp/ng.oauth`
OAuth Functionality moved to a seperate package named `@abp/ng.oauth`, so ABP users should add the `@abp/ng.oauth` packages on app.module.ts.
Add the new npm package to your app.
```
yarn add @abp/ng.oauth
// or npm i ---save @abp/ng.oauth
```
```typescript
// app.module.ts
import { AbpOAuthModule } from "@abp/ng.oauth";
// ...
@NgModule({
// ...
imports: [
AbpOAuthModule.forRoot(), // <-- Add This
// ...
],
// ...
})
export class AppModule {}
```
## Lepton X Google-Font
If you are using LeptonX that has google fonts, the fonts were built-in the Lepton file. It's been moved to a seperate file. So the ABP user should add font-bundle in angular.json. ( under the 'yourProjectName' > 'architect' > 'build' > 'options' >'styles' )
// for LeptonX Lite
```json
{
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css',
inject: false,
bundleName: 'font-bundle.rtl',
},
{
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css',
inject: false,
bundleName: 'font-bundle',
},
```
// for LeptonX
```json
{
input: 'node_modules/@volosoft/ngx-lepton-x/assets/css/font-bundle.css',
inject: false,
bundleName: 'font-bundle',
},
{
input: 'node_modules/@volosoft/ngx-lepton-x/assets/css/font-bundle.rtl.css',
inject: false,
bundleName: 'font-bundle.rtl',
},
```
## Updated Side Menu Layout
In side menu layout, eThemeLeptonXComponents.Navbar has been changed to eThemeLeptonXComponents.Toolbar, and
eThemeLeptonXComponents.Sidebar to eThemeLeptonXComponents.Navbar.
And also added new replaceable component like Logo Component, Language Component etc.
If you are using replaceable component system you can check [documentation](https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular#customization).
## ng-zorro-antd-tree.css
ng-zorro-antd-tree.css file should be in angular.json if the user uses AbpTree component or Abp-commercial. The ABP User should add this style definition on angular.json. ( under the 'yourProjectName' > 'architect' > 'build' > 'options' >'styles' )
{ "input": "node_modules/ng-zorro-antd/tree/style/index.min.css", "inject": false, "bundleName": "ng-zorro-antd-tree" },

19
docs/en/Migration-Guides/Abp-7_1.md

@ -0,0 +1,19 @@
# ABP Version 7.1 Migration Guide
This document is a guide for upgrading ABP v7.0 solutions to ABP v7.1. There are a few changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application.
## Navigation Menu - `CustomData` type changed to `Dictionary<string, object>`
`ApplicationMenu` and `ApplicationMenuItem` classes' `CustomData` property type has been changed to `Dictionary<string, object>`. So, if you use the optional `CustomData` property of these classes, change it accordingly. See [#15608](https://github.com/abpframework/abp/pull/15608) for more information.
*Old usage:*
```csharp
var menu = new ApplicationMenu("Home", L["Home"], "/", customData: new MyCustomData());
```
*New usage:*
```csharp
var menu = new ApplicationMenu("Home", L["Home"], "/").WithCustomData("CustomDataKey", new MyCustomData());
```

230
docs/en/Migration-Guides/IdentityServer4-Step-by-Step.md

@ -0,0 +1,230 @@
# Migrating from OpenIddict to IdentityServer4 Step by Step Guide
ABP startup templates use `OpenIddict` OpenID provider from v6.0.0 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is the default OpenID provider library for ABP templates since v6.0, you may want to keep using [IdentityServer4](https://github.com/IdentityServer/IdentityServer4) library, even it is **archived and no longer maintained by the owners**. ABP doesn't provide support for newer versions of IdentityServer. This guide provides layer-by-layer guidance for migrating your existing [OpenIddict](https://github.com/openiddict/openiddict-core) application to IdentityServer4.
## IdentityServer4 Migration Steps
Use the `abp update` command to update your existing application. See [Upgrading docs](../Upgrading.md) for more info. Apply required migrations by following the [Migration Guides](Index.md) based on your application version.
### Domain.Shared Layer
- In **MyApplication.Domain.Shared.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.Domain.Shared" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.Domain.Shared" Version="6.0.*" />
```
- In **MyApplicationDomainSharedModule.cs** replace usings and **module dependencies:**
```csharp
using Volo.Abp.OpenIddict;
...
typeof(AbpOpenIddictDomainSharedModule)
```
with
```csharp
using Volo.Abp.IdentityServer;
...
typeof(AbpIdentityServerDomainSharedModule)
```
### Domain Layer
- In **MyApplication.Domain.csproj** replace **project references**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.Domain" Version="6.0.*" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.OpenIddict" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.Domain" Version="6.0.*" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.IdentityServer" Version="6.0.*" />
```
- In **MyApplicationDomainModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement.OpenIddict;
...
typeof(AbpOpenIddictDomainModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
```
with
```csharp
using Volo.Abp.IdentityServer;
using Volo.Abp.PermissionManagement.IdentityServer;
...
typeof(AbpIdentityServerDomainModule),
typeof(AbpPermissionManagementDomainIdentityServerModule),
```
#### OpenIddictDataSeedContributor
DataSeeder is the most important part for starting the application since it seeds the initial data for both OpenID providers.
- Create a folder named *IdentityServer* under the Domain project and copy the [IdentityServerDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/master/Ids2OpenId/src/Ids2OpenId.Domain/IdentityServer/IdentityServerDataSeedContributor.cs) under this folder. **Rename** all the `OpenId2Ids` with your project name.
- Delete *OpenIddict* folder that contains `OpenIddictDataSeedContributor.cs` which is no longer needed.
### EntityFrameworkCore Layer
If you are using MongoDB, skip this step and check the *MongoDB* layer section.
- In **MyApplication.EntityFrameworkCore.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" Version="6.0.*" />
```
- In **MyApplicationEntityFrameworkCoreModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
typeof(AbpOpenIddictEntityFrameworkCoreModule),
```
with
```csharp
using Volo.Abp.IdentityServer.EntityFrameworkCore;
...
typeof(AbpIdentityServerEntityFrameworkCoreModule),
```
- In **MyApplicationDbContext.cs** replace usings and **fluent api configurations**:
```csharp
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
...
builder.ConfigureOpenIddict();
```
with
```csharp
using Volo.Abp.IdentityServer.EntityFrameworkCore;
...
using Volo.Abp.OpenIddict.EntityFrameworkCore;
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
...
builder.ConfigureIdentityServer();
```
> Not: You need to create new migration after updating the fluent api. Navigate to *EntityFrameworkCore* folder and add a new migration. Ex, `dotnet ef migrations add Updated_To_IdentityServer `
### MongoDB Layer
If you are using EntityFrameworkCore, skip this step and check the *EntityFrameworkCore* layer section.
- In **MyApplication.MongoDB.csproj** replace **project reference**:
```csharp
<PackageReference Include="Volo.Abp.OpenIddict.MongoDB" Version="6.0.*" />
```
with
```csharp
<PackageReference Include="Volo.Abp.IdentityServer.MongoDB" Version="6.0.*" />
```
- In **MyApplicationMongoDbModule.cs** replace usings and **module dependencies**:
```csharp
using Volo.Abp.OpenIddict.MongoDB;
...
typeof(AbpOpenIddictMongoDbModule),
```
with
```csharp
using Volo.Abp.IdentityServer.MongoDB;
...
typeof(AbpIdentityServerMongoDbModule),
```
### DbMigrator Project
- In `appsettings.json` **replace OpenIddict section with IdentityServer** since IdentityServerDataSeeder will be using these information for initial data seeding:
```json
"IdentityServer": { // Rename OpenIddict to IdentityServer
"Clients ": { // Rename Applications to Clients
...
}
}
```
### Test Project
- In **MyApplicationTestBaseModule.cs** **add** the IdentityServer related using and PreConfigurations:
```csharp
using Volo.Abp.IdentityServer;
```
and
```csharp
PreConfigure<AbpIdentityServerBuilderOptions>(options =>
{
options.AddDeveloperSigningCredential = false;
});
PreConfigure<IIdentityServerBuilder>(identityServerBuilder =>
{
identityServerBuilder.AddDeveloperSigningCredential(false, System.Guid.NewGuid().ToString());
});
```
to `PreConfigureServices` to run authentication related unit tests.
### UI Layer
You can follow the migrations guides from IdentityServer to OpenIddict in **reverse order** to update your UIs. You can also check the source-code for [Index.cshtml.cs](https://github.com/abpframework/abp-samples/blob/master/OpenId2Ids/src/OpenId2Ids.AuthServer/Pages/Index.cshtml) and [Index.cshtml](https://github.com/abpframework/abp-samples/blob/master/OpenId2Ids/src/OpenId2Ids.AuthServer/Pages/Index.cshtml.cs) files for **AuthServer** project.
- [Angular UI Migration](OpenIddict-Angular.md)
- [MVC/Razor UI Migration](OpenIddict-Mvc.md)
- [Blazor-Server UI Migration](OpenIddict-Blazor-Server.md)
- [Blazor-Wasm UI Migration](OpenIddict-Blazor.md)
## Source code of samples and module
* [Open source tiered & separate auth server application migrate OpenIddict to Identity Server](https://github.com/abpframework/abp-samples/tree/master/OpenId2Ids)
* [IdentityServer module document](https://docs.abp.io/en/abp/6.0/Modules/IdentityServer)
* [IdentityServer module source code](https://github.com/abpframework/abp/tree/rel-6.0/modules/identityserver)

1
docs/en/Migration-Guides/Index.md

@ -2,6 +2,7 @@
The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../Upgrading.md).
- [7.0 to 7.1](Abp-7_1.md)
- [6.0 to 7.0](Abp-7_0.md)
- [5.3 to 6.0](Abp-6_0.md)
- [5.2 to 5.3](Abp-5_3.md)

2
docs/en/Migration-Guides/OpenIddict-Angular.md

@ -112,7 +112,7 @@
## IdentityServer
This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
- In **MyApplication.IdentityServer.csproj** replace **project references**:

2
docs/en/Migration-Guides/OpenIddict-Blazor-Server.md

@ -103,7 +103,7 @@
## IdentityServer
This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
- In **MyApplication.IdentityServer.csproj** replace **project references**:

8
docs/en/Migration-Guides/OpenIddict-Blazor.md

@ -62,7 +62,7 @@
typeof(AbpAccountWebOpenIddictModule),
```
- In the **MyApplicationBlazorModule.cs** add `PreConfigureServices` like below with your application name as the audience:
- In the **MyApplicationHostModule.cs** add `PreConfigureServices` like below with your application name as the audience:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
@ -79,7 +79,7 @@
}
```
- In the **MyApplicationBlazorModule.cs** `ConfigureServices` method, **replace the method call**:
- In the **MyApplicationHostModule.cs** `ConfigureServices` method, **replace the method call**:
From `ConfigureAuthentication(context, configuration);` to `ConfigureAuthentication(context);` and update the method as:
@ -90,7 +90,7 @@
}
```
- In the **MyApplicationBlazorModule.cs** `OnApplicationInitialization` method, **replace the midware**:
- In the **MyApplicationHostModule.cs** `OnApplicationInitialization` method, **replace the midware**:
```csharp
app.UseJwtTokenMiddleware();
@ -131,7 +131,7 @@
## IdentityServer
This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
- In **MyApplication.IdentityServer.csproj** replace **project references**:

2
docs/en/Migration-Guides/OpenIddict-Mvc.md

@ -111,7 +111,7 @@ Replace role scope to **roles** and add **UsePkce** and **SignoutScheme** option
## IdentityServer
This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future.
- In **MyApplication.IdentityServer.csproj** replace **project references**:

2
docs/en/Migration-Guides/OpenIddict-Step-by-Step.md

@ -1,6 +1,6 @@
# Migrating from IdentityServer to OpenIddict Step by Step Guide
This guide provides layer-by-layer guidance for migrating your existing application to [OpenIddict](https://github.com/openiddict/openiddict-core) from IdentityServer. ABP startup templates use `OpenIddict` OpenId provider from v6.0.0-rc1 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is only available with ABP v6.0, you will need to update your existing application in order to apply OpenIddict changes.
This guide provides layer-by-layer guidance for migrating your existing application to [OpenIddict](https://github.com/openiddict/openiddict-core) from IdentityServer. ABP startup templates use `OpenIddict` OpenId provider from v6.0.0 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is only available with ABP v6.0, you will need to update your existing application in order to apply OpenIddict changes.
## History
We are not removing Identity Server packages and we will continue to release new versions of IdentityServer-related NuGet/NPM packages. That means you won't have an issue while upgrading to v6.0 when the stable version releases. We will continue to fix bugs in our packages for a while. ABP 7.0 will be based on .NET 7. If Identity Server continues to work with .NET 7, we will also continue to ship NuGet packages for our IDS integration.

1
docs/en/Module-Development-Basics.md

@ -20,7 +20,6 @@ public class BlogModule : AbpModule
{
}
````
### Configuring Dependency Injection & Other Modules

4
docs/en/Module-Entity-Extensions.md

@ -312,12 +312,14 @@ An enum properties is shown as combobox (select) in the create/edit forms:
Enum member name is shown on the table and forms by default. If you want to localize it, just create a new entry on your [localization](https://docs.abp.io/en/abp/latest/Localization) file:
````json
"UserType.SuperUser": "Super user"
"Enum:UserType.0": "Super user"
````
One of the following names can be used as the localization key:
* `Enum:UserType.0`
* `Enum:UserType.SuperUser`
* `UserType.0`
* `UserType.SuperUser`
* `SuperUser`

2
docs/en/Modules/Account.md

@ -6,7 +6,7 @@ This module is based on [Microsoft's Identity library](https://docs.microsoft.co
## 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.
This module comes as pre-installed (as NuGet/NPM packages). 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

2
docs/en/Modules/Audit-Logging.md

@ -6,7 +6,7 @@ The Audit Logging Module basically implements the `IAuditingStore` to save the a
## 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.
This module comes as pre-installed (as NuGet/NPM packages). 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

2
docs/en/Modules/Background-Jobs.md

@ -6,7 +6,7 @@ The Background Jobs module implements the `IBackgroundJobStore` interface and ma
## 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.
This module comes as pre-installed (as NuGet/NPM packages). 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

6
docs/en/Modules/Cms-Kit/Blogging.md

@ -2,6 +2,12 @@
The blogging feature provides the necessary UI to manage and render blogs and blog posts.
## Enabling the Blogging Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## User Interface
### Menu Items

25
docs/en/Modules/Cms-Kit/Comments.md

@ -1,10 +1,16 @@
# CMS Kit: Comments
CMS kit provides a **comment** system to add comments feature to any kind of resource, like blog posts, products, etc.
CMS kit provides a **comment** system to add the comment feature to any kind of resource, like blog posts, products, etc.
## Enabling the Comment Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## Options
The comment system provides a mechanism to group comment definitions by entity types. For example, if you want to use comment system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product`, and add comments under these entity types.
The comment system provides a mechanism to group comment definitions by entity types. For example, if you want to use the comment system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product`, and add comments under these entity types.
`CmsKitCommentOptions` can be configured in the domain layer, in the `ConfigureServices` method of your [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Example:
@ -12,14 +18,16 @@ The comment system provides a mechanism to group comment definitions by entity t
Configure<CmsKitCommentOptions>(options =>
{
options.EntityTypes.Add(new CommentEntityTypeDefinition("Product"));
options.IsRecaptchaEnabled = true; //false by default
});
```
> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above.
> If you're using the [Blogging Feature](Blogging.md), the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above.
`CmsKitCommentOptions` properties:
- `EntityTypes`: List of defined entity types(`CmsKitCommentOptions`) in the comment system.
- `IsRecaptchaEnabled`: This flag enables or disables the reCaptcha for the comment system. You can set it as **true** if you want to use reCaptcha in your comment system.
`CommentEntityTypeDefinition` properties:
@ -33,11 +41,12 @@ The comment system provides a commenting [widget](../../UI/AspNetCore/Widgets.md
@await Component.InvokeAsync(typeof(CommentingViewComponent), new
{
entityType = "Product",
entityId = "..."
entityId = "...",
referralLinks = new [] {"nofollow"}
})
```
`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here.
`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. `referralLinks` is an optional parameter. You can use this parameter to add values (such as "nofollow", "noreferrer", or any other values) to the [rel attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel) of links.
## User Interface
@ -77,7 +86,7 @@ A comment represents a written comment from a user.
This module follows the [Repository Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories) guide.
Following custom repositories are defined for this feature:
The following custom repositories are defined for this feature:
- `ICommentRepository`
@ -93,8 +102,8 @@ This module follows the [Domain Services Best Practices & Conventions](https://d
#### Application services
- `CommentAdminAppService` (implements `ICommentAdminAppService`): Implements the use cases of comment management system, like listing or removing comments etc.
- `CommentPublicAppService` (implements `ICommentPublicAppService`): Implements the use cases of comment management on the public websites, like listing comments, adding comments etc.
- `CommentAdminAppService` (implements `ICommentAdminAppService`): Implements the use cases of the comment management system, like listing or removing comments etc.
- `CommentPublicAppService` (implements `ICommentPublicAppService`): Implements the use cases of the comment management on the public websites, like listing comments, adding comments etc.
### Database providers

6
docs/en/Modules/Cms-Kit/Global-Resources.md

@ -2,6 +2,12 @@
CMS Kit Global Resources system allows to add global styles and scripts dynamically.
## Enabling the Global Resources Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## The User Interface
### Menu items

7
docs/en/Modules/Cms-Kit/Index.md

@ -16,11 +16,12 @@ The following features are currently available:
* Provides a [**global resources**](Global-Resources.md) system to add global styles and scripts dynamically.
* Provides a [**Dynamic Widget**](Dynamic-Widget.md) system to create dynamic widgets for page and blog posts.
Click to a feature to understand and learn how to use it.
> You can click on the any feature links above to understand and learn how to use it.
All features are individually usable. If you disable a feature, it completely disappears from your application, even from the database tables, by the help of the [Global Features](../../Global-Features.md) system.
All features are individually usable. If you disable a feature, it completely disappears from your application, even from the database tables, with the help of the [Global Features](../../Global-Features.md) system.
## Pre Requirements
- This module depends on [BlobStoring](../../Blob-Storing.md) module for keeping media content.
> Make sure `BlobStoring` module is installed and at least one provider is configured properly. For more information, check the [documentation](../../Blob-Storing.md).
@ -62,7 +63,7 @@ GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit =>
This module follows the [module development best practices guide](https://docs.abp.io/en/abp/latest/Best-Practices/Index) and consists of several NuGet and NPM packages. See the guide if you want to understand the packages and relations between them.
CMS kit packages are designed for various usage scenarios. If you check the [CMS kit packages](https://www.nuget.org/packages?q=Volo.CmsKit), you will see that some packages have `Admin` and `Public` suffixes. The reason is that the module has two application layers, considering they might be used in different type of applications. These application layers uses a single domain layer.
CMS kit packages are designed for various usage scenarios. If you check the [CMS kit packages](https://www.nuget.org/packages?q=Volo.CmsKit), you will see that some packages have `Admin` and `Public` suffixes. The reason is that the module has two application layers, considering they might be used in different type of applications. These application layers uses a single domain layer:
- `Volo.CmsKit.Admin.*` packages contain the functionalities required by admin (back office) applications.
- `Volo.CmsKit.Public.*` packages contain the functionalities used in public websites where users read blog posts or leave comments.

18
docs/en/Modules/Cms-Kit/Menus.md

@ -1,7 +1,13 @@
# CMS Kit: Pages
# CMS Kit: Menus
CMS Kit Menu system allows to manage public menus dynamically.
## Enabling the Menu Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## The User Interface
### Menu items
@ -20,22 +26,18 @@ Menus page is used to manage dynamic public menus in the system.
![cms-kit-menus-page](../../images/cmskit-module-menus-page.png)
Created menus will be visible on public site.
The created menu items will be visible on the public-web side, as shown below:
![cms-kit-public-menus](../../images//cmskit-module-menus-public.png)
# Internals
## Internals
## Domain Layer
### Domain Layer
#### Aggregates
This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide.
##### Menus
- `MenuItem` (aggregate root): A Menu Item presents a single node at menu tree.
#### Repositories

8
docs/en/Modules/Cms-Kit/Pages.md

@ -2,6 +2,12 @@
CMS Kit Page system allows you to create dynamic pages by specifying URLs, which is the fundamental feature of a CMS.
## Enabling the Pages Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## The User Interface
### Menu items
@ -24,5 +30,5 @@ You can create or edit an existing page on this page.
![pages-edit](../../images/cmskit-module-pages-edit.png)
When you create a page, you can access the created page via `/pages/{slug}` URL.
When you create a page, you can access the created page via `/{slug}` URL.

8
docs/en/Modules/Cms-Kit/Ratings.md

@ -4,6 +4,12 @@ CMS kit provides a **rating** system to to add ratings feature to any kind of re
![ratings](../../images/cmskit-module-ratings.png)
## Enabling the Rating Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## Options
The rating system provides a mechanism to group ratings by entity types. For example, if you want to use the rating system for products, you need to define an entity type named `Product` and then add ratings under the defined entity type.
@ -17,7 +23,7 @@ Configure<CmsKitRatingOptions>(options =>
});
```
> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above.
> If you're using the [Blogging Feature](Blogging.md), the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above.
`CmsKitRatingOptions` properties:

8
docs/en/Modules/Cms-Kit/Reactions.md

@ -8,6 +8,12 @@ Reaction component allows users to react to your content via pre-defined icons/e
You can also customize the reaction icons shown in the reaction component.
## Enabling the Reaction Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## Options
Reaction system provides a mechanism to group reactions by entity types. For example, if you want to use the reaction system for products, you need to define an entity type named `Product`, and then add reactions under the defined entity type.
@ -32,7 +38,7 @@ Configure<CmsKitReactionOptions>(options =>
});
```
> If you're using the comment or blog features, the ABP framework defines predefined reactions for these features automatically.
> If you're using the [Comment](Comments.md) or [Blogging](Blogging.md) features, the ABP framework defines predefined reactions for these features automatically.
`CmsKitReactionOptions` properties:

8
docs/en/Modules/Cms-Kit/Tags.md

@ -2,6 +2,12 @@
CMS kit provides a **tag** system to tag any kind of resources, like a blog post.
## Enabling the Tag Management Feature
By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime.
> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time.
## Options
The tag system provides a mechanism to group tags by entity types. For example, if you want to use the tag system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product` and add tags under these entity types.
@ -17,7 +23,7 @@ Configure<CmsKitTagOptions>(options =>
});
```
> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically.
> If you're using the [Blogging Feature](Blogging.md), the ABP framework defines an entity type for the blog feature automatically.
`CmsKitTagOptions` properties:

575
docs/en/Modules/Database-Tables.md

@ -0,0 +1,575 @@
# Database Tables
This documentation describes all database tables and their purposes. You can read this documentation to get general knowledge of the database tables that come from each module.
## [Audit Logging Module](Audit-Logging.md)
### AbpAuditLogs
This table stores information about the audit logs in the application. Each record represents an audit log and tracks the actions performed in the application.
### AbpAuditLogActions
This table stores information about the actions performed in the application, which are logged for auditing purposes.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpAuditLogs](#abpauditlogs) | Id | Links each action to a specific audit log. |
### AbpEntityChanges
This table stores information about entity changes in the application, which are logged for auditing purposes.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpAuditLogs](#abpauditlogs) | Id | Links each entity change to a specific audit log. |
### AbpEntityPropertyChanges
This table stores information about property changes to entities in the application, which are logged for auditing purposes.
## Uses
| Table | Column | Description |
| --- | --- | --- |
| [AbpEntityChanges](#abpentitychanges) | Id | Links each property change to a specific entity change. |
## [Background Jobs Module](Background-Jobs.md)
### AbpBackgroundJobs
This table stores information about the background jobs in the application and facilitates their efficient management and tracking. Each entry in the table contains details of a background job, including the job name, arguments, try count, next try time, last try time, abandoned status, and priority.
## [Tenant Management Module](Tenant-Management.md)
### AbpTenants
This table stores information about the tenants. Each record represents a tenant and contains information about the tenant, such as name and other details.
### AbpTenantConnectionStrings
This table stores information about the tenant database connection strings. When you define a connection string for a tenant, a new record will be added to this table. You can query this database to get connection strings by tenants.
## Uses
| Table | Column | Description |
| --- | --- | --- |
| [AbpTenants](#abptenants) | Id | The `Id` column in the `AbpTenants` table is used to associate the tenant connection string with the corresponding tenant. |
## Blogging Module
### BlgUsers
This table stores information about the blog users. When a new identity user is created, a new record will be added to this table.
### BlgBlogs
This table serves to store blog information and semantically separates the posts of each blog.
### BlgPosts
This table stores information about the blog posts. You can query this table to get blog posts by blogs.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [BlgBlogs](#blgblogs) | Id | To associate the blog post with the corresponding blog. |
### BlgComments
This table stores information about the comments made on blog posts. You can query this table to get comments by posts.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [BlgPosts](#blgposts) | Id | Links the comment to the corresponding blog post. |
| [BlgComments](#blgcomments) | Id | Links the comment to the parent comment. |
### BlgTags
This table stores information about the tags. When a new tag is used, a new record will be added to this table. You can query this table to get tags by blogs.
### BlgPostTags
This table is used to associate tags with blog posts in order to categorize and organize the content. You can query this table to get post tags by posts.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [BlgTags](#blgtags) | Id | Links the post tag to the corresponding tag. |
| [BlgPosts](#blgposts) | Id | Links the post tag to the corresponding blog post. |
## [CMS Kit Module](Cms-Kit/Index.md)
### CmsUsers
This table stores information about the CMS Kit module users. When a new identity user is created, a new record will be added to this table.
### CmsBlogs
This table serves to store blog information and semantically separates the posts of each blog.
### CmsBlogPosts
This table stores information about the blog posts. You can query this table to get blog posts by blogs.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [CmsUsers](#cmsusers) | Id | Links the blog post to the corresponding author. |
### CmsBlogFeatures
This table stores information about the blog features. You can query this table to get blog features by blogs.
### CmsComments
This table is utilized by the [CMS Kit Comment system](Cms-Kit/Comments.md) to store comments made on the blog posts. You can query this table to get comments by posts.
### CmsTags
This table stores information about the tags. When a new tag is used, a new record will be added to this table. You can query this table to get tags by blogs.
### CmsEntityTags
This table is utilized by the [Tag Management system](Cms-Kit/Tags.md) to store tags and their relationship with various entities, thus enabling efficient categorization and organization of content. You can query this table to get entity tags by entities.
### CmsGlobalResources
This table is a database table for the [CMS Kit Global Resources system](Cms-Kit/Global-Resources.md), allowing dynamic addition of global styles and scripts.
### CmsMediaDescriptors
This table is utilized by the CMS kit module to manage media files by using the [BlobStoring](../Blob-Storing.md) module.
### CmsMenuItems
This table is used by the [CMS Kit Menu system](Cms-Kit/Menus.md) to manage and store information about dynamic public menus, including details such as menu item display names, URLs, and hierarchical relationships.
### CmsPages
This table is utilized by the [CMS Kit Page system](Cms-Kit/Pages.md) to store dynamic pages within the application, including information such as page URLs, titles, and content.
### CmsRatings
This table is utilized by the [CMS Kit Rating system](Cms-Kit/Ratings.md) to store ratings made on blog posts. You can query this table to get ratings by posts.
### CmsUserReactions
This table is utilized by the [CMS Kit Reaction system](Cms-Kit/Reactions.md) to store reactions made on blog posts. You can query this table to get reactions by posts.
## [Docs Module](Docs.md)
### DocsProjects
This table stores project information to categorize documents according to different projects.
### DocsDocuments
This table retrieves the document if it's not found in the cache. The documentation is being updated when the content is retrieved from the database.
### DocsDocumentContributors
This table stores information about the contributors of the documents. You can query this table to get document contributors by documents.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [DocsDocuments](#docsdocuments) | Id | Links the document contributor to the corresponding document. |
## [Feature Management Module](Feature-Management.md)
### AbpFeatureGroups
This table stores information about the feature groups in the application. For example, you can group all the features in the [`AbpFeatures`](#abpfeatures) table related to the `Identity` module under the `Identity` group.
### AbpFeatures
This table stores information about the features in the application. You can use the `Name` column to link each feature with its corresponding feature value in the [`AbpFeatureValues`](#abpfeaturevalues) table, so that you can easily manage and organize the features.
### AbpFeatureValues
This table stores the values of the features for different providers. You can use the `Name` column to link each feature value with its corresponding feature in the [`AbpFeatures`](#abpfeatures) table, so that you can easily manage and organize the features.
## [Identity Module](Identity.md)
### AbpUsers
This table stores information about the identity users in the application.
### AbpRoles
This table stores information about the roles in the application. Roles are used to manage and control access to different parts of the application by assigning permissions and claims to roles and then assigning those roles to users. This table is important for managing and organizing the roles in the application, and for defining the access rights of the users.
### AbpClaimTypes
This table stores information about the claim types used in the application. You can use the `Name`, `Regex` columns to filter the claim types by name, and regex pattern respectively, so that you can easily manage and track the claim types in the application.
### AbpLinkUsers
This table is useful for linking multiple user accounts across different tenants or applications to a single user, allowing them to easily switch between their accounts.
### AbpUserClaims
This table can manage user-based access control by allowing to assign claims to users, which describes the access rights of the individual user.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpUsers](#abpusers) | Id | Links the user claim to the corresponding user. |
### AbpUserLogins
This table can store information about the user's external logins such as login with Facebook, Google, etc. and it can also be used to track the login history of users.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpUsers](#abpusers) | Id | Links the user login to the corresponding user. |
### AbpUserRoles
This table can manage user-based access control by allowing to assign roles to users, which describe the access rights of the individual user.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpUsers](#abpusers) | Id | Links the user role to the corresponding user. |
| [AbpRoles](#abproles) | Id | Links the user role to the corresponding role. |
### AbpUserTokens
This table can store information about user's refresh tokens, access tokens and other tokens used in the application. It can also be used to invalidate or revoke user tokens.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpUsers](#abpusers) | Id | Links the user token to the corresponding user. |
### AbpOrganizationUnits
This table is useful for creating and managing a hierarchical structure of the organization, allowing to group users and assign roles based on the organization structure. You can use the `Code`, `ParentId` columns to filter the organization units by code and parent id respectively, so that you can easily manage and track the organization units in the application.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpOrganizationUnits](#abporganizationunits) | ParentId | Links the organization unit to its parent organization unit. |
### AbpOrganizationUnitRoles
This table is useful for managing role-based access control at the level of organization units, allowing to assign different roles to different parts of the organization structure. You can use the `OrganizationUnitId`, `RoleId` columns to filter the roles by organization unit id and role id respectively, so that you can easily manage and track the roles assigned to organization units in the application.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpOrganizationUnits](#abporganizationunits) | Id | Links the organization unit role to the corresponding organization unit. |
| [AbpRoles](#abproles) | Id | Links the organization unit role to the corresponding role. |
### AbpUserOrganizationUnits
This table stores information about the organization units assigned to the users in the application. This table can manage user-organization unit relationships, and to group users based on the organization structure.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpUsers](#abpusers) | Id | Links the user organization unit to the corresponding user. |
| [AbpOrganizationUnits](#abporganizationunits) | Id | Links the user organization unit to the corresponding organization unit. |
### AbpRoleClaims
This table is useful for managing role-based access control by allowing to assign claims to roles, which describes the access rights of the users that belong to that role.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpRoles](#abproles) | Id | Links the role claim to the corresponding role. |
### AbpSecurityLogs
This table logs important operations and changes related to user accounts, allowing users to save the security logs for future reference.
## [Permission Management](Permission-Management.md)
### AbpPermissionGroups
This table is important for managing and organizing the permissions in the application, by grouping them into logical categories.
### AbpPermissions
This table is important for managing and controlling access to different parts of the application and for defining the granular permissions that make up the larger permissions or roles.
### AbpPermissionGrants
The table stores and manage the permissions in the application and to keep track of permissions that are granted, to whom and when. Columns such as `Name`, `ProviderName`, `ProviderKey`, `TenantId` can be used to filter the granted permissions by name, provider name, provider key, and tenant id respectively, so that you can easily manage and track the granted permissions in the application.
## [Setting Management](Setting-Management.md)
### AbpSettings
This table stores key-value pairs of settings for the application, and it allows dynamic configuration of the application without the need for recompilation.
## [OpenIddict](OpenIddict.md)
### OpenIddictApplications
This table can store information about the OpenID Connect applications, including the client id, client secret, redirect URI, and other relevant information. It can also be used to authenticate and authorize clients using OpenID Connect protocol.
### OpenIddictAuthorizations
This table stores the OpenID Connect authorization data in the application. It can also be used to manage and validate the authorization grants issued to clients and users.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [OpenIddictApplications](#openiddictapplications) | Id | Links the authorization to the corresponding application. |
### OpenIddictTokens
This table can store information about the OpenID Connect tokens, including the token payload, expiration, type, and other relevant information. It can also be used to manage and validate the tokens issued to clients and users, such as access tokens and refresh tokens, and to control access to protected resources.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [OpenIddictApplications](#openiddictapplications) | Id | Links the token to the corresponding application. |
| [OpenIddictAuthorizations](#openiddictauthorizations) | Id | Links the token to the corresponding authorization. |
### OpenIddictScopes
This table can store information about the OpenID Connect scopes, including the name and description of the scope. It can also be used to define the permissions or access rights associated with the scopes, which are then used to control access to protected resources.
## [IdentityServer](IdentityServer.md)
### IdentityServerApiResources
This table can store information about the API resources, including the resource name, display name, description, and other relevant information. It can also be used to define the scopes, claims, and properties associated with the API resources, which are then used to control access to protected resources.
### IdentityServerIdentityResources
This table can store information about the identity resources, including the name, display name, description, and enabled status.
### IdentityServerClients
This table can store information about the clients, including the client id, client name, client URI and other relevant information. It can also be used to define the scopes, claims, and properties associated with the clients, which are then used to control access to protected resources.
### IdentityServerApiScopes
This table can store information about the API scopes, including the scope name, display name, description, and other relevant information. It can also be used to define the claims and properties associated with the API scopes, which are then used to control access to protected resources.
### IdentityServerApiResourceClaims
This table can store information about the claims of an API resource, including the claim type and API resource id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiResources](#identityserverapiresources) | Id | Links the claim to the corresponding API resource. |
### IdentityServerIdentityResourceClaims
This table can store information about the claims of an identity resource, including the claim type and identity resource id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerIdentityResources](#identityserveridentityresources) | Id | Links the claim to the corresponding identity resource. |
### IdentityServerClientClaims
This table can store information about the claims of a client, including the claim type, claim value and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the claim to the corresponding client. |
### IdentityServerApiScopeClaims
This table can store information about the claims of an API scope, including the claim type and API scope id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiScopes](#identityserverapiscopes) | Id | Links the claim to the corresponding API scope. |
### IdentityServerApiResourceProperties
This table can store information about properties, including the property key and value, and the associated API resource. These properties can store additional metadata or configuration information related to the API resources.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiResources](#identityserverapiresources) | Id | Links the property to the corresponding API resource. |
### IdentityServerIdentityResourceProperties
This table can store information about properties, including the property key and value, and the associated identity resource. These properties can store additional metadata or configuration information related to the identity resources.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerIdentityResources](#identityserveridentityresources) | Id | Links the property to the corresponding identity resource. |
### IdentityServerClientProperties
This table can be store information about the properties of a client, including the key, value and client id. These properties can store additional metadata or configuration information related to the clients.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the property to the corresponding client. |
### IdentityServerApiScopeProperties
This table can store information about the properties of an API scope, including the key, value and API scope id. These properties can store additional metadata or configuration information related to the API scopes.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiScopes](#identityserverapiscopes) | Id | Links the property to the corresponding API scope. |
### IdentityServerApiResourceScopes
This table can store information about the scopes of an API resource, including the scope name and API resource id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiResources](#identityserverapiresources) | Id | Links the scope to the corresponding API resource. |
### IdentityServerClientScopes
This table can store information about the scopes of a client, including the scope and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the scope to the corresponding client. |
### IdentityServerApiResourceSecrets
This table can store information about the secrets of an API resource, including the secret value, expiration date, and API resource id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerApiResources](#identityserverapiresources) | Id | Links the secret to the corresponding API resource. |
### IdentityServerClientSecrets
This table can store information about the secrets of a client, including the secret value, expiration date, and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the secret to the corresponding client. |
### IdentityServerClientCorsOrigins
This table can store information about the CORS origins of a client, including the origin and client id. It can also be used to manage and validate the CORS origins of a client.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the CORS origin to the corresponding client. |
### IdentityServerClientGrantTypes
This table can store information about the grant types of a client, including the grant type and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the grant type to the corresponding client. |
### IdentityServerClientIdPRestrictions
This table can store information about the identity provider restrictions of a client, including the identity provider and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the identity provider restriction to the corresponding client. |
### IdentityServerClientPostLogoutRedirectUris
This table can store information about the post logout redirect URIs of a client, including the post logout redirect URI and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the post logout redirect URI to the corresponding client. |
### IdentityServerClientRedirectUris
This table can store information about the redirect URIs of a client, including the redirect URI and client id.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [IdentityServerClients](#identityserverclients) | Id | Links the redirect URI to the corresponding client. |
### IdentityServerDeviceFlowCodes
This table can store information about the device flow codes, including the user code, device code, subject id, client id, creation time, expiration, data and session id.
### IdentityServerPersistedGrants
This table can store information about the persisted grants, including the key, type, subject id, client id, creation time, expiration, and data.
## Others
### AbpBlobContainers
This table is important for providing a better user experience by allowing the application to support multiple containers and providing BLOB-specific features.
### AbpBlobs
This table stores the binary data of BLOBs (binary large objects) in the application. Each BLOB is related to a container in the [AbpBlobContainers](#abpblobcontainers) table, where the container name, tenant id and other properties of the container can be found.
#### Foreign Keys
| Table | Column | Description |
| --- | --- | --- |
| [AbpBlobContainers](#abpblobcontainers) | Id | Links the BLOB to the corresponding container. |
### AbpLocalizationResources
This table stores the localization resources for the application. This table is important for providing a better user experience by allowing the application to support multiple resources and providing localized text and other localization-specific features.
### AbpLocalizationTexts
The table contains the resource name, culture name, and a json encoded value which holds the key-value pair of localization text. It allows for efficient storage and management of localization texts and allows for easy update or addition of new translations for specific resources and cultures.

42
docs/en/Modules/Docs.md

@ -28,9 +28,11 @@ If you do not have an existing ABP project, this step shows you how to create a
It is recommended to use ABP CLI to create new projects. Use the following command:
`abp new Acme.MyProject`
```bash
abp new Acme.MyProject
```
You can also navigate to https://abp.io/get-started. Enter your project name as `Acme.MyProject`, other use default options.
You can also generate a CLI command from [get started page](https://abp.io/get-started). Enter your project name as `Acme.MyProject`, other use default options.
Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider.
@ -66,7 +68,9 @@ Docs module packages are hosted on NuGet. There are 4 packages that needs be to
It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command:
`abp add-module Volo.Docs`
```bash
abp add-module Volo.Docs
```
#### 3.2- Manually install
@ -74,19 +78,27 @@ Or you can also manually install nuget package to each project:
* Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project.
`Install-Package Volo.Docs.Domain`
```bash
Install-Package Volo.Docs.Domain
```
* Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project.
`Install-Package Volo.Docs.EntityFrameworkCore`
```bash
Install-Package Volo.Docs.EntityFrameworkCore
```
* Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project.
`Install-Package Volo.Docs.Application`
```bash
Install-Package Volo.Docs.Application
```
* Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project.
`Install-Package Volo.Docs.Web`
```bash
Install-Package Volo.Docs.Web
```
##### 3.2.1- Adding Module Dependencies
@ -258,7 +270,7 @@ If you choose Entity Framework as your database provider, you need to configure
The default route for Docs module is;
```
```txt
/Documents
```
@ -307,7 +319,7 @@ The new menu item for Docs Module is added to the menu. Run your web application
You will see a warning says;
```
```txt
There are no projects yet!
```
@ -487,14 +499,14 @@ For example [Getting-Started.md](https://github.com/abpio/abp-commercial-docs/bl
```
.....
​````json
​```json
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
​````
​```
........
```
@ -530,9 +542,9 @@ For example:
You can also use variables in a text, adding **_Value** postfix to its key:
````
```txt
This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider.
````
```
Also, **Document_Language_Code** and **Document_Version** keys are pre-defined if you want to get the language code or the version of the current document (This may be useful for creating links that redirects to another documentation system in another domain).
@ -603,7 +615,7 @@ Finally a new Docs Module is added to your project which is feeded with GitHub.
The Docs module supports full-text search using Elastic Search. It is not enabled by default. You can configure `DocsElasticSearchOptions` to enable it.
```
```csharp
Configure<DocsElasticSearchOptions>(options =>
{
options.Enable = true;
@ -616,7 +628,7 @@ The `Index` is automatically created after the application starts if the `Index`
`DefaultElasticClientProvider` is responsible for creating `IElasticClient`. By default, it reads Elastic Search's `Url` from `IConfiguration`.
If your `IElasticClient` needs additional configuration, please use override `IElasticClientProvider` service and replace it in the [dependency injection](../Dependency-Injection.md) system.
```
```json
{
"ElasticSearch": {
"Url": "http://localhost:9200"

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

Loading…
Cancel
Save