Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into angular9

pull/2883/head
mehmet-erim 6 years ago
parent
commit
c1913b8e5c
  1. 24
      .github/ISSUE_TEMPLATE
  2. 30
      .github/workflows/build-and-test.yml
  3. 11
      .gitignore
  4. 4
      README.md
  5. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  6. 13
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ru.json
  7. 73
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  8. 90
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ru.json
  9. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  10. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  11. 9
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/pt-BR.json
  12. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  13. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json
  14. 9
      build/common.ps1
  15. 7
      common.DotSettings
  16. 2
      common.props
  17. 3
      docs/cs/AspNetCore/Widgets.md
  18. 6
      docs/cs/Contribution/Index.md
  19. 40
      docs/cs/Contribution/Localization-Text-Files.md
  20. 4
      docs/cs/Entity-Framework-Core-PostgreSQL.md
  21. 4
      docs/cs/Getting-Started-AspNetCore-Application.md
  22. 2
      docs/cs/Getting-Started-Console-Application.md
  23. 16
      docs/en/API/Dynamic-CSharp-API-Clients.md
  24. 103
      docs/en/Application-Services.md
  25. 20
      docs/en/AspNet-Boilerplate-Migration-Guide.md
  26. 30
      docs/en/Authorization.md
  27. 3
      docs/en/AutoMapper-Integration.md
  28. 43
      docs/en/Background-Jobs-Hangfire.md
  29. 54
      docs/en/Background-Jobs-Quartz.md
  30. 6
      docs/en/Background-Jobs.md
  31. 84
      docs/en/Background-Workers-Quartz.md
  32. 139
      docs/en/Background-Workers.md
  33. 26
      docs/en/Best-Practices/Application-Services.md
  34. 1
      docs/en/Best-Practices/Data-Transfer-Objects.md
  35. 9
      docs/en/Best-Practices/Entity-Framework-Core-Integration.md
  36. 4
      docs/en/Best-Practices/MongoDB-Integration.md
  37. 3
      docs/en/Blob-Storing.md
  38. 142
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/Post.md
  39. BIN
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png
  40. BIN
      docs/en/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png
  41. 247
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/Post.md
  42. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png
  43. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png
  44. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png
  45. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png
  46. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png
  47. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png
  48. BIN
      docs/en/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png
  49. 214
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md
  50. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png
  51. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png
  52. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png
  53. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif
  54. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png
  55. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png
  56. 296
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/Post.md
  57. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png
  58. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png
  59. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png
  60. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png
  61. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png
  62. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/suite.png
  63. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png
  64. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png
  65. 169
      docs/en/CLI.md
  66. 19
      docs/en/Contribution/Index.md
  67. 31
      docs/en/Contribution/Localization-Text-Files.md
  68. 38
      docs/en/Customizing-Application-Modules-Extending-Entities.md
  69. 6
      docs/en/Customizing-Application-Modules-Guide.md
  70. 156
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  71. 5
      docs/en/Customizing-Application-Modules-Overriding-User-Interface.md
  72. 21
      docs/en/Dapper.md
  73. 3
      docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
  74. 11
      docs/en/Entities.md
  75. 111
      docs/en/Entity-Framework-Core-Migrations.md
  76. 17
      docs/en/Entity-Framework-Core-MySQL.md
  77. 62
      docs/en/Entity-Framework-Core-Oracle.md
  78. 17
      docs/en/Entity-Framework-Core-Other-DBMS.md
  79. 4
      docs/en/Entity-Framework-Core-PostgreSQL.md
  80. 112
      docs/en/Entity-Framework-Core.md
  81. 52
      docs/en/Exception-Handling.md
  82. 128
      docs/en/Getting-Started-Angular-Template.md
  83. 4
      docs/en/Getting-Started-AspNetCore-Application.md
  84. 106
      docs/en/Getting-Started-AspNetCore-MVC-Template.md
  85. 2
      docs/en/Getting-Started-Console-Application.md
  86. 8
      docs/en/Getting-Started-With-Startup-Templates.md
  87. 413
      docs/en/Getting-Started.md
  88. 212
      docs/en/How-To/Azure-Active-Directory-Authentication-MVC.md
  89. 113
      docs/en/How-To/Customize-Login-Page-MVC.md
  90. 101
      docs/en/How-To/Customize-SignIn-Manager.md
  91. 9
      docs/en/How-To/Index.md
  92. 4
      docs/en/Index.md
  93. 52
      docs/en/Localization.md
  94. 4
      docs/en/Microservice-Architecture.md
  95. 2
      docs/en/Modules/Docs.md
  96. 1
      docs/en/Modules/Index.md
  97. 80
      docs/en/Modules/Virtual-File-Explorer.md
  98. 26
      docs/en/Multi-Tenancy.md
  99. 15
      docs/en/Nightly-Builds.md
  100. 412
      docs/en/Object-Extensions.md

24
.github/ISSUE_TEMPLATE

@ -0,0 +1,24 @@
### Documentation
Please check the official documentation before asking questions: https://docs.abp.io
### GitHub Issues
GitHub issues are for bug reports, feature requests and other discussions about the framework.
If you're creating a bug/problem report, please include followings:
* Your **ABP Framework version**.
* Your **User Interface** type (Angular/MVC/React... etc.) if the issue is related to a specific UI
* Exception message and **stack trace** if available (check the logs).
* Steps needed to **reproduce** the problem.
Please **write in English**.
### Stack Overflow
Please use Stack Overflow for your questions about using the framework, templates and samples:
https://stackoverflow.com/questions/tagged/abp
Use **abp** tag in your questions.

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

@ -0,0 +1,30 @@
name: "build and test"
on:
pull_request:
paths:
- "framework/**"
- "modules/**"
- "templates/**"
push:
paths:
- "framework/**"
- "modules/**"
- "templates/**"
jobs:
build-test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 3.1.100
- name: Build All
run: .\build-all.ps1
working-directory: .\build
shell: powershell
- name: Test All
run: .\test-all.ps1
working-directory: .\build
shell: powershell

11
.gitignore

@ -293,3 +293,14 @@ samples/MicroserviceDemo/applications/ConsoleClientDemo/Logs/logs.txt
modules/docs/app/Volo.DocsTestApp/Logs/logs.txt modules/docs/app/Volo.DocsTestApp/Logs/logs.txt
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt
samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt
/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package-lock.json
/npm/packs/bootstrap/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json
modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json
/templates/app/angular/package-lock.json
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json

4
README.md

@ -1,9 +1,9 @@
# ABP # ABP
[![Build Status](http://vjenkins.dynu.net:5480/job/abp/badge/icon)](http://ci.volosoft.com:5480/blue/organizations/jenkins/abp/activity) ![build and test](https://github.com/abpframework/abp/workflows/build%20and%20test/badge.svg)
[![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) [![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
[![MyGet (with prereleases)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds) [![MyGet (with prereleases)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds)
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework. See [the announcement](https://blog.abp.io/abp/Abp-vNext-Announcement). This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework. See [the announcement](https://blog.abp.io/abp/Abp-vNext-Announcement).

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

@ -1,7 +1,7 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"Account": "Account", "Account": "ABP Account - Login & Register | ABP.IO",
"Welcome": "Welcome", "Welcome": "Welcome",
"UseOneOfTheFollowingLinksToContinue": "Use one of the following links to continue", "UseOneOfTheFollowingLinksToContinue": "Use one of the following links to continue",
"FrameworkHomePage": "Framework home page", "FrameworkHomePage": "Framework home page",

13
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ru.json

@ -0,0 +1,13 @@
{
"culture": "ru",
"texts": {
"Account": "Аккаунт",
"Welcome": "Добро пожаловать",
"UseOneOfTheFollowingLinksToContinue": "Для продолжения используйте одну из следующих ссылок",
"FrameworkHomePage": "Главная страница фреймворка",
"FrameworkDocumentation": "Документация фреймворка",
"OfficialBlog": "Официальный блог",
"CommercialHomePage": "Главная страница коммерческой версии",
"CommercialSupportWebSite": "Сайт коммерческой поддержки"
}
}

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

@ -3,6 +3,12 @@
"texts": { "texts": {
"Permission:Organizations": "Organizations", "Permission:Organizations": "Organizations",
"Permission:Manage": "Manage Organizations", "Permission:Manage": "Manage Organizations",
"Permission:DiscountRequests": "Discount Requests",
"Permission:DiscountManage": "Manage Discount Requests",
"Permission:Disable": "Disable",
"Permission:Enable": "Enable",
"Permission:EnableSendEmail": "Enable Send Email",
"Permission:SendEmail": "Send Email",
"Permission:NpmPackages": "NPM Packages", "Permission:NpmPackages": "NPM Packages",
"Permission:NugetPackages": "Nuget Packages", "Permission:NugetPackages": "Nuget Packages",
"Permission:Maintenance": "Maintenance", "Permission:Maintenance": "Maintenance",
@ -13,8 +19,13 @@
"Permission:Edit": "Edit", "Permission:Edit": "Edit",
"Permission:Delete": "Delete", "Permission:Delete": "Delete",
"Permission:Create": "Create", "Permission:Create": "Create",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Permission:Accounting:Invoice": "Invoice",
"Menu:Organizations": "Organizations", "Menu:Organizations": "Organizations",
"Menu:Accounting": "Accounting",
"Menu:Packages": "Packages", "Menu:Packages": "Packages",
"Menu:DiscountRequests": "Discount Requests",
"NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?", "NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?",
"NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?", "NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?",
"ModuleDeletionWarningMessage": "This Module will be deleted. Do you confirm that?", "ModuleDeletionWarningMessage": "This Module will be deleted. Do you confirm that?",
@ -24,6 +35,7 @@
"NameFilter": "Name", "NameFilter": "Name",
"CreationTime": "Creation time", "CreationTime": "Creation time",
"IsPro": "Is pro", "IsPro": "Is pro",
"ShowOnModuleList": "Show on module list",
"EfCoreConfigureMethodName": "Configure method name", "EfCoreConfigureMethodName": "Configure method name",
"IsProFilter": "Is pro", "IsProFilter": "Is pro",
"ApplicationType": "Application type", "ApplicationType": "Application type",
@ -75,16 +87,73 @@
"AddDeveloper": "Add developer", "AddDeveloper": "Add developer",
"Create": "Create", "Create": "Create",
"UserNotFound": "User not found", "UserNotFound": "User not found",
"{0}WillBeRemovedFromMembers": "{0} Will be removed from members", "{0}WillBeRemovedFromDevelopers": "{0} Will be removed from developers, do you confirm?",
"{0}WillBeRemovedFromOwners": "{0} Will be removed from owners, do you confirm?",
"Computers": "Computers", "Computers": "Computers",
"UniqueComputerId": "Unique computer id", "UniqueComputerId": "Unique computer id",
"LastSeenDate": "Last seen date", "LastSeenDate": "Last seen date",
"{0}Computer{1}WillBeRemovedFromRecords": "Computer of {0} ({1}) will be removed from records", "{0}Computer{1}WillBeRemovedFromRecords": "Computer of {0} ({1}) will be removed from records",
"OrganizationDeletionWarningMessage": "Organization will be deleted", "OrganizationDeletionWarningMessage": "Organization will be deleted",
"DeletingLastOwnerWarningMessage": "An organization must have at least one owner! Therefore you cannot remove this owner",
"This{0}AlreadyExistInThisOrganization": "This {0} already exist in this organization", "This{0}AlreadyExistInThisOrganization": "This {0} already exist in this organization",
"AreYouSureYouWantToDeleteAllComputers": "Are you sure you want to delete all computers?", "AreYouSureYouWantToDeleteAllComputers": "Are you sure you want to delete all computers?",
"DeleteAll": "Delete all", "DeleteAll": "Delete all",
"DoYouWantToCreateNewUser": "Do you want to create new user?", "DoYouWantToCreateNewUser": "Do you want to create new user?",
"MasterModules": "Master Modules" "MasterModules": "Master Modules",
"OrganizationName": "Organization name",
"OrganizationNamePlaceholder": "Organization name...",
"UsernameOrEmail": "Username or email",
"UsernameOrEmailPlaceholder": "Username or email...",
"Member": "Member",
"PurchaseOrderNo": "Purchase order no",
"QuotationDate": "Quotation date",
"CompanyName": "Company name",
"CompanyAddress": "Company address",
"Price": "Price",
"DiscountText": "Discount text",
"DiscountQuantity": "Discount quantity",
"DiscountPrice": "Discount price",
"Quotation": "Quotation",
"ExtraText": "Extra Text",
"ExtraAmount": "Extra Amount",
"DownloadQuotation": "Download Quotation",
"Invoice": "Invoice",
"TaxNumber": "Tax Number",
"InvoiceNumber": "Invoice Number",
"InvoiceDate": "Invoice Date",
"Quantity": "Quantity",
"AddProduct": "Add Product",
"AddProductWarning": "You need to add product!",
"TotalPrice": "Total Price",
"Generate": "Generate",
"MissingQuantityField": "The quantity field is required!",
"MissingPriceField": "The Price field is required!",
"CodeUsageStatus": "Status",
"Country": "Country",
"DeveloperCount": "Developer Count",
"RequestCode": "Request Code",
"WebSite": "Web Site",
"GithubUsername": "Github Username",
"PhoneNumber": "Phone Number",
"ProjectDescription": "Project Description",
"Referrer": "Referrer",
"DiscountRequests": "Discount Request",
"Copylink": "Copy Link",
"Disable": "Disable",
"Enable": "Enable",
"EnableSendEmail": "Enable Send Email",
"SendEmail": "Send Email",
"SuccessfullyDisabled": "Successfully Disabled",
"SuccessfullyEnabled": "Successfully Enabled",
"EmailSent": "Email Sent",
"SuccessfullySent": "Successfully Sent",
"SuccessfullyDeleted": "Successfully Deleted",
"DiscountRequestDeletionWarningMessage": "Discount request will be deleted",
"BusinessType": "Business Type",
"TotalQuestionCount": "Total question count",
"RemainingQuestionCount": "Remaining question count",
"TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount must be greater than RemainingQuestionCount !",
"QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount and RemainingQuestionCount must be zero or greater than zero !",
"UnlimitedQuestionCount": "Unlimited question count"
} }
} }

90
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ru.json

@ -0,0 +1,90 @@
{
"culture": "ru",
"texts": {
"Permission:Organizations": "Организации",
"Permission:Manage": "Управление организациями",
"Permission:NpmPackages": "Пакеты NPM",
"Permission:NugetPackages": "Пакеты NuGet",
"Permission:Maintenance": "Обслуживание",
"Permission:Maintain": "Обслуживать",
"Permission:ClearCaches": "Очистить кэш",
"Permission:Modules": "Модули",
"Permission:Packages": "Пакеты",
"Permission:Edit": "Редактировать",
"Permission:Delete": "Удалить",
"Permission:Create": "Создать",
"Menu:Organizations": "Организации",
"Menu:Packages": "Пакеты",
"NpmPackageDeletionWarningMessage": "Этот пакет NPM будет удален. Вы подтверждаете это?",
"NugetPackageDeletionWarningMessage": "Этот пакет NuGet будет удален. Вы подтверждаете это?",
"ModuleDeletionWarningMessage": "Этот модуль будет удален. Вы подтверждаете это?",
"Name": "Имя",
"DisplayName": "Отображаемое имя",
"ShortDescription": "Краткое описание",
"NameFilter": "Имя",
"CreationTime": "Время создания",
"IsPro": "Is pro",
"EfCoreConfigureMethodName": "Настроить имя метода",
"IsProFilter": "Is pro",
"ApplicationType": "Тип приложения",
"Target": "Цель",
"TargetFilter": "Цель",
"ModuleClass": "Класс модуля",
"NugetPackageTarget.DomainShared": "Domain Shared",
"NugetPackageTarget.Domain": "Domain",
"NugetPackageTarget.Application": "Application",
"NugetPackageTarget.ApplicationContracts": "Application Contracts",
"NugetPackageTarget.HttpApi": "Http Api",
"NugetPackageTarget.HttpApiClient": "Http Api Client",
"NugetPackageTarget.Web": "Web",
"NugetPackageTarget.EntityFrameworkCore": "DeleteAllEntityFramework Core",
"NugetPackageTarget.MongoDB": "MongoDB",
"Edit": "Редактировать",
"Delete": "Удалить",
"Refresh": "Обновить",
"NpmPackages": "NPM пакеты",
"NugetPackages": "NuGet пакеты",
"NpmPackageCount": "Количество пакетов NPM",
"NugetPackageCount": "Количество пакетов NuGet",
"Module": "Модули",
"ModuleInfo": "Информация о модуле",
"CreateANpmPackage": "Создать пакет NPM",
"CreateAModule": "Создать модуль",
"CreateANugetPackage": "Создать пакет NuGet",
"AddNew": "Добавить новый",
"PackageAlreadyExist{0}": "\"{0}\" пакет уже существует.",
"ModuleAlreadyExist{0}": "\"{0}\" модуль уже добавлен.",
"ClearCache": "Очистить кэш",
"SuccessfullyCleared": "Успешно очищено",
"Menu:NpmPackages": "Пакеты NPM",
"Menu:Modules": "Модули",
"Menu:Maintenance": "Поддержка",
"Menu:NugetPackages": "Пакеты NuGet",
"CreateAnOrganization": "Создать организацию",
"Organizations": "Организации",
"LongName": "Полное название",
"LicenseType": "Тип лицензии",
"LicenseStartTime": "Время начала действия лицензии",
"LicenseEndTime": "Время окончания действия лицензии",
"AllowedDeveloperCount": "Разрешенное количество разработчиков",
"UserNameOrEmailAddress": "Имя пользователя или адрес электронной почты",
"AddOwner": "Добавить владельца",
"UserName": "Имя пользователя",
"Email": "Электронная почта",
"Developers": "Разработчики",
"AddDeveloper": "Добавить разработчика",
"Create": "Создать",
"UserNotFound": "Пользователь не обнаружен",
"{0}WillBeRemovedFromMembers": "{0} будет удален из членов",
"Computers": "Компьютеры",
"UniqueComputerId": "Уникальный id компьютера",
"LastSeenDate": "Дата последнего визита",
"{0}Computer{1}WillBeRemovedFromRecords": "Компьютер {0} ({1}) будет удален из записей",
"OrganizationDeletionWarningMessage": "Организация будет удалена",
"This{0}AlreadyExistInThisOrganization": "{0} уже существует в данной организации",
"AreYouSureYouWantToDeleteAllComputers": "Вы уверены, что хотите удалить все компьютеры?",
"DeleteAll": "Удалить все",
"DoYouWantToCreateNewUser": "Вы хотите создать нового пользователя?",
"MasterModules": "Мастер модулей"
}
}

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

@ -29,7 +29,7 @@
"ApiKey": "API key", "ApiKey": "API key",
"UserNameNotFound": "There is no user with username {0}", "UserNameNotFound": "There is no user with username {0}",
"SuccessfullyAddedToNewsletter": "Thanks you for subscribing to our newsletter!", "SuccessfullyAddedToNewsletter": "Thanks you for subscribing to our newsletter!",
"ManageProfile": "Manage your profile", "MyProfile": "My profile",
"EmailNotValid": "Please enter a valid email address." "EmailNotValid": "Please enter a valid email address."
} }
} }

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

@ -1,7 +1,7 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"GetStarted": "Get Started", "GetStarted": "Get Started - Startup Templates",
"Create": "Create", "Create": "Create",
"NewProject": "New Project", "NewProject": "New Project",
"DirectDownload": "Direct Download", "DirectDownload": "Direct Download",
@ -39,7 +39,7 @@
"Theming": "Theming", "Theming": "Theming",
"ThemingExplanationShort": "Use and customize the bootstrap-based standard UI theme or create your own one.", "ThemingExplanationShort": "Use and customize the bootstrap-based standard UI theme or create your own one.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"BootstrapTagHelpersDynamicFormsExplanation": "Built-in background job system that can be integrated to Hangfire, RabbitMQ or any tool you like.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "Instead of manually writing the repeating details of bootstrap components, Use ABP's tag helpers to simplify it and take advantage of intellisense. Dynamic form can create the complete form from a C# class as the model.",
"HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies", "HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies",
"HTTPAPIsDynamicProxiesExplanation": "Automatically expose application services as REST style HTTP APIs and consume with dynamic JavaScript & C# proxies.", "HTTPAPIsDynamicProxiesExplanation": "Automatically expose application services as REST style HTTP APIs and consume with dynamic JavaScript & C# proxies.",
"CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.", "CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.",
@ -86,7 +86,7 @@
"BasedOnFamiliarToolsExplanation": "Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.", "BasedOnFamiliarToolsExplanation": "Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.",
"ORMIndependent": "ORM Independent", "ORMIndependent": "ORM Independent",
"ORMIndependentExplanation": "The core framework is ORM/database independent and can work with any data source. Entity Framework Core and MongoDB providers are already available.", "ORMIndependentExplanation": "The core framework is ORM/database independent and can work with any data source. Entity Framework Core and MongoDB providers are already available.",
"Features": "Features", "Features": "Explore the ABP Framework Features",
"ABPCLI": "ABP CLI", "ABPCLI": "ABP CLI",
"Modularity": "Modularity", "Modularity": "Modularity",
"BootstrapTagHelpers": "Bootstrap Tag Helpers", "BootstrapTagHelpers": "Bootstrap Tag Helpers",

9
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/pt-BR.json

@ -29,17 +29,17 @@
"Authorization": "Autorização", "Authorization": "Autorização",
"AuthorizationExplanation": "Autorização avançada com usuário, função e sistema de permissão refinado. Criada na biblioteca Microsoft Identity.", "AuthorizationExplanation": "Autorização avançada com usuário, função e sistema de permissão refinado. Criada na biblioteca Microsoft Identity.",
"MultiTenancy": "Múltiplos inquilinos", "MultiTenancy": "Múltiplos inquilinos",
"MultiTenancyExplanation": "Aplicativos SaaS facilitados! Multilocação integrada do banco de dados à interface do usuário.", "MultiTenancyExplanationShort": "Aplicativos SaaS facilitados! Multilocação integrada do banco de dados à interface do usuário.",
"CrossCuttingConcerns": "Características transversais", "CrossCuttingConcerns": "Características transversais",
"CrossCuttingConcernsExplanation": "Infraestrutura completa para autorização, validação, tratamento de exceções, armazenamento em cache, log de auditoria, gerenciamento de transações e assim por diante.", "CrossCuttingConcernsExplanationShort": "Infraestrutura completa para autorização, validação, tratamento de exceções, armazenamento em cache, log de auditoria, gerenciamento de transações e assim por diante.",
"BuiltInBundlingMinification": "Pacote & Minificação Integrados", "BuiltInBundlingMinification": "Pacote & Minificação Integrados",
"BuiltInBundlingMinificationExplanation": "Pare de usar ferramentas externas para empacotamento e minificação. O ABP oferece uma maneira mais simples, dinâmica, poderosa, modular e integrada!", "BuiltInBundlingMinificationExplanation": "Pare de usar ferramentas externas para empacotamento e minificação. O ABP oferece uma maneira mais simples, dinâmica, poderosa, modular e integrada!",
"VirtualFileSystem": "Sistema de arquivos virtual", "VirtualFileSystem": "Sistema de arquivos virtual",
"VirtualFileSystemExplanation": "Incorpore visualizações, scripts, estilos, imagens ... aos pacotes/bibliotecas e reutilize-os em diferentes aplicativos.", "VirtualFileSystemExplanation": "Incorpore visualizações, scripts, estilos, imagens ... aos pacotes/bibliotecas e reutilize-os em diferentes aplicativos.",
"Theming": "Theming", "Theming": "Theming",
"ThemingExplanation": "Use e personalize o tema da interface do usuário padrão baseado em bootstrap-based ou crie o seu próprio.", "ThemingExplanationShort": "Use e personalize o tema da interface do usuário padrão baseado em bootstrap-based ou crie o seu próprio.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"BootstrapTagHelpersDynamicFormsExplanation": "Sistema de tarefas em segundo plano integrado que pode ser integrado ao Hangfire, RabbitMQ ou a qualquer ferramenta que você desejar.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "Em vez de escrever manualmente os detalhes repetidos dos componentes de autoinicialização, use os auxiliares de tag da ABP para simplificá-lo e tirar proveito do intellisense. O formulário dinâmico pode criar o formulário completo a partir de uma classe C# como modelo.",
"HTTPAPIsDynamicProxies": "APIs HTTP e proxies dinâmicos", "HTTPAPIsDynamicProxies": "APIs HTTP e proxies dinâmicos",
"HTTPAPIsDynamicProxiesExplanation": "Exponha automaticamente os serviços de aplicativo como APIs HTTP do estilo REST e consuma com proxies JavaScript & C # dinâmicos.", "HTTPAPIsDynamicProxiesExplanation": "Exponha automaticamente os serviços de aplicativo como APIs HTTP do estilo REST e consuma com proxies JavaScript & C # dinâmicos.",
"CompleteArchitectureInfo": "Arquitetura moderna para criar soluções de software sustentáveis.", "CompleteArchitectureInfo": "Arquitetura moderna para criar soluções de software sustentáveis.",
@ -100,7 +100,6 @@
"DistributedEventBus": "Barramento de Eventos Distribuídos", "DistributedEventBus": "Barramento de Eventos Distribuídos",
"DistributedEventBusWithRabbitMQIntegration": "Barramento de Eventos Distribuídos com Integração RabbitMQ", "DistributedEventBusWithRabbitMQIntegration": "Barramento de Eventos Distribuídos com Integração RabbitMQ",
"TestInfrastructure": "Infraestrutura de Teste", "TestInfrastructure": "Infraestrutura de Teste",
"AuditLogging": "Log de auditoria",
"AuditLoggingEntityHistories": "Log de auditoria e históricos de entidades", "AuditLoggingEntityHistories": "Log de auditoria e históricos de entidades",
"ObjectToObjectMapping": "Objeto para Mapeamento de Objetos", "ObjectToObjectMapping": "Objeto para Mapeamento de Objetos",
"EmailSMSAbstractions": "Abstrações de E-mail e SMS", "EmailSMSAbstractions": "Abstrações de E-mail e SMS",

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

@ -31,7 +31,7 @@
"MultiTenancy": "多租户", "MultiTenancy": "多租户",
"MultiTenancyExplanationShort": "SaaS应用程序变得简单! 从数据库到UI的多租户集成.", "MultiTenancyExplanationShort": "SaaS应用程序变得简单! 从数据库到UI的多租户集成.",
"CrossCuttingConcerns": "横切关注点", "CrossCuttingConcerns": "横切关注点",
"CrossCuttingConcernsExplanationShort": "完整的基础架构,用于授权,验证,异常处理,缓存,审计日志记录事务管理等.", "CrossCuttingConcernsExplanationShort": "完整的基础架构,用于授权,验证,异常处理,缓存,审计日志记录,事务管理等.",
"BuiltInBundlingMinification": "内置Bundling & Minification", "BuiltInBundlingMinification": "内置Bundling & Minification",
"BuiltInBundlingMinificationExplanation": "无需使用外部工具进行Bundling & Minification. ABP提供了一种更简单,动态,功能强大,模块化和内置的方式!", "BuiltInBundlingMinificationExplanation": "无需使用外部工具进行Bundling & Minification. ABP提供了一种更简单,动态,功能强大,模块化和内置的方式!",
"VirtualFileSystem": "虚拟文件系统", "VirtualFileSystem": "虚拟文件系统",
@ -39,7 +39,7 @@
"Theming": "主题", "Theming": "主题",
"ThemingExplanationShort": "使用和定制基于bootstrap的标准UI主题,或创建自己的主题.", "ThemingExplanationShort": "使用和定制基于bootstrap的标准UI主题,或创建自己的主题.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和动态表单", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和动态表单",
"BootstrapTagHelpersDynamicFormsExplanation": "内置的后台作业系统可以集成到Hangfire,RabbitMQ或您喜欢的任何工具中.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "与其手动重复编写Bootstrap的组件,不如使用ABP的Tag Helpers利用智能感知来简化它. 动态表单可以从C#类创建完整的表单.",
"HTTPAPIsDynamicProxies": "HTTP APIs和动态代理", "HTTPAPIsDynamicProxies": "HTTP APIs和动态代理",
"HTTPAPIsDynamicProxiesExplanation": "自动将应用程序服务公开为REST样式的HTTP API,并与动态JavaScript和C#代理一起使用.", "HTTPAPIsDynamicProxiesExplanation": "自动将应用程序服务公开为REST样式的HTTP API,并与动态JavaScript和C#代理一起使用.",
"CompleteArchitectureInfo": "现代架构用来创建可维护的软件解决方案.", "CompleteArchitectureInfo": "现代架构用来创建可维护的软件解决方案.",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json

@ -39,7 +39,7 @@
"Theming": "主題", "Theming": "主題",
"ThemingExplanationShort": "使用和訂製基於bootstrap的標準UI主題,或建立自己的主題.", "ThemingExplanationShort": "使用和訂製基於bootstrap的標準UI主題,或建立自己的主題.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和動態表單", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和動態表單",
"BootstrapTagHelpersDynamicFormsExplanation": "內建的背景作業系統可以整合到Hangfire,RabbitMQ或您喜歡的任何工具中.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "與其手動重複編寫Bootstrap的組件,不如使用ABP的Tag Helpers利用智能感知來簡化它. 動態表單可以從C#類創建完整的表單.",
"HTTPAPIsDynamicProxies": "HTTP APIs和動態代理", "HTTPAPIsDynamicProxies": "HTTP APIs和動態代理",
"HTTPAPIsDynamicProxiesExplanation": "自動將應用程式服務公開為REST樣式的HTTP API,並與動態JavaScript和C#代理一起使用.", "HTTPAPIsDynamicProxiesExplanation": "自動將應用程式服務公開為REST樣式的HTTP API,並與動態JavaScript和C#代理一起使用.",
"CompleteArchitectureInfo": "現在架構用來建立可維護的軟體解決方案.", "CompleteArchitectureInfo": "現在架構用來建立可維護的軟體解決方案.",

9
build/common.ps1

@ -19,16 +19,9 @@ $solutionPaths = (
"../modules/audit-logging", "../modules/audit-logging",
"../modules/background-jobs", "../modules/background-jobs",
"../modules/client-simulation", "../modules/client-simulation",
"../modules/virtual-file-explorer",
"../templates/module/aspnet-core", "../templates/module/aspnet-core",
"../templates/app/aspnet-core", "../templates/app/aspnet-core",
"../samples/BasicAspNetCoreApplication",
"../samples/BasicConsoleApplication",
"../samples/BookStore",
"../samples/BookStore-Angular-MongoDb/aspnet-core",
"../samples/BookStore-Modular/modules/book-management",
"../samples/BookStore-Modular/application",
"../samples/DashboardDemo",
"../samples/MicroserviceDemo", "../samples/MicroserviceDemo",
"../samples/RabbitMqEventBus",
"../abp_io/AbpIoLocalization" "../abp_io/AbpIoLocalization"
) )

7
common.DotSettings

@ -20,4 +20,11 @@
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String> <s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String> <s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary> </wpf:ResourceDictionary>

2
common.props

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

3
docs/cs/AspNetCore/Widgets.md

@ -502,4 +502,5 @@ Configure<AbpWidgetOptions>(options =>
## Podívejte se také na ## Podívejte se také na
* [Příklad projektu (zdrojový kód)](https://github.com/abpframework/abp/tree/dev/samples/DashboardDemo). * [Příklad projektu (zdrojový kód)](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo).

6
docs/cs/Contribution/Index.md

@ -41,7 +41,9 @@ Nový jazyk je publikován jakmile jsou minimálně tyto překlady dokončeny.
ABP framework má flexibilní [lokalizační systém](../Localization.md). Můžete tak vytvořit lokalizované uživatelské prostředí pro svou vlastní aplikaci. ABP framework má flexibilní [lokalizační systém](../Localization.md). Můžete tak vytvořit lokalizované uživatelské prostředí pro svou vlastní aplikaci.
K tomu mají framework a vestavěné moduly již lokalizované texty. Například [lokalizační texty pro Volo.Abp.UI balík](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). Můžete vytvořit nový soubor ve [stejné složce](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) k přidání překladu. K tomu mají framework a vestavěné moduly již lokalizované texty. Například [lokalizační texty pro Volo.Abp.UI balík](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).
Můžete vytvořit nový soubor ve [stejné složce](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) k přidání překladu.
* Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu. * Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu.
* Vytvořte nový soubor pro cílový jazyk pro lokalizační text v (json) souboru (u souboru en.json). * Vytvořte nový soubor pro cílový jazyk pro lokalizační text v (json) souboru (u souboru en.json).
@ -49,6 +51,8 @@ K tomu mají framework a vestavěné moduly již lokalizované texty. Napříkla
* Přeložte texty. * Přeložte texty.
* Zašlete pull request na Githubu. * Zašlete pull request na Githubu.
K překladu lokalizovaných textů můžete také použít příkaz `abp translate` of [ABP CLI](CLI.md).
ABP je modulářní framework, proto je zde mnoho zdrojů lokalizačních textů, jeden pro každý modul. K najití všech .json souborů, vyhledejte po naklonování repozitáře soubory "en.json". Můžete se taky podívat na [tento seznam](Localization-Text-Files.md) souborů lokalizačních textů. ABP je modulářní framework, proto je zde mnoho zdrojů lokalizačních textů, jeden pro každý modul. K najití všech .json souborů, vyhledejte po naklonování repozitáře soubory "en.json". Můžete se taky podívat na [tento seznam](Localization-Text-Files.md) souborů lokalizačních textů.
### Příspevky do blogu a návody ### Příspevky do blogu a návody

40
docs/cs/Contribution/Localization-Text-Files.md

@ -1,40 +0,0 @@
## Textové soubory pro lokalizaci
Toto je seznam lokalizačních textových souborů pro každého kdo chce přispět k překladu textů ve frameworku. Tento seznam budeme udržovat aktuální:
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpValidation/en.json
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/en.json
* https://github.com/abpframework/abp/tree/master/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json
* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json
* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Web/Localization/Resources/AbpIdentity/en.json
* https://github.com/abpframework/abp/tree/master/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/en.json
* https://github.com/abpframework/abp/tree/master/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/en.json
* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/en.json
* https://github.com/abpframework/abp/tree/master/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json
* https://github.com/abpframework/abp/tree/master/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json
* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Application.Contracts/Localization/MyProjectName/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/DomainShared/en.json
* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Web/Localization/MyProjectName/Web/en.json
* https://github.com/abpframework/abp/tree/master/templates/mvc/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json

4
docs/cs/Entity-Framework-Core-PostgreSQL.md

@ -10,9 +10,9 @@ Projekt `.EntityFrameworkCore` v řešení závisí na NuGet balíku [Volo.Abp.E
Najděte třídu ***YourProjectName*EntityFrameworkCoreModule** v projektu `.EntityFrameworkCore`, odstraňte `typeof(AbpEntityFrameworkCoreSqlServerModule)` z atributu `DependsOn`, přidejte `typeof(AbpEntityFrameworkCorePostgreSqlModule)` (také nahraďte `using Volo.Abp.EntityFrameworkCore.SqlServer;` za `using Volo.Abp.EntityFrameworkCore.PostgreSql;`). Najděte třídu ***YourProjectName*EntityFrameworkCoreModule** v projektu `.EntityFrameworkCore`, odstraňte `typeof(AbpEntityFrameworkCoreSqlServerModule)` z atributu `DependsOn`, přidejte `typeof(AbpEntityFrameworkCorePostgreSqlModule)` (také nahraďte `using Volo.Abp.EntityFrameworkCore.SqlServer;` za `using Volo.Abp.EntityFrameworkCore.PostgreSql;`).
## UsePostgreSql() ## UseNpgsql()
Najděte volání `UseSqlServer()` v *YourProjectName*EntityFrameworkCoreModule.cs uvnitř projektu `.EntityFrameworkCore` a nahraďte za `UsePostgreSql()`. Najděte volání `UseSqlServer()` v *YourProjectName*EntityFrameworkCoreModule.cs uvnitř projektu `.EntityFrameworkCore` a nahraďte za `UseNpgsql()`.
Najděte volání `UseSqlServer()` v *YourProjectName*MigrationsDbContextFactory.cs uvnitř projektu `.EntityFrameworkCore.DbMigrations` a nahraďte za `UseNpgsql()`. Najděte volání `UseSqlServer()` v *YourProjectName*MigrationsDbContextFactory.cs uvnitř projektu `.EntityFrameworkCore.DbMigrations` a nahraďte za `UseNpgsql()`.

4
docs/cs/Getting-Started-AspNetCore-Application.md

@ -57,7 +57,7 @@ namespace BasicAspNetCoreApplication
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseMvcWithDefaultRouteAndArea(); app.UseConfiguredEndpoints();
} }
} }
} }
@ -153,5 +153,5 @@ namespace BasicAspNetCoreApplication
## Zdrojový kód ## Zdrojový kód
Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp/tree/master/samples/BasicAspNetCoreApplication). Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication).

2
docs/cs/Getting-Started-Console-Application.md

@ -178,4 +178,4 @@ Stačí volat metodu `options.UseAutofac()` v možnostech `AbpApplicationFactory
## Zdrojový kód ## Zdrojový kód
Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp/tree/master/samples/BasicConsoleApplication). Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp-samples/tree/master/BasicConsoleApplication).

16
docs/en/API/Dynamic-CSharp-API-Clients.md

@ -39,7 +39,7 @@ Now, it's ready to create the client proxies. Example:
````csharp ````csharp
[DependsOn( [DependsOn(
typeof(AbpHttpClientModule), //used to create client proxies typeof(AbpHttpClientModule), //used to create client proxies
typeof(BookStoreApplicationModule) //contains the application service interfaces typeof(BookStoreApplicationContractsModule) //contains the application service interfaces
)] )]
public class MyClientAppModule : AbpModule public class MyClientAppModule : AbpModule
{ {
@ -47,7 +47,7 @@ public class MyClientAppModule : AbpModule
{ {
//Create dynamic client proxies //Create dynamic client proxies
context.Services.AddHttpClientProxies( context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly typeof(BookStoreApplicationContractsModule).Assembly
); );
} }
} }
@ -69,7 +69,7 @@ public class MyClientAppModule : AbpModule
} }
```` ````
See the "RemoteServiceOptions" section below for more detailed configuration. See the "AbpRemoteServiceOptions" section below for more detailed configuration.
## Usage ## Usage
@ -104,7 +104,7 @@ While you can inject `IBookAppService` like above to use the client proxy, you c
## Configuration ## Configuration
### RemoteServiceOptions ### AbpRemoteServiceOptions
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example: `AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
@ -142,12 +142,12 @@ The examples above have configured the "Default" remote service endpoint. You ma
````csharp ````csharp
context.Services.AddHttpClientProxies( context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly, typeof(BookStoreApplicationContractsModule).Assembly,
remoteServiceName: "BookStore" remoteServiceConfigurationName: "BookStore"
); );
```` ````
`remoteServiceName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. `remoteServiceConfigurationName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
### As Default Services ### As Default Services
@ -155,7 +155,7 @@ When you create a service proxy for `IBookAppService`, you can directly inject t
````csharp ````csharp
context.Services.AddHttpClientProxies( context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly, typeof(BookStoreApplicationContractsModule).Assembly,
asDefaultServices: false asDefaultServices: false
); );
```` ````

103
docs/en/Application-Services.md

@ -132,6 +132,8 @@ The `CreateAsync` method above manually creates a `Book` entity from given `Crea
However, in many cases, it's very practical to use **auto object mapping** to set properties of an object from a similar object. ABP provides an [object to object mapping](Object-To-Object-Mapping.md) infrastructure to make this even easier. However, in many cases, it's very practical to use **auto object mapping** to set properties of an object from a similar object. ABP provides an [object to object mapping](Object-To-Object-Mapping.md) infrastructure to make this even easier.
Object to object mapping provides abstractions and it is implemented by the [AutoMapper](https://automapper.org/) library by default.
Let's create another method to get a book. First, define the method in the `IBookAppService` interface: Let's create another method to get a book. First, define the method in the `IBookAppService` interface:
````csharp ````csharp
@ -146,7 +148,6 @@ public interface IBookAppService : IApplicationService
`BookDto` is a simple [DTO](Data-Transfer-Objects.md) class defined as below: `BookDto` is a simple [DTO](Data-Transfer-Objects.md) class defined as below:
````csharp ````csharp
[AbpAutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto public class BookDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
@ -159,21 +160,38 @@ public class BookDto
} }
```` ````
* `BookDto` defines `[AbpAutoMapFrom(typeof(Book))]` attribute to create the object mapping from `Book` to `BookDto`. AutoMapper requires to create a mapping [profile class](https://docs.automapper.org/en/stable/Configuration.html#profile-instances). Example:
Then you can implement the `GetAsync` method as shown below: ````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<Book, BookDto>();
}
}
````
You should then register profiles using the `AbpAutoMapperOptions`:
````csharp ````csharp
public async Task<BookDto> GetAsync(Guid id) [DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{ {
var book = await _bookRepository.GetAsync(id); public override void ConfigureServices(ServiceConfigurationContext context)
return book.MapTo<BookDto>(); {
Configure<AbpAutoMapperOptions>(options =>
{
//Add all mappings defined in the assembly of the MyModule class
options.AddMaps<MyModule>();
});
}
} }
```` ````
`MapTo` extension method converts `Book` object to `BookDto` object by copying all properties with the same naming. `AddMaps` registers all profile classes defined in the assembly of the given class, typically your module class. It also registers for the [attribute mapping](https://docs.automapper.org/en/stable/Attribute-mapping.html).
An alternative to the `MapTo` is using the `IObjectMapper` service: Then you can implement the `GetAsync` method as shown below:
````csharp ````csharp
public async Task<BookDto> GetAsync(Guid id) public async Task<BookDto> GetAsync(Guid id)
@ -183,8 +201,6 @@ public async Task<BookDto> GetAsync(Guid id)
} }
```` ````
While the second syntax is a bit harder to write, it better works if you write unit tests.
See the [object to object mapping document](Object-To-Object-Mapping.md) for more. See the [object to object mapping document](Object-To-Object-Mapping.md) for more.
## Validation ## Validation
@ -201,7 +217,7 @@ See the [authorization document](Authorization.md) for more.
## CRUD Application Services ## CRUD Application Services
If you need to create a simple **CRUD application service** which has Create, Update, Delete and Get methods, you can use ABP's **base classes** to easily build your services. You can inherit from `CrudAppService`. If you need to create a simple **CRUD application service** which has Create, Update, Delete and Get methods, you can use ABP's **base classes** to easily build your services. You can inherit from the `CrudAppService`.
### Example ### Example
@ -219,7 +235,9 @@ public interface IBookAppService :
} }
```` ````
* `ICrudAppService` has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface). `ICrudAppService` has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface).
> Creating interface for an application service is a good practice, but not required by the ABP Framework. You can skip the interface part.
`ICrudAppService` declares the following methods: `ICrudAppService` declares the following methods:
@ -248,7 +266,6 @@ public interface ICrudAppService<
DTO classes used in this example are `BookDto` and `CreateUpdateBookDto`: DTO classes used in this example are `BookDto` and `CreateUpdateBookDto`:
````csharp ````csharp
[AbpAutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid> public class BookDto : AuditedEntityDto<Guid>
{ {
public string Name { get; set; } public string Name { get; set; }
@ -258,7 +275,6 @@ public class BookDto : AuditedEntityDto<Guid>
public float Price { get; set; } public float Price { get; set; }
} }
[AbpAutoMapTo(typeof(Book))]
public class CreateUpdateBookDto public class CreateUpdateBookDto
{ {
[Required] [Required]
@ -273,6 +289,19 @@ public class CreateUpdateBookDto
} }
```` ````
[Profile](https://docs.automapper.org/en/stable/Configuration.html#profile-instances) class of DTO class.
```csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<Book, BookDto>();
CreateMap<CreateUpdateBookDto, Book>();
}
}
```
* `CreateUpdateBookDto` is shared by create and update operations, but you could use separated DTO classes as well. * `CreateUpdateBookDto` is shared by create and update operations, but you could use separated DTO classes as well.
And finally, the `BookAppService` implementation is very simple: And finally, the `BookAppService` implementation is very simple:
@ -292,6 +321,52 @@ public class BookAppService :
`CrudAppService` implements all methods declared in the `ICrudAppService` interface. You can then add your own custom methods or override and customize base methods. `CrudAppService` implements all methods declared in the `ICrudAppService` interface. You can then add your own custom methods or override and customize base methods.
> `CrudAppService` has different versions gets different number of generic arguments. Use the one suitable for you.
### AbstractKeyCrudAppService
`CrudAppService` requires to have an Id property as the primary key of your entity. If you are using composite keys then you can not utilize it.
`AbstractKeyCrudAppService` implements the same `ICrudAppService` interface, but this time without making assumption about your primary key.
#### Example
Assume that you have a `District` entity with `CityId` and `Name` as a composite primary key. Using `AbstractKeyCrudAppService` requires to implement `DeleteByIdAsync` and `GetEntityByIdAsync` methods yourself:
````csharp
public class DistrictAppService
: AbstractKeyCrudAppService<District, DistrictDto, DistrictKey>
{
public DistrictAppService(IRepository<District> repository)
: base(repository)
{
}
protected override async Task DeleteByIdAsync(DistrictKey id)
{
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name);
}
protected override async Task<District> GetEntityByIdAsync(DistrictKey id)
{
return await AsyncQueryableExecuter.FirstOrDefaultAsync(
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name)
);
}
}
````
This implementation requires you to create a class represents your composite key:
````csharp
public class DistrictKey
{
public Guid CityId { get; set; }
public string Name { get; set; }
}
````
## Lifetime ## Lifetime
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system. Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system.

20
docs/en/AspNet-Boilerplate-Migration-Guide.md

@ -216,9 +216,9 @@ However, it provides the following methods those can be used to query a single e
* `FindAsync(id)` returns the entity or null if not found. * `FindAsync(id)` returns the entity or null if not found.
* `GetAsync(id)` method returns the entity or throws an `EntityNotFoundException` (which causes HTTP 404 status code) if not found. * `GetAsync(id)` method returns the entity or throws an `EntityNotFoundException` (which causes HTTP 404 status code) if not found.
#### Sync vs Async #### Sync vs Async
ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions. ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions.
In general, ABP Framework forces you to completely use async everywhere, because mixing async & sync methods is not a recommended approach. In general, ABP Framework forces you to completely use async everywhere, because mixing async & sync methods is not a recommended approach.
@ -444,7 +444,7 @@ ASP.NET Boilerplate uses Castle Windsor's [logging facility](http://docs.castlep
using Castle.Core.Logging; //1: Import Logging namespace using Castle.Core.Logging; //1: Import Logging namespace
public class TaskAppService : ITaskAppService public class TaskAppService : ITaskAppService
{ {
//2: Getting a logger using property injection //2: Getting a logger using property injection
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
@ -693,26 +693,22 @@ public class AbpTenantManagementWebMainMenuContributor : IMenuContributor
var administrationMenu = context.Menu.GetAdministration(); var administrationMenu = context.Menu.GetAdministration();
//Resolve some needed services from the DI container //Resolve some needed services from the DI container
var authorizationService = context.ServiceProvider var l = context.GetLocalizer<AbpTenantManagementResource>();
.GetRequiredService<IAuthorizationService>();
var l = context.ServiceProvider
.GetRequiredService<IStringLocalizer<AbpTenantManagementResource>>();
var tenantManagementMenuItem = new ApplicationMenuItem( var tenantManagementMenuItem = new ApplicationMenuItem(
TenantManagementMenuNames.GroupName, TenantManagementMenuNames.GroupName,
l["Menu:TenantManagement"], l["Menu:TenantManagement"],
icon: "fa fa-users"); icon: "fa fa-users");
administrationMenu.AddItem(tenantManagementMenuItem); administrationMenu.AddItem(tenantManagementMenuItem);
//Conditionally add the "Tenants" menu item based on the permission //Conditionally add the "Tenants" menu item based on the permission
if (await authorizationService if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
{ {
tenantManagementMenuItem.AddItem( tenantManagementMenuItem.AddItem(
new ApplicationMenuItem( new ApplicationMenuItem(
TenantManagementMenuNames.Tenants, TenantManagementMenuNames.Tenants,
l["Tenants"], l["Tenants"],
url: "/TenantManagement/Tenants")); url: "/TenantManagement/Tenants"));
} }
} }
@ -731,4 +727,4 @@ The following features are not present for the ABP Framework. Here, a list of so
* [Real time notification system](https://aspnetboilerplate.com/Pages/Documents/Notification-System) ([#633](https://github.com/abpframework/abp/issues/633)) * [Real time notification system](https://aspnetboilerplate.com/Pages/Documents/Notification-System) ([#633](https://github.com/abpframework/abp/issues/633))
* [NHibernate Integration](https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration) ([#339](https://github.com/abpframework/abp/issues/339)) - We don't intent to work on this, but any community contribution welcome. * [NHibernate Integration](https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration) ([#339](https://github.com/abpframework/abp/issues/339)) - We don't intent to work on this, but any community contribution welcome.
Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated. Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated.

30
docs/en/Authorization.md

@ -142,6 +142,20 @@ myGroup.AddPermission(
); );
``` ```
#### Enable/Disable Permissions
A permission is enabled by default. It is possible to disable a permission. A disabled permission will be prohibited for everyone. You can still check for the permission, but it will always return prohibited.
Example definition:
````csharp
myGroup.AddPermission("Author_Management", isEnabled: false);
````
You normally don't need to define a disabled permission (unless you temporary want disable a feature of your application). However, you may want to disable a permission defined in a depended module. In this way you can disable the related application functionality. See the "*Changing Permission Definitions of a Depended Module*" section below for an example usage.
> Note: Checking an undefined permission will throw an exception while a disabled permission check simply returns prohibited (false).
#### Child Permissions #### Child Permissions
A permission may have child permissions. It is especially useful when you want to create a hierarchical permission tree where a permission may have additional sub permissions which are available only if the parent permission has been granted. A permission may have child permissions. It is especially useful when you want to create a hierarchical permission tree where a permission may have additional sub permissions which are available only if the parent permission has been granted.
@ -208,6 +222,18 @@ See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/se
A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions. A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions.
Example:
````csharp
context
.GetPermissionOrNull(IdentityPermissions.Roles.Delete)
.IsEnabled = false;
````
When you write this code inside your permission definition provider, it finds the "role deletion" permission of the [Identity Module](Modules/Identity.md) and disabled the permission, so no one can delete a role on the application.
> Tip: It is better to check the value returned by the `GetPermissionOrNull` method since it may return null if the given permission was not defined.
## IAuthorizationService ## IAuthorizationService
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization. ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization.
@ -339,10 +365,10 @@ A permission value provider should return one of the following values from the `
- `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return. - `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return.
- `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission. - `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission.
Once a provider is defined, it should be added to the `PermissionOptions` as shown below: Once a provider is defined, it should be added to the `AbpPermissionOptions` as shown below:
```csharp ```csharp
Configure<PermissionOptions>(options => Configure<AbpPermissionOptions>(options =>
{ {
options.ValueProviders.Add<SystemAdminPermissionValueProvider>(); options.ValueProviders.Add<SystemAdminPermissionValueProvider>();
}); });

3
docs/en/AutoMapper-Integration.md

@ -1,3 +0,0 @@
## AutoMapper Integration
TODO

43
docs/en/Background-Jobs-Hangfire.md

@ -40,4 +40,45 @@ public class YourModule : AbpModule
## Configuration ## Configuration
TODO... You can install any storage for Hangfire. The most common one is SQL Server (see the [Hangfire.SqlServer](https://www.nuget.org/packages/Hangfire.SqlServer) NuGet package).
After you have installed these NuGet packages, you need to configure your project to use Hangfire.
1.First, we change the `Module` class (example: `<YourProjectName>HttpApiHostModule`) to add Hangfire configuration of the storage and connection string in the `ConfigureServices` method:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
//... other configarations.
ConfigureHangfire(context, configuration);
}
private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddHangfire(config =>
{
config.UseSqlServerStorage(configuration.GetConnectionString("Default"));
});
}
````
2. We need to add `UseHangfireServer` call in the `OnApplicationInitialization` method in `Module` class
If you want to use hangfire's dashboard, you can add it, too: by `UseHangfireDashboard`
````csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
// ... others
app.UseHangfireServer();
app.UseHangfireDashboard();
}
````

54
docs/en/Background-Jobs-Quartz.md

@ -40,7 +40,7 @@ public class YourModule : AbpModule
## Configuration ## Configuration
Quartz is a very configurable library,and the ABP framework provides `AbpQuartzPreOptions` for this. You can use the `PreConfigure` method in your module class to pre-configure this option. ABP will use it when initializing the Quartz module. For example: Quartz is a very configurable library,and the ABP framework provides `AbpQuartzOptions` for this. You can use the `PreConfigure` method in your module class to pre-configure this option. ABP will use it when initializing the Quartz module. For example:
````csharp ````csharp
[DependsOn( [DependsOn(
@ -53,7 +53,7 @@ public class YourModule : AbpModule
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
PreConfigure<AbpQuartzPreOptions>(options => PreConfigure<AbpQuartzOptions>(options =>
{ {
options.Properties = new NameValueCollection options.Properties = new NameValueCollection
{ {
@ -70,4 +70,52 @@ public class YourModule : AbpModule
} }
```` ````
Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html).
## Exception handling
### Default exception handling strategy
When an exception occurs in the background job,ABP provide the **default handling strategy** retrying once every 3 seconds, up to 3 times. You can change the retry count and retry interval via `AbpBackgroundJobQuartzOptions` options:
```csharp
[DependsOn(
//...other dependencies
typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundJobQuartzOptions>(options =>
{
options.RetryCount = 1;
options.RetryIntervalMillisecond = 1000;
});
}
}
```
### Customize exception handling strategy
You can customize the exception handling strategy via `AbpBackgroundJobQuartzOptions` options:
```csharp
[DependsOn(
//...other dependencies
typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundJobQuartzOptions>(options =>
{
options.RetryStrategy = async (retryIndex, executionContext, exception) =>
{
// customize exception handling
};
});
}
}
```

6
docs/en/Background-Jobs.md

@ -171,4 +171,8 @@ Background job system is extensible and you can change the default background jo
See pre-built job manager alternatives: See pre-built job manager alternatives:
* [Hangfire Background Job Manager](Background-Jobs-Hangfire.md) * [Hangfire Background Job Manager](Background-Jobs-Hangfire.md)
* [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md) * [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md)
* [Quartz Background Job Manager](Background-Jobs-Quartz.md)
## See Also
* [Background Workers](Background-Workers.md)

84
docs/en/Background-Workers-Quartz.md

@ -36,11 +36,11 @@ public class YourModule : AbpModule
} }
```` ````
### Configuration ## Configuration
See [Configuration](Background-Jobs-Quartz#Configuration). See [Configuration](Background-Jobs-Quartz#Configuration).
### Create a Background Worker ## Create a Background Worker
A background work is a class that derives from the `QuartzBackgroundWorkerBase` base class. for example. A simple worker class is shown below: A background work is a class that derives from the `QuartzBackgroundWorkerBase` base class. for example. A simple worker class is shown below:
@ -49,8 +49,8 @@ public class MyLogWorker : QuartzBackgroundWorkerBase
{ {
public MyLogWorker() public MyLogWorker()
{ {
JobDetail = JobBuilder.Create<MyLogWorker>().Build(); JobDetail = JobBuilder.Create<MyLogWorker>().WithIdentity(nameof(MyLogWorker)).Build();
Trigger = TriggerBuilder.Create().StartNow().Build(); Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build();
} }
public override Task Execute(IJobExecutionContext context) public override Task Execute(IJobExecutionContext context)
@ -63,6 +63,82 @@ public class MyLogWorker : QuartzBackgroundWorkerBase
We simply implemented the Execute method to write a log. The background worker is a **singleton by default**. If you want, you can also implement a [dependency interface](Dependency-Injection#DependencyInterfaces) to register it as another life cycle. We simply implemented the Execute method to write a log. The background worker is a **singleton by default**. If you want, you can also implement a [dependency interface](Dependency-Injection#DependencyInterfaces) to register it as another life cycle.
> Tips: Add identity to background workers is a best practice,because quartz distinguishes different jobs based on identity.
## Add to BackgroundWorkerManager
Default background workers are **automatically** added to the BackgroundWorkerManager when the application is **initialized**. You can set `AutoRegister` property value to `false`,if you want to add it manually:
```` csharp
public class MyLogWorker : QuartzBackgroundWorkerBase
{
public MyLogWorker()
{
AutoRegister = false;
JobDetail = JobBuilder.Create<MyLogWorker>().WithIdentity(nameof(MyLogWorker)).Build();
Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build();
}
public override Task Execute(IJobExecutionContext context)
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
}
}
````
If you want to globally disable auto add worker, you can global disable via `AbpBackgroundWorkerQuartzOptions` options:
```csharp
[DependsOn(
//...other dependencies
typeof(AbpBackgroundWorkersQuartzModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundWorkerQuartzOptions>(options =>
{
options.IsAutoRegisterEnabled = false;
});
}
}
```
## Advanced topics
### Customize ScheduleJob
Assume you have a worker executes every 10 minutes,but because server is unavailable for 30 minutes, 3 executions are missed. You want to execute all missed times after the server is available. You should define your background worker like this:
```csharp
public class MyLogWorker : QuartzBackgroundWorkerBase
{
public MyLogWorker()
{
JobDetail = JobBuilder.Create<MyLogWorker>().WithIdentity(nameof(MyLogWorker)).Build();
Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).WithSimpleSchedule(s=>s.WithIntervalInMinutes(1).RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires()).Build();
ScheduleJob = async scheduler =>
{
if (!await scheduler.CheckExists(JobDetail.Key))
{
await scheduler.ScheduleJob(JobDetail, Trigger);
}
};
}
public override Task Execute(IJobExecutionContext context)
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
}
}
```
In the example we defined the worker execution interval to be 10 minutes and set `WithMisfireHandlingInstructionIgnoreMisfires`. we customized `ScheduleJob` and add worker to quartz only when the background worker does not exist.
### More ### More
Please see Quartz's [documentation](https://www.quartz-scheduler.net/documentation/index.html) for more information. Please see Quartz's [documentation](https://www.quartz-scheduler.net/documentation/index.html) for more information.

139
docs/en/Background-Workers.md

@ -1,3 +1,140 @@
# Background Workers # Background Workers
TODO ## Introduction
Background workers are simple independent threads in the application running in the background. Generally, they run periodically to perform some tasks. Examples;
* A background worker can run periodically to **delete old logs**.
* A background worker can run periodically to **determine inactive users** and **send emails** to get users to return to your application.
## Create a Background Worker
A background worker should directly or indirectly implement the `IBackgroundWorker` interface.
> A background worker is inherently [singleton](Dependency-Injection.md). So, only a single instance of your worker class is instantiated and run.
### BackgroundWorkerBase
`BackgroundWorkerBase` is an easy way to create a background worker.
````csharp
public class MyWorker : BackgroundWorkerBase
{
public override Task StartAsync(CancellationToken cancellationToken = default)
{
//...
}
public override Task StopAsync(CancellationToken cancellationToken = default)
{
//...
}
}
````
Start your worker in the `StartAsync` (which is called when the application begins) and stop in the `StopAsync` (which is called when the application shuts down).
> You can directly implement the `IBackgroundWorker`, but `BackgroundWorkerBase` provides some useful properties like `Logger`.
### AsyncPeriodicBackgroundWorkerBase
Assume that we want to make a user passive, if the user has not logged in to the application in last 30 days. `AsyncPeriodicBackgroundWorkerBase` class simplifies to create periodic workers, so we will use it for the example below:
````csharp
public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
{
public PassiveUserCheckerWorker(
AbpTimer timer,
IServiceScopeFactory serviceScopeFactory
) : base(
timer,
serviceScopeFactory)
{
Timer.Period = 600000; //10 minutes
}
protected override async Task DoWorkAsync(
PeriodicBackgroundWorkerContext workerContext)
{
Logger.LogInformation("Starting: Setting status of inactive users...");
//Resolve dependencies
var userRepository = workerContext
.ServiceProvider
.GetRequiredService<IUserRepository>();
//Do the work
await userRepository.UpdateInactiveUserStatusesAsync();
Logger.LogInformation("Completed: Setting status of inactive users...");
}
}
````
* `AsyncPeriodicBackgroundWorkerBase` uses the `AbpTimer` (a thread-safe timer) object to determine **the period**. We can set its `Period` property in the constructor.
* It required to implement the `DoWorkAsync` method to **execute** the periodic work.
* It is a good practice to **resolve dependencies** from the `PeriodicBackgroundWorkerContext` instead of constructor injection. Because `AsyncPeriodicBackgroundWorkerBase` uses a `IServiceScope` that is **disposed** when your work finishes.
* `AsyncPeriodicBackgroundWorkerBase` **catches and logs exceptions** thrown by the `DoWorkAsync` method.
## Register Background Worker
After creating a background worker class, you should to add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitialization` method of your module class:
````csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<PassiveUserCheckerWorker>();
}
}
````
`context.AddBackgroundWorker(...)` is a shortcut extension method for the expression below:
````csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
context
.ServiceProvider
.GetRequiredService<PassiveUserCheckerWorker>()
);
````
So, it resolves the given background worker and adds to the `IBackgroundWorkerManager`.
While we generally add workers in `OnApplicationInitialization`, there are no restrictions on that. You can inject `IBackgroundWorkerManager` anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.
## Options
`AbpBackgroundWorkerOptions` class is used to [set options](Options.md) for the background workers. Currently, there is only one option:
* `IsEnabled` (default: true): Used to **enable/disable** the background worker system for your application.
> See the [Options](Options.md) document to learn how to set options.
## Making Your Application Always Run
Background workers only work if your application is running. If you host the background job execution in your web application (this is the default behavior), you should ensure that your web application is configured to always be running. Otherwise, background jobs only work while your application is in use.
## Running On a Cluster
Be careful if you run multiple instances of your application simultaneously in a clustered environment. In that case, every application runs the same worker which may create conflicts if your workers are running on the same resources (processing the same data, for example).
If that's a problem for your workers, you have two options;
* Disable the background worker system using the `AbpBackgroundWorkerOptions` described above, for all the application instances, except one of them.
* Disable the background worker system for all the application instances and create another special application that runs on a single server and execute the workers.
## Quartz Integration
ABP Framework's background worker system is good to implement periodic tasks. However, you may want to use an advanced task scheduler like [Quartz](https://www.quartz-scheduler.net/). See the community contributed [quartz integration](Background-Workers-Quartz.md) for the background workers.
## See Also
* [Quartz Integration for the background workers](Background-Workers-Quartz.md)
* [Background Jobs](Background-Jobs.md)

26
docs/en/Best-Practices/Application-Services.md

@ -17,17 +17,18 @@
##### Basic DTO ##### Basic DTO
**Do** define a **basic** DTO for an entity. **Do** define a **basic** DTO for an aggregate root.
- Include all the **primitive properties** directly on the entity. - Include all the **primitive properties** directly on the aggregate root.
- Exception: Can **exclude** properties for **security** reasons (like User.Password). - Exception: Can **exclude** properties for **security** reasons (like `User.Password`).
- Include all the **sub collections** of the entity where every item in the collection is a simple **relation DTO**. - Include all the **sub collections** of the entity where every item in the collection is a simple **relation DTO**.
- Inherit from one of the **extensible entity DTO** classes for aggregate roots (and entities implement the `IHasExtraProperties`).
Example: Example:
```c# ```c#
[Serializable] [Serializable]
public class IssueDto : FullAuditedEntityDto<Guid> public class IssueDto : ExtensibleFullAuditedEntityDto<Guid>
{ {
public string Title { get; set; } public string Title { get; set; }
public string Text { get; set; } public string Text { get; set; }
@ -57,7 +58,7 @@ Example:
````C# ````C#
[Serializable] [Serializable]
public class IssueWithDetailsDto : FullAuditedEntityDto<Guid> public class IssueWithDetailsDto : ExtensibleFullAuditedEntityDto<Guid>
{ {
public string Title { get; set; } public string Title { get; set; }
public string Text { get; set; } public string Text { get; set; }
@ -66,14 +67,14 @@ public class IssueWithDetailsDto : FullAuditedEntityDto<Guid>
} }
[Serializable] [Serializable]
public class MilestoneDto : EntityDto<Guid> public class MilestoneDto : ExtensibleEntityDto<Guid>
{ {
public string Name { get; set; } public string Name { get; set; }
public bool IsClosed { get; set; } public bool IsClosed { get; set; }
} }
[Serializable] [Serializable]
public class LabelDto : EntityDto<Guid> public class LabelDto : ExtensibleEntityDto<Guid>
{ {
public string Name { get; set; } public string Name { get; set; }
public string Color { get; set; } public string Color { get; set; }
@ -120,6 +121,7 @@ Task<List<QuestionWithDetailsDto>> GetListAsync(QuestionListQueryDto queryDto);
* **Do** use the `CreateAsync` **method name**. * **Do** use the `CreateAsync` **method name**.
* **Do** get a **specialized input** DTO to create the entity. * **Do** get a **specialized input** DTO to create the entity.
* **Do** inherit the DTO class from the `ExtensibleObject` (or any other class implements the `IHasExtraProperties`) to allow to pass extra properties if needed.
* **Do** use **data annotations** for input validation. * **Do** use **data annotations** for input validation.
* Share constants between domain wherever possible (via constants defined in the **domain shared** package). * Share constants between domain wherever possible (via constants defined in the **domain shared** package).
* **Do** return **the detailed** DTO for new created entity. * **Do** return **the detailed** DTO for new created entity.
@ -135,10 +137,11 @@ The related **DTO**:
````C# ````C#
[Serializable] [Serializable]
public class CreateQuestionDto public class CreateQuestionDto : ExtensibleObject
{ {
[Required] [Required]
[StringLength(QuestionConsts.MaxTitleLength, MinimumLength = QuestionConsts.MinTitleLength)] [StringLength(QuestionConsts.MaxTitleLength,
MinimumLength = QuestionConsts.MinTitleLength)]
public string Title { get; set; } public string Title { get; set; }
[StringLength(QuestionConsts.MaxTextLength)] [StringLength(QuestionConsts.MaxTextLength)]
@ -152,6 +155,7 @@ public class CreateQuestionDto
- **Do** use the `UpdateAsync` **method name**. - **Do** use the `UpdateAsync` **method name**.
- **Do** get a **specialized input** DTO to update the entity. - **Do** get a **specialized input** DTO to update the entity.
- **Do** inherit the DTO class from the `ExtensibleObject` (or any other class implements the `IHasExtraProperties`) to allow to pass extra properties if needed.
- **Do** get the Id of the entity as a separated primitive parameter. Do not include to the update DTO. - **Do** get the Id of the entity as a separated primitive parameter. Do not include to the update DTO.
- **Do** use **data annotations** for input validation. - **Do** use **data annotations** for input validation.
- Share constants between domain wherever possible (via constants defined in the **domain shared** package). - Share constants between domain wherever possible (via constants defined in the **domain shared** package).
@ -200,6 +204,10 @@ This method votes a question and returns the current score of the question.
* **Do not** use LINQ/SQL for querying data from database inside the application service methods. It's repository's responsibility to perform LINQ/SQL queries from the data source. * **Do not** use LINQ/SQL for querying data from database inside the application service methods. It's repository's responsibility to perform LINQ/SQL queries from the data source.
#### Extra Properties
* **Do** use either `MapExtraPropertiesTo` extension method ([see](Object-Extensions.md)) or configure the object mapper (`MapExtraProperties`) to allow application developers to be able to extend the objects and services.
#### Manipulating / Deleting Entities #### Manipulating / Deleting Entities
* **Do** always get all the related entities from repositories to perform the operations on them. * **Do** always get all the related entities from repositories to perform the operations on them.

1
docs/en/Best-Practices/Data-Transfer-Objects.md

@ -2,6 +2,7 @@
* **Do** define DTOs in the **application contracts** package. * **Do** define DTOs in the **application contracts** package.
* **Do** inherit from the pre-built **base DTO classes** where possible and necessary (like `EntityDto<TKey>`, `CreationAuditedEntityDto<TKey>`, `AuditedEntityDto<TKey>`, `FullAuditedEntityDto<TKey>` and so on). * **Do** inherit from the pre-built **base DTO classes** where possible and necessary (like `EntityDto<TKey>`, `CreationAuditedEntityDto<TKey>`, `AuditedEntityDto<TKey>`, `FullAuditedEntityDto<TKey>` and so on).
* **Do** inherit from the **extensible DTO** classes for the **aggregate roots** (like `ExtensibleAuditedEntityDto<TKey>`), because aggregate roots are extensible objects and extra properties are mapped to DTOs in this way.
* **Do** define DTO members with **public getter and setter**. * **Do** define DTO members with **public getter and setter**.
* **Do** use **data annotations** for **validation** on the properties of DTOs those are inputs of the service. * **Do** use **data annotations** for **validation** on the properties of DTOs those are inputs of the service.
* **Do** not add any **logic** into DTOs except implementing `IValidatableObject` when necessary. * **Do** not add any **logic** into DTOs except implementing `IValidatableObject` when necessary.

9
docs/en/Best-Practices/Entity-Framework-Core-Integration.md

@ -89,13 +89,15 @@ public static class IdentityDbContextModelBuilderExtensions
builder.Entity<IdentityUser>(b => builder.Entity<IdentityUser>(b =>
{ {
b.ToTable(options.TablePrefix + "Users", options.Schema); b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ConfigureByConvention();
//code omitted for brevity //code omitted for brevity
}); });
builder.Entity<IdentityUserClaim>(b => builder.Entity<IdentityUserClaim>(b =>
{ {
b.ToTable(options.TablePrefix + "UserClaims", options.Schema); b.ToTable(options.TablePrefix + "UserClaims", options.Schema);
b.ConfigureByConvention();
//code omitted for brevity //code omitted for brevity
}); });
@ -104,10 +106,11 @@ public static class IdentityDbContextModelBuilderExtensions
} }
```` ````
* **Do** create a **configuration options** class by inheriting from the `ModelBuilderConfigurationOptions`. Example: * **Do** call `b.ConfigureByConvention();` for each entity mapping (as shown above).
* **Do** create a **configuration options** class by inheriting from the `AbpModelBuilderConfigurationOptions`. Example:
````C# ````C#
public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{ {
public IdentityModelBuilderConfigurationOptions() public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema) : base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)

4
docs/en/Best-Practices/MongoDB-Integration.md

@ -90,11 +90,11 @@ public static class AbpIdentityMongoDbContextExtensions
} }
``` ```
- **Do** create a **configuration options** class by inheriting from the `MongoModelBuilderConfigurationOptions`. Example: - **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example:
```c# ```c#
public class IdentityMongoModelBuilderConfigurationOptions public class IdentityMongoModelBuilderConfigurationOptions
: MongoModelBuilderConfigurationOptions : AbpMongoModelBuilderConfigurationOptions
{ {
public IdentityMongoModelBuilderConfigurationOptions() public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix) : base(AbpIdentityConsts.DefaultDbTablePrefix)

3
docs/en/Blob-Storing.md

@ -0,0 +1,3 @@
# Blog Storing
TODO

142
docs/en/Blog-Posts/2020-03-19 v2_3_Release/Post.md

@ -0,0 +1,142 @@
# ABP Framework v2.3.0 Has Been Released!
In the days of **coronavirus**, we have released **ABP Framework v2.3** and this post will explain **what's new** with this release and **what we've done** in the last two weeks.
## About the Coronavirus & Our Team
**We are very sad** about the coronavirus case. As [Volosoft](https://volosoft.com/) team, we have **remote workers** working in their home in different countries. Beginning from the last week, we've **completely started to work remotely** from home including our main office employees.
We believe in and pray for that the humanity will overcome this issue in a short time.
## About the Release Cycle
Beginning from the ABP v2.1.0, we have started to release feature versions once **in two weeks**, on Thursdays. This is the 3rd release after that decision and we see that it works fine for now and improved our agility.
We will continue to release **feature versions** (like v2.4, v2.5) in every two weeks. In addition, we may release **hotfix versions** (like v2.3.1, v2.3.2) whenever needed.
## What's New in ABP Framework v2.3.0
We've completed & merged **[104](https://github.com/abpframework/abp/milestone/30?closed=1) issues and pull requests** with **393 commits** in this two weeks development period.
I will introduce some new features and enhancements introduced with this release.
### React Native Mobile Application
We have finally completed the **react native mobile application**. It currently allows you to **login**, manage your **users** and **tenants**. It utilizes the same setting, authorization and localization systems of the ABP Framework.
A few screenshots from the application:
![mobile-ui](react-native-ui.png)
It doesn't have much functionality but it is a **perfect starting point** for your own mobile application since it is completely integrated to the backend and supports multi-tenancy.
### Angular TypeScript Proxy Generator
It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on he server side) and **model objects** (matches to [DTOs](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects) in the server side).
In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced:
* It generates a **big, single** .ts file which has some problems;
* It get **too large** when your application grows.
* It doesn't fit into the **[modular](https://docs.abp.io/en/abp/latest/Module-Development-Basics) approach** of the ABP framework.
* It creates a bit **ugly code**. We want to have a clean code (just like if we write manually).
* It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies.
So, we've decided to create an ABP CLI command to automatically generate the typescript client proxies ([#2222](https://github.com/abpframework/abp/issues/2222)) for your REST API developed with the ABP Framework.
It is easy to use. Just run the following command in the **root folder** of the angular application:
````bash
abp generate-proxy
````
It only creates proxies only for your own application's services. It doesn't create proxies for the services of the application modules you're using (by default). There are several options. See the [CLI documentation](https://docs.abp.io/en/abp/latest/CLI).
### CRUD Application Services for Entities with Composite Keys
` CrudAppService ` is a useful base class to create CRUD application services for your entities. But it doesn't support entities with **composite primary keys**. `AbstractKeyCrudAppService` is the new base class that is developed to support entities with composite primary keys. See [the documentation](https://docs.abp.io/en/abp/latest/Application-Services#abstractkeycrudappservice) for more.
### Add Source Code of the Modules
The application startup template comes with some [application modules](https://docs.abp.io/en/abp/latest/Modules/Index) **pre-installed** as **NuGet & NPM packages**. This have a few important advantages:
* You can **easily [upgrade](https://docs.abp.io/en/abp/latest/CLI#update)** these modules when a new version is available.
* Your solution becomes **cleaner**, so you can focus on your own code.
However, when you need to make **major customizations** for a depended module, it is not easy as its source code is in your applications. To solve this problem, we've introduces a new command to the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) that **replaces** NuGet packages with their **source code** in your solution. The usage is simple:
````bash
abp add-module --with-source-code
````
This command adds a module with source code or replaces with its source code if it is already added as package references.
> It is suggested to **save your changes** to your source control system before using this command since it makes a lot of changes in your source code.
In addition, we've documented how to customize depended modules without changing their source code (see the section below). It is suggested to use modules as packages to easily upgrade them in the future.
> Source code of the free modules are licensed under **MIT**, so you can freely change them and add into your solution.
### Switch to Preview
ABP Framework is rapidly evolving and we are frequently releasing new versions. However, if you want to follow it closer, you can use the **daily preview packages**.
We've created an ABP CLI command to easily **update to the latest preview packages** for your solution. Run the following command in the root folder of your solution:
````bash
abp switch-to-preview
````
It will change the versions of all ABP related NuGet and NPM packages. You can **switch back to the latest stable** when you want:
````bash
abp switch-to-stable
````
See the [ABP CLI document](https://docs.abp.io/en/abp/latest/CLI#switch-to-preview) fore more.
### Documentation Improvements
#### Extending/Customizing Depended Application Modules
We've created a huge documentation that explains how to customize a depended module without changing its source code. See [the documentation](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide).
In addition to the documentation, we've revised all the modules ([#3166](https://github.com/abpframework/abp/issues/3166)) to make their services easily extensible & customizable.
#### EF Core Migration Guide
We've recently created a guide to explain the migration system that is used by the ABP startup templates. [This guide](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations) also explains how to customize the migration structure, split your modules across multiple databases, reusing a module's table and son on.
#### Migration from the ASP.NET Boilerplate
If you have a solution built on the ASP.NET Boilerplate, we've [created a guide](https://docs.abp.io/en/abp/latest/AspNet-Boilerplate-Migration-Guide) that tries to help you if you want to migrate your solution to the new ABP Framework.
### Some Other Features
#### The Framework
* Add `IRepository.GetAsync` and `IRepository.FindAsync` methods ([#3184](https://github.com/abpframework/abp/issues/3148)).
#### Modules
* Get password & email address of the admin while creating a new tenant, for the tenant management module ([#3088](https://github.com/abpframework/abp/issues/3088)).
* Elastic search integrated full text search for the docs module ([#2901](https://github.com/abpframework/abp/pull/2901)).
* New Quartz background worker module ([#2762](https://github.com/abpframework/abp/issues/2762))
#### Samples
* Add multi-tenancy support to the microservice demo ([#3032](https://github.com/abpframework/abp/pull/3032)).
See [the release notes](https://github.com/abpframework/abp/releases/tag/2.3.0) for all feature, enhancement and bugfixes.
## What's Next?
We have the following goals for the next few months:
* Complete the **documentation and samples**, write more tutorials.
* Make the framework and existing modules more **customizable and extensible**.
* Integrate to **gRPC** & implement gRPC endpoint for pre-built modules ([#2882](https://github.com/abpframework/abp/issues/2882)).
* Create a **Blazor UI** for the ABP Framework & implement it for all the modules and startup templates ([#394](https://github.com/abpframework/abp/issues/394)).
* Add **new features** to pre-built modules and create new modules for the [ABP Commercial](https://commercial.abp.io/).
See [the GitHub milestones](https://github.com/abpframework/abp/milestones) for details.

BIN
docs/en/Blog-Posts/2020-03-19 v2_3_Release/abp-io-release-2-3-0-blog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

BIN
docs/en/Blog-Posts/2020-03-19 v2_3_Release/react-native-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

247
docs/en/Blog-Posts/2020-05-08 v2_7_Release/Post.md

@ -0,0 +1,247 @@
# ABP Framework v2.7.0 Has Been Released!
The **ABP Framework** & and the **ABP Commercial** v2.7 have been released. We hadn't created blog post for the 2.4, 2.4 and 2.6 releases, so this post will also cover **what's new** with these releases and **what we've done** in the last 2 months.
## About the Release Cycle & Development
Reminding that we had started to release a new minor feature version **in every two weeks**, generally on Thursdays. Our goal is to deliver new features as soon as possible.
We've completed & merged hundreds of issues and pull requests with **1,300+ commits** in the last 7-8 weeks, only for the ABP Framework repository. Daily commit counts are constantly increasing:
![github-contribution-graph](github-contribution-graph.png)
ABP.IO Platform is rapidly growing and we are getting more and more contributions from the community.
## What's New in the ABP Framework?
### Object Extending System
In the last few releases, we've mostly focused on providing ways to extend existing modules when you use them as NuGet/NPM Packages.
The Object Extending System allows module developers to create extensible modules and allows application developers to customize and extend a module easily.
For example, you can add two extension properties to the user entity of the identity module:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdate<IdentityUser>(options =>
{
options.AddOrUpdateProperty<string>("SocialSecurityNumber");
options.AddOrUpdateProperty<bool>("IsSuperUser");
}
);
````
It is easy to define validation rules for the properties:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserCreateDto, string>(
"SocialSecurityNumber",
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
}
);
});
````
You can even write custom code to validate the property. It automatically works for the objects those are parameters of an application service, controller or a page.
While extension properties of an entity are normally stored in a single JSON formatted field in the database table, you can easily configure to store a property as a table field using the EF Core mapping:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
options.MapEfCore(b => b.HasMaxLength(32));
}
);
````
See the [Object Extensions document](https://docs.abp.io/en/abp/latest/Object-Extensions) for details about this system.
See also the [Customizing the Existing Modules](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide) guide to learn all the possible customization options.
### Text Templating Package
[Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) is a new package introduced with the v2.7.0. Previously, [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) package had a similar functionality but it was limited, experimental and tightly coupled to the emailing.
The new text templating package allows you to define text based templates those can be easily localized and reused. You can define layout templates and share the layout from other templates.
We are currently using it for email sending. A module needs to send an email typically defines a template. Example:
````xml
<h3>{{L "PasswordReset"}}</h3>
<p>{{L "PasswordResetInfoInEmail"}}</p>
<div>
<a href="{{model.link}}">{{L "ResetMyPassword"}}</a>
</div>
````
This is a typical password reset email template.
* The template system is based on the open source [Scriban library](https://github.com/lunet-io/scriban). So it supports if conditions, loops and much more.
* `model` is used to pass data to the template (just like the ASP.NET Core MVC).
* `L` is a special function that localizes the given string.
It is typical to use the same layout for all emails. So, you can define a layout template. This is the standard layout template comes with the framework:
````xml
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{{content}}
</body>
</html>
````
A layout should have a `{{content}}` area to render the child content (just like the `RenderBody()` in the MVC).
It is very easy to override a template content by the final application to customize it.
Whenever you need to render a template, use the `ITemplateRenderer` service by providing the template name and a model. See the [text templating documentation](https://docs.abp.io/en/abp/latest/Text-Templating) for details. We've even created a UI for the ABP Commercial (see the related section below).
### Subscribing to the Exceptions
ABP Framework's [exception handling system](https://docs.abp.io/en/abp/latest/Exception-Handling) automatically handles exceptions and returns an appropriate result to the client. In some cases, you may want to have a callback that is notified whenever an exception occurs. In this way, for example, you can send an email or take any action based on the exception.
Just create a class derived from the `ExceptionSubscriber` class in your application:
````csharp
public class MyExceptionSubscriber : ExceptionSubscriber
{
public override async Task HandleAsync(ExceptionNotificationContext context)
{
//TODO...
}
}
````
See the [exception handling](https://docs.abp.io/en/abp/latest/Exception-Handling) document for more.
### Others
There are many minor features and enhancements made to the framework in the past releases. Here, a few ones:
* Added `AbpLocalizationOptions.DefaultResourceType` to set the default resource type for the application. In this way, the localization system uses the default resource whenever the resource was not specified. The latest application startup template already configures it, but you may want to set it for your existing applications.
* Added `IsEnabled` to permission definition. In this way, you can completely disable a permission and hide the related functionality from the application. This can be a way of feature switch for some applications. See [#3486](https://github.com/abpframework/abp/issues/3486) for usage.
* Added Dutch and German localizations to all the localization resources defined by the framework. Thanks to the contributors.
## What's New in the ABP Commercial
The goal of the [ABP Commercial](https://commercial.abp.io/) is to provide pre-build application functionalities, code generation tools, professional themes, advanced samples and premium support for ABP Framework based projects.
We are working on the ABP Commercial in the parallel to align with the ABP Framework features and provide more modules, theme options and tooling.
This section explains what's going on the ABP Commercial side.
### Module Entity Extension System
Module entity extension system is a higher level API that uses the object extension system (introduced above) and provides an easy way to add extension properties to existing entities. A new extension property easily automatically becomes a part of the HTTP API and the User Interface.
Example: Add a `SocialSecurityNumber` to the user entity of the identity module
````csharp
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>( //property type: string
"SocialSecurityNumber", //property name
property =>
{
//validation rules
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(
new StringLengthAttribute(64) {
MinimumLength = 4
}
);
//...other configurations for this property
}
);
});
});
````
With just such a configuration, the user interface will have the new property (on the table and on the create/edit forms):
![module-entity-extended-ui](module-entity-extended-ui.png)
The new property can be easily localized and validated. Currently, it supports primitive types like string, number and boolean, but we planned to add more advanced scenarios by the time (like navigation/lookup properties).
See the [Module Entity Extensions](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions) guide to learn how to use it and configure details.
#### Other Extension Points
There are also some other pre-defined points to customize and extend the user interface of a depended module:
* You can add a new action for an entity on the data table (left side on the picture below).
* You can add new buttons (or other controls) to the page toolbar (right side on the picture below).
* You can add custom columns to a data table.
![abp-commercial-ui-extensions](abp-commercial-ui-extensions.png)
See the [Customizing the Modules](https://docs.abp.io/en/commercial/latest/guides/customizing-modules) guide to learn all the possible ways to customize a depended module.
### Text Template Management Module
We are introducing a new module with the v2.7 release: [Text Template Management](https://docs.abp.io/en/commercial/latest/modules/text-template-management). It is basically used to edit text/email templates (introduced with the ABP Framework 2.7) on the user interface and save changed in the database.
A screenshot from the content editing for the password reset email template:
![text-template-content-ui](text-template-content-ui.png)
This module comes pre-installed when you create a new project.
### Entity History Views
Audit logging UI module now shows all the entity changes in the application with property change details.
![audit-log-entity-changes](audit-log-entity-changes.png)
You can also check history for an entity when you click to the actions menu for the entity:
![tenant-entity-changes](tenant-entity-changes.png)
### More Samples
We are creating more advanced sample applications built with the ABP Commercial. Easy CRM is one of them which will be available in a few days to the commercial customers.
Here, a screenshot from the Easy CRM dashboard:
![easy-crm](easy-crm.png)
It has accounts, contacts, product groups, products, orders and so on.
### New Modules
We continue to improve existing modules and creating new modules. In addition to the new [text template management](https://docs.abp.io/en/commercial/latest/modules/text-template-management) module introduced above;
* We've recently released a [payment module](https://commercial.abp.io/modules/Volo.Payment) that currently works with PayU and 2Checkout payment gateways. More gateways will be added by the time.
* We've created a simple [Twilio SMS integration](https://docs.abp.io/en/commercial/latest/modules/twilio-sms) module to send SMS over the Twilio.
* We are working on a **chat module** that is currently being developed and will be available in the next weeks.
* We are working on the **organization unit management** system for the identity module to create hierarchical organization units (domain layer will be open source & free).
More modules, theme and tooling options are being developed for the ABP Commercial and the ABP Framework.
## ABP Framework vs ABP Commercial
We ([Volosoft](https://volosoft.com/) - the core team behind the ABP.IO platform), are spending almost equal time on the ABP Framework and the ABP Commercial and we consider the ABP.IO platform as a whole.
[ABP Framework](https://abp.io/) provides all the infrastructure and application independent framework features to make you more productive, focus on your own business code and implement software development best practices. It provides you a well defined and comfortable development experience without repeating yourself.
[ABP Commercial](https://commercial.abp.io/) provides pre-built functionalities, themes and tooling to save your time if your requirements involve these functionalities in addition to the premium support for the framework and the pre-built modules.

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

214
docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md

@ -0,0 +1,214 @@
# ABP v2.8.0 Releases & Road Map
The **ABP Framework** & and the **ABP Commercial** v2.8 have been released. This post will cover **what's new** with these releases and the **middle-term road maps** for the projects.
## What's New in the ABP Framework 2.8?
You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.8.0). This post will only cover the important features/changes.
### SignalR Integration Package
We've published [a new package](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) to integrate SignalR to ABP framework based applications.
> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages those simplify the integration and usage.
See the [SignalR Integration document](https://docs.abp.io/en/abp/latest/SignalR-Integration) to start with the SignalR.
#### SignalR Demo Application
We've also created a simple chat application to demonstrate how to use it.
![signalr-chat-demo](signalr-chat-demo.png)
See [the source code of the application.](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo)
### Console Application Startup Template
The new console application template can be used to create a new console application that has the ABP Framework integrated.
Use ABP CLI to create a new console application, specifying the `console` as the `-t` (template) option:
````bash
abp new MyApp -t console
````
Thanks to the contribution of [@liangshiw](https://github.com/liangshiw) for this template.
### RTL Support for the MVC UI & Arabic Localization
[@kgamalseif](https://github.com/kgamalseif) has contributed a RTL implementation for the MVC UI which looks pretty fine:
![rtl-ui](rtl-ui.png)
He also localized all the framework and module resources. Thanks to him for this great contribution.
### Others
Some other highlights from this release:
* Converted HttpApi.Client packages of the modules to .netstandard 2.0 to be compatible with other kind of applications.
* Improved the object extensibility system to better handle UI, localization and validation.
* Implemented disabling background job execution for HangFire & Quartz intergrations.
* New JsTree integration package for the MVC UI.
* Moved all samples to the new [abp-samples](https://github.com/abpframework/abp-samples) repository and created an [index page](https://docs.abp.io/en/abp/latest/Samples/Index) to see all.
### Deprecations
* Deprecated the `app.UseMvcWithDefaultRouteAndArea()` and introduced the `app.UseConfiguredEndpoints()` (see [#3880](https://github.com/abpframework/abp/issues/3880)).
* Deprecated the `UsePostgreSql()` and introduced the `UseNpgsql()` for the [Volo.Abp.EntityFrameworkCore.PostgreSql](http://nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql) package. Switch to `UseNpgsql()` if you are using PostgreSQL.
Old methods are marked as `Obsolete` and will be removed in the next major versions.
## What's New in the ABP Commercial 2.8?
### The New Lepton Theme
We've completely revised [the lepton theme](https://commercial.abp.io/themes). See with different styles:
![lepton-themes](lepton-themes.gif)
Example screenshots from the language management page of the ABP Commercial:
![lepton-abp-default-theme](lepton-abp-default-theme.png)
(Default style UI)
![lepton-abp-material-theme](lepton-abp-material-theme.png)
(Material style UI)
[Create a demo](https://commercial.abp.io/demo) to test all the styles in live. You can change the style from the settings page.
### The New Chat Module
The first version of [the chat module](https://commercial.abp.io/modules/Volo.Chat) has been released with this version. It has only the MVC / Razor Pages UI. Angular UI is on the way.
![abp-chat-module](abp-chat-module.png)
It currently has a simple **real time text messaging** functionality. More features like group messaging, sending images/files are on the road map.
### Others
* Implemented [module entity extension](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions) system for the Angular UI. Also improved the system to better handle float/double/decimal, date, datetime, enum and boolean properties.
* Managing product groups on a tree view for the [EasyCRM sample application](https://docs.abp.io/en/commercial/latest/samples/easy-crm).
## About the Next Versions
We publish feature releases in **every 2 weeks**. So, the planned date of the next feature version is **June 04** and the version number is **2.9**. This (probably) will be the **last 2.x version** and the following version will be **3.0**.
### ABP Framework 2.9 & 3.0
#### Organization Unit System
Organization Unit system for the Identity module was intended to be released with 2.8, but unfortunately we couldn't be sure about the stability of the feature, so deferred it to the 2.9.
#### gRPC
We planned to work on a gRPC integrated example application. Then we will plan to create gRPC endpoints for all [pre-built modules](https://docs.abp.io/en/abp/latest/Modules/Index) and to [the startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Index). We want to use these endpoints with the new planned [Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) UI option (there is a [huge demand](https://github.com/abpframework/abp/issues/394) on a Blazor UI, we know). It doesn't mean that we'll finish the whole work in 3.0, but we are starting and will continue in 3.0+ versions.
#### Oracle with EF Core
We see that the people using Oracle with EF Core has some pains, independent from the ABP Framework. Because there is no stable & free Oracle provider for EF Core 3.1 yet. We only see the [Devart](https://www.devart.com/) has created a [paid package](https://www.nuget.org/packages/Devart.Data.Oracle.EFCore).
[@ebicoglu](https://github.com/ebicoglu) has [created a gist](https://gist.github.com/ebicoglu/9f364c7eff9d87315af0178866186401) to demonstrate how to use it. We [planned](https://github.com/abpframework/abp/issues/3983) to work on an integration package to make it even easier.
#### API Documentation
We are [working](https://github.com/abpframework/abp/issues/1184) to create an API documentation for the framework and build a CD pipeline to automatically publish it in every new release. This will make easier to explore the framework classes.
#### Sample Application: Using SignalR on a Tiered/Distributed system
Using SignalR on a distributed/microservice system can be tricky since the services are not connected to clients and can not directly call client functions from the server. One way to overcome this problem is using a distributed message bus (like RabbitMQ) that transfers the message from the service to the web application to deliver to the client.
We will create an example application and document it to demonstrate such an architecture and how it is easy by using the ABP Framework.
While this topic is not directly related to the ABP Framework and the problem is not unique to an ABP based application, we find useful to create such guides to developers.
#### And...
We will spend more time to write more documentation, implement performance improvements, make more tests, creating more extensibility points and so on.
### ABP Commercial 2.9 & 3.0
#### Organization Unit Management UI
In parallel to the OU system in the ABP Framework (mentioned above), we are creating a UI to manage the organization units, which will be released with the 2.9.
#### Angular UI for the Chat Module
The Chat Module (mentioned above) only has the ASP.NET Core MVC / Razor Pages UI now. We are working to create the Angular UI for this module.
#### New Module Idea: File Management
We are looking to create a File Management Module that is used to manage (upload/download) and share files between users. You may think as a very simple and lightweight Google Drive :).
#### Easy CRM Angular UI
[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm) is a sample application we've released with the previous version of the ABP Commercial. In this version, we've added more features to this application. In the next version, we will work on the Angular UI for it.
We found this application very useful since it is very close to a real world application compared to the simple [BookStore](https://docs.abp.io/en/commercial/latest/samples/index#book-store) example.
#### And...
We are working to improve current [modules](https://commercial.abp.io/modules), [themes](https://commercial.abp.io/themes) and the [tooling](https://commercial.abp.io/tools) to provide a more comfortable developer experience with the version 3.0.
## The Road Map
We are frequently asked about the road map of the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/). While we've answered to it in various platforms, with this release, we've adding road map pages for these products to their documentation:
* [ABP Framework Road Map](https://docs.abp.io/en/abp/latest/Road-Map)
* [ABP Commercial Road Map](https://docs.abp.io/en/commercial/latest/road-map)
I am also writing the road map here, in the following sections;
### ABP Framework Road Map
You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones).
While we will **continue to add other exciting features**, we will work on the following major items in the middle term:
* **gRPC integration** and implementation for all the pre-built modules.
* **Blazor UI** for the framework and all the pre-built modules.
* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition.
### ABP Commercial Road Map
We will work on the same items in parallel to to ABP Framework to implement them in the ABP Commercial side:
* gRPC integration
* Blazor UI
* .NET 5.0
In addition, we will be working on the following items in the middle term:
* A startup template to create microservice solutions (that has Ocelot, Redis, RabbitMQ, ElasticSearch, IdentityServer... etc. pre-integrated and configured).
* More module extension points.
* Dynamic dashboard system.
* Real-time notification system.
* Subscription and payment system for the SaaS module.
* More authentication options.
* New application modules (we have tens of module ideas and will share by the time - the "file management" announced above was one of them).
* New themes & theme styles (including public/corporate web site themes).
## BONUS: ABP.IO Platform Road Map
While the ABP Framework and the ABP Commercial are the fundamental components of the ABP.IO Platform, we want to create a much bigger platform to bring the .NET community together to create reusable modules, share knowledge, help each other by taking the advantage of the ABP Framework's unified and standardized development model.
So, we have new *.abp.io web site ideas I want to share with the community
#### market.abp.io
A platform that is used by developers/companies to publish their reusable application modules, themes, libraries and tools base don the ABP Framework. There will be free/open source and commercial products on this web site.
#### jobs.abp.io
We are getting too many emails from companies want to hire developers or other other companies to build their products based on the ABP.IO Platform. We, as [Volosoft](https://volosoft.com/), want to stay in the product side rather than customer basis projects. We generally lead them to experienced developers and companies.
We have a plan to create a web site to meet each side, so you can find developers for your projects or you find short or long term works to do.
## Follow the ABP!
Follow the social media accounts to get informed about happenings on the ABP.IO Platform:
* [@abpframework](https://twitter.com/abpframework): ABP Framework official Twitter account
* [@abpcommercial](https://twitter.com/abpcommercial): ABP Commercial official Twitter account

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

296
docs/en/Blog-Posts/2020-06-05 v2_9_Release/Post.md

@ -0,0 +1,296 @@
# ABP Framework v2.9 Has Been Released
The **ABP Framework** & and the **ABP Commercial** version 2.9 have been released, which are the last versions before v3.0! This post will cover **what's new** with these this release.
## What's New with the ABP Framework 2.9?
You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.9.0). This post will only cover the important features/changes.
### Pre-Compiling Razor Pages
Pre-built pages (for [the application modules](https://docs.abp.io/en/abp/latest/Modules/Index)) and view components were compiling on runtime until this version. Now, they are pre-compiled and we've measured that the application startup time (especially for the MVC UI) has been reduced more than 50%. In other words, it is **two-times faster** than the previous version. The speed change also effects when you visit a page for the first time.
Here, a test result for the startup application template with v2.8 and v.2.9:
````
### v2.8
2020-06-04 22:59:04.891 +08:00 [INF] Starting web host.
2020-06-04 22:59:07.662 +08:00 [INF] Now listening on: https://localhost:44391
2020-06-04 22:59:17.315 +08:00 [INF] Request finished in 7756.6218ms 200 text/html;
Total: 12.42s
### v2.9
2020-06-04 22:59:13.720 +08:00 [INF] Starting web host.
2020-06-04 22:59:16.639 +08:00 [INF] Now listening on: https://localhost:44369
2020-06-04 22:59:18.957 +08:00 [INF] Request finished in 1780.5461ms 200 text/html;
Total: 5.24s
````
You do nothing to get the benefit of the new approach. [Overriding UI pages/components](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface) are also just working as before. We will be working on more performance improvements in the v3.0.
### Organization Unit System
[The Identity Module](https://docs.abp.io/en/abp/latest/Modules/Identity) now has the most requested feature: Organization Units!
Organization unit system is used to create a hierarchical organization tree in your application. You can then use this organization tree to authorize data and functionality in your application.
The documentation will come soon...
### New Blob Storing Package
We've created a new [Blob Storing package](https://www.nuget.org/packages/Volo.Abp.BlobStoring) to store arbitrary binary objects. It is generally used to store the content of the files in your application. This package provides an abstraction, so any application or [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics) can save and retrieve files independent from the actual storing provider.
There are two storage provider currently implemented:
* [Volo.Abp.BlobStoring.FileSystem](https://www.nuget.org/packages/Volo.Abp.BlobStoring.FileSystem) package stores objects/files in the local file system.
* [Volo.Abp.BlobStoring.Database](https://github.com/abpframework/abp/tree/dev/modules/blob-storing-database) module stores objects/files in a database. It currently supports [Entity Framework Core](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) (so, you can use [any relational DBMS](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS)) and [MongoDB](https://docs.abp.io/en/abp/latest/MongoDB).
[Azure BLOB provider](https://github.com/abpframework/abp/issues/4098) will be available with v3.0. You can request other cloud providers or contribute yourself on the [GitHub repository](https://github.com/abpframework/abp/issues/new).
One of the benefits of the blob storing system is that it allows you to create multiple containers (each container is a blob storage) and use different storage providers for each container.
**Example: Use the default container to save and get a byte array**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer _container;
public MyService(IBlobContainer container)
{
_container = container;
}
public async Task FooAsync()
{
//Save a BLOB
byte[] bytes = GetBytesFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", bytes);
//Retrieve a BLOB
bytes = await _container.GetAllBytesAsync("my-unique-blob-name");
}
}
````
It can work with `byte[]` and `Stream` objects.
**Example: Use a typed (named) container to save and get a stream**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer<TestContainer> _container;
public MyService(IBlobContainer<TestContainer> container)
{
_container = container;
}
public async Task FooAsync()
{
//Save a BLOB
Stream stream = GetStreamFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", stream);
//Retrieve a BLOB
stream = await _container.GetAsync("my-unique-blob-name");
}
}
````
`TestContainer` is an empty class that has no purpose than identifying the container:
````csharp
[BlobContainerName("test")] //specifies the name of the container
public class TestContainer
{
}
````
A typed (named) container can be configured to use a different storing provider than the default one. It is a good practice to always use a typed container while developing re-usable modules, so the final application can configure provider for this container without effecting the other containers.
**Example: Configure the File System provider for the `TestContainer`**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<TestContainer>(configuration =>
{
configuration.UseFileSystem(fileSystem =>
{
fileSystem.BasePath = "C:\\MyStorageFolder";
});
});
});
````
See the [blob storing documentation](https://docs.abp.io/en/abp/latest/Blob-Storing) for more information.
### Oracle Integration Package for Entity Framework Core
We've created an [integration package for Oracle](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart), so you can easily switch to the Oracle for the EF Core. It is tested for the framework and pre-built modules.
[See the documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Oracle) to start using the Oracle integration package.
### Automatically Determining the Database Provider
When you develop a **reusable application module** with EF Core integration, you generally want to develop your module **DBMS independent**. However, there are minor (sometimes major) differences between different DBMSs. If you perform a custom mapping based on the DBMS, you can now use `ModelBuilder.IsUsingXXX()` extension methods:
````csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Phone>(b =>
{
//...
if (modelBuilder.IsUsingPostgreSql()) //Check if using PostgreSQL!
{
b.Property(x => x.Number).HasMaxLength(20);
}
else
{
b.Property(x => x.Number).HasMaxLength(32);
}
});
}
````
Beside the stupid example above, you can configure your mapping however you need!
### ABP CLI: Translate Command
`abp translate` is a new command that simplifies to translate [localization](https://docs.abp.io/en/abp/latest/Localization) files when you have multiple JSON localization files in a source control repository.
The main purpose of this command is to **translate the ABP Framework** localization files (since the [abp repository](https://github.com/abpframework/abp) has tens of localization files to be translated in different folders).
It is appreciated if you use this command to translate the framework resources **for your mother language**.
See [the documentation](https://docs.abp.io/en/abp/latest/CLI#translate) to learn how to use it. Also see [the contribution guide](https://docs.abp.io/en/abp/latest/Contribution/Index).
### The New Virtual File System Explorer Module
Thanks to [@liangshiw](https://github.com/liangshiw) created and contributed a new module to explore files in the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). It works for MVC UI and shows all the virtual files in the application. Example screenshots:
![virtual-file-explorer-1](virtual-file-explorer-1.png)
![virtual-file-explorer-2](virtual-file-explorer-2.png)
[See the documentation](https://docs.abp.io/en/abp/latest/Modules/Virtual-File-Explorer) to learn how to use it.
### Sample Application: SignalR with Tiered Architecture
Implementing SignalR in a distributed/tiered architecture can be challenging. We've created a sample application that demonstrate how to implement it using the [SignalR integration](https://docs.abp.io/en/abp/latest/SignalR-Integration) and the [distributed event bus](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus) system easily.
See [the source code](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo) of the sample solution.
**An article is on the road** that will deeply explain the solution. Follow the [@abpframework](https://twitter.com/abpframework) Twitter account.
![signalr-tiered-demo](signalr-tiered-demo.png)
*A picture from the article that shows the communication diagram of the solution*
### About gRPC
We've created a sample application to show how to create and consume gRPC endpoints in your ABP based applications.
See [the source code](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) on GitHub.
We were planning to create gRPC endpoints for all the pre-built application modules, but we see that ASP.NET Core gRPC integration is not mature enough and doesn't support some common deployment scenarios yet. So, deferring this to the next versions ([see this comment](https://github.com/abpframework/abp/issues/2882#issuecomment-633080242) for more). However, it is pretty standard if you want to use gRPC in your applications. ABP Framework has no issue with gRPC. Just check the [sample application](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo).
### Others
* [Time zone system](https://github.com/abpframework/abp/pull/3933) to support different time zones for an application.
* Support for [virtual path deployment](https://github.com/abpframework/abp/issues/4089) on IIS.
* RTL support for the Angular UI.
See the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.9.0) for others updates.
## What's New with the ABP Commercial 2.9
In addition to all the features coming with the ABP Framework, the ABP Commercial has additional features with this release, as always. This section covers the [ABP Commercial](https://commercial.abp.io/) highlights in the version 2.9.
### Organization Unit Management UI
We've created the UI for manage organization units, their members and roles for the ABP Commercial [Identity Module](https://commercial.abp.io/modules/Volo.Identity.Pro):
![organization-units](organization-units.png)
OU management is available for both of the MVC (Razor Pages) and the Angular user interfaces.
> If you are using the MVC / Razor Pages UI and upgrading from an earlier version, you need to add `@volo/identity` (version 2.9.0) NPM package to your `package.json` of the web project.
### Chat Module Angular UI
We had introduced a new [chat module](https://commercial.abp.io/modules/Volo.Chat) in the previous version, which was only supporting the ASP.NET Core MVC / Razor Pages UI. Now, it has also an Angular UI option.
![abp-chat-module](abp-chat-module.png)
*A screenshot from the chat module - two users are sending messages to each other*
### Easy CRM Angular UI
Easy CRM is a sample application that is built on the ABP Commercial to provide a relatively complex application to the ABP Commercial customers. In the version 2.7, we have lunched it with MVC / Razor Pages UI. With the 2.9 version, we are releasing the Angular UI for the Easy CRM application.
![easy-crm](easy-crm.png)
*A screenshot from the "Order Details" page of the Easy CRM application.*
See the [Easy CRM document](https://docs.abp.io/en/commercial/latest/samples/easy-crm) to learn how to download and run it.
### Module Code Generation for the ABP Suite
[ABP Suite](https://commercial.abp.io/tools/suite) is a tool that's main feature is to [generate code](https://docs.abp.io/en/commercial/latest/abp-suite/generating-crud-page) for complete CRUD functionality for an entity, from database to the UI layer.
![suite](suite.png)
*A screenshot from the ABP Suite: Define the properties of a new entity and let it to create the application code for you!*
It was working only for [the application template](https://docs.abp.io/en/commercial/latest/startup-templates/application/index) until this release. Now, it supports to generate code for the [module projects](https://docs.abp.io/en/commercial/latest/startup-templates/module/index) too. That's a great way to create reusable application modules by taking the power of the code generation.
In addition to this main feature, we added many minor enhancements on the ABP Suite in this release.
> Notice: Generating code for the module template is currently in beta. Please inform us if you find any bug.
### Lepton Theme
[Lepton Theme](https://commercial.abp.io/themes) is the commercial theme we've developed for the ABP Commercial;
* It is 100% bootstrap compatible - so you don't write theme specific HTML!
* Provides different kind of styles - you see the material style in the picture below.
* Provides different kind of layouts (side/top menu, fluid/boxed layout...).
* It is lightweight, responsive and modern.
* And... it is upgradeable with no cost! You just update a NuGet/NPM package to get the new features.
We've create its own web site: [http://leptontheme.com/](http://leptontheme.com/)
You can view all the components together, independent from an application:
![lepton-theme](lepton-theme.png)
This web site is currently in a very early stage. We will be documenting and improving this web site to be a reference for your development and explore the features of the theme.
### Coming Soon: The File management Module
Based on the new blob storing system (introduced above), we've started to build a file management module that is used to manage (navigate/upload/download) a hierarchical file system on your application and share the files between your users and with your customers.
We plan to release the initial version with the ABP Commercial v3.0 and continue to improve it with the subsequent releases.
## About the Next Version: 3.0
We have added many new features with the [v2.8](https://blog.abp.io/abp/ABP-v2.8.0-Releases-%26-Road-Map) and v2.9. In the next version, we will completely focus on the **documentation, performance improvements** and and other enhancements as well as bug fixes.
For a long time, we were releasing a new feature version in every 2 weeks. We will continue to this approach after v3.0. But, as an exception to the v3.0, the development cycle will be ~4 weeks. **The planned release date for the v3.0 is the July 1, 2020**.
## Bonus: Articles!
Beside developing our products, our team are constantly writing articles/tutorials on various topics. You may want to check the latest articles:
* [ASP.NET Core 3.1 WebHook Implementation Using Pub/Sub](https://volosoft.com/blog/ASP.NET-CORE-3.1-Webhook-Implementation-Using-Pub-Sub)
* [Using Azure Key Vault with ASP.NET Core](https://volosoft.com/blog/Using-Azure-Key-Vault-with-ASP.NET-Core)

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/suite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

169
docs/en/CLI.md

@ -18,11 +18,42 @@ dotnet tool update -g Volo.Abp.Cli
## Commands ## Commands
Here, the list of all available commands before explaining their details:
* **`help`**: Shows help on the usage of the ABP CLI.
* **`new`**: Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md).
* **`update`**: Automatically updates all ABP related NuGet and NPM packages in a solution.
* **`add-package`**: Adds an ABP package to a project.
* **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution.
* **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints on the server.
* **`switch-to-preview`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution.
* **`switch-to-stable`**: Switches to the latest stable versions of the ABP related packages on a solution.
* **`translate`**: Simplifies to translate localization files when you have multiple JSON [localization](Localization.md) files in a source control repository.
* **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password.
* **`logout`**: Logouts from your computer if you've authenticated before.
### help
Shows basic usages of the ABP CLI.
Usage:
````bash
abp help [command-name]
````
Examples:
````bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
````
### new ### new
Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md).
Basic usage: Usage:
````bash ````bash
abp new <solution-name> [options] abp new <solution-name> [options]
@ -40,7 +71,7 @@ abp new Acme.BookStore
#### Options #### Options
* `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates: * `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates:
* `app` (default): [Application template](Startup-Templates/Application.md). Additional options: * **`app`** (default): [Application template](Startup-Templates/Application.md). Additional options:
* `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks: * `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks:
* `mvc`: ASP.NET Core MVC. There are some additional options for this template: * `mvc`: ASP.NET Core MVC. There are some additional options for this template:
* `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
@ -48,14 +79,41 @@ abp new Acme.BookStore
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `none`: Without UI. There are some additional options for this template: * `none`: Without UI. There are some additional options for this template:
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks:
* `ef`: Entity Framework Core. * `none`: no mobile application.
* `mongodb`: MongoDB. * `react-native`: React Native.
* `module`: [Module template](Startup-Templates/Module.md). Additional options: * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* **`module`**: [Module template](Startup-Templates/Module.md). Additional options:
* `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI). * `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI).
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory. * `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version. * `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.
* `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`). * `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. You can set your own connection string if you don't want to use the default. Be aware that the default database provider is `SQL Server`, therefore you can only enter connection string for SQL Server!
* `--local-framework-ref --abp-path`: keeps local references to projects instead of replacing with NuGet package references.
### update
Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related NuGet and NPM packages in a solution or project to the latest versions.
Usage:
````bash
abp update [options]
````
* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions.
* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions.
#### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
* `--npm`: Only updates NPM packages.
* `--nuget`: Only updates NuGet packages.
* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default
* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default.
### add-package ### add-package
@ -90,7 +148,7 @@ Adds a [multi-package application module](Modules/Index) to a solution by findin
> A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module. > A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Basic usage: Usage
````bash ````bash
abp add-module <module-name> [options] abp add-module <module-name> [options]
@ -111,24 +169,28 @@ abp add-module Volo.Blogging
* `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder. * `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder.
* `--with-source-code`: Add source code of the module instead of NuGet/NPM packages. * `--with-source-code`: Add source code of the module instead of NuGet/NPM packages.
### update ### generate-proxy
Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related NuGet and NPM packages in a solution or project to the latest versions. Generates client proxies for your HTTP APIs to make easy to consume your services from the client side. Before running `generate-proxy` command, your host must be up and running.
Usage: Usage:
````bash ````bash
abp update [options] abp generate-proxy [options]
```` ````
* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions.
* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions.
#### Options #### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. * `--apiUrl` or `-a`: Specifies the root URL of the HTTP API. The default value is being retrieved from the `environment.ts` file for the Angular application. Make sure your host is up and running before running `abp generate-proxy`.
* `--npm`: Only updates NPM packages. * `--ui` or `-u`: Specifies the UI framework. Default value is `angular` and it is the only UI framework supported for now. Creates TypeScript code.
* `--nuget`: Only updates NuGet packages. * `--module` or `-m`: Specifies the module name. Default module name is `app`, which indicates your own application (you typically want this since every module is responsible to maintain its own client proxies). Set `all` for to generate proxies for all the modules.
Example usage with the options:
````bash
abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all
````
### switch-to-preview ### switch-to-preview
@ -139,9 +201,10 @@ Usage:
````bash ````bash
abp switch-to-preview [options] abp switch-to-preview [options]
```` ````
#### Options #### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory. `--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory.
### switch-to-stable ### switch-to-stable
@ -154,17 +217,67 @@ abp switch-to-stable [options]
```` ````
#### Options #### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory. `--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory.
### translate
Simplifies to translate [localization](Localization.md) files when you have multiple JSON [localization](Localization.md) files in a source control repository.
* This command will create a unified json file based on the reference culture.
* It searches all the localization `JSON` files in the current directory and all subdirectories (recursively). Then creates a single file (named `abp-translation.json` by default) that includes all the entries need to be translated.
* Once you translate the entries in this file, you can then apply your changes to the original localization files using the `--apply` command.
> The main purpose of this command is to translate ABP Framework localization files (since the [abp repository](https://github.com/abpframework/abp) has tens of localization files to be translated in different directories).
#### Creating the Translation File
First step is to create the unified translation file:
````bash
abp translate -c <culture> [options]
````
Example:
````bash
abp translate -c de-DE
````
This command created the unified translation file for the `de-DE` (German) culture.
##### Additional Options
* `--reference-culture` or `-r`: Default `en`. Specifies the reference culture.
* `--output` or `-o`: Output file name. Default `abp-translation.json`.
* `--all-values` or `-all`: Include all keys to translate. By default, the unified translation file only includes the missing texts for the target culture. Specify this parameter if you may need to revise the values already translated before.
#### Applying Changes
Once you translate the entries in the unified translation file, you can apply your changes to the original localization files using the `--apply` parameter:
````bash
abp translate --apply # apply all changes
abp translate -a # shortcut for --apply
````
Then review changes on your source control system to be sure that it has changed the proper files and send a Pull Request if you've translated ABP Framework resources. Thank you in advance for your contribution.
##### Additional Options
* `--file` or `-f`: Default: `abp-translation.json`. The translation file (use only if you've used the `--output` option before).
### login ### login
Some features of the CLI requires to be logged in to abp.io platform. To login with your username write: Some features of the CLI requires to be logged in to abp.io platform. To login with your username write:
```bash ```bash
abp login <username> abp login <username> # Asks password separately
abp login <username> -p <password> # Specify the password as a parameter
``` ```
Notice that, a new login with an already active session, will kill the previous session and creates a new one. > Using `-p` parameter might not be safe if someone is watching your screen :) It can be useful for automation purposes.
A new login with an already active session overwrites the previous session.
### logout ### logout
@ -174,19 +287,3 @@ Logs you out by removing the session token from your computer.
abp logout abp logout
``` ```
### help
Writes basic usage information of the CLI.
Usage:
````bash
abp help [command-name]
````
Examples:
````bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
````

19
docs/en/Contribution/Index.md

@ -41,15 +41,22 @@ A new language is published after these minimum translations have been completed
ABP framework has a flexible [localization system](../Localization.md). You can create localized user interfaces for your own application. ABP framework has a flexible [localization system](../Localization.md). You can create localized user interfaces for your own application.
In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. In addition to that, the framework and the [pre-build modules](https://docs.abp.io/en/abp/latest/Modules/Index) have localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).
#### Using the "abp translate" command
This is the recommended approach, since it automatically finds all missing texts for a specific culture and lets you to translate in one place.
* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. * Clone the [ABP repository](https://github.com/abpframework/abp/) from Github.
* Create a new file for the target language for a localization text (json) file (near to the en.json file). * Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before.
* Copy all texts from the en.json file. * Run `abp translate -c <culture-name>` command for your language in the root folder of the abp repository. For example, use `abp translate -c fr` for French. Check [this document](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes) to find the culture code for your language.
* Translate the texts. * This command creates a file in the same folder, named `abp-translation.json`. Open this file in your favorite editor and fill the missing text values.
* Send pull request on Github. * Once you done the translation, use `abp translate -a` command to apply changes to the related files.
* Send a pull request on GitHub.
#### Manual Translation
ABP is a modular framework. So there are many localization text resource, one per module. To find all .json files, you can search for "en.json" after cloning the repository. You can also check [this list](Localization-Text-Files.md) for a list of localization text files. If you want to make a change on a specific resource file, you can find the file yourself, make the necessary change (or create a new file for your language) and send a pull request on GitHub.
### Blog Posts & Tutorials ### Blog Posts & Tutorials

31
docs/en/Contribution/Localization-Text-Files.md

@ -1,31 +0,0 @@
## Localization Text Files
Here, a list of localization text files for anyone wants to contribute to localization of the texts coming from the framework. We will keep this list up to date:
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json
* https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json
* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json
* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/en.json
* https://github.com/abpframework/abp/blob/master/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
* https://github.com/abpframework/abp/tree/master/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json
* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json
* https://github.com/abpframework/abp/tree/master/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json
* https://github.com/abpframework/abp/tree/master/samples/DashboardDemo/src/DashboardDemo.Domain.Shared/Localization/DashboardDemo/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/en.json
* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json
* https://github.com/abpframework/abp/tree/master/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json

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

@ -4,7 +4,7 @@ In some cases, you may want to add some additional properties (and database fiel
## Extra Properties ## Extra Properties
[Extra properties](Entities.md) is a way of storing some additional data on an entity without changing it. The entity should implement the `IHasExtraProperties` interface to allow it. All the aggregate root entities defined in the pre-built modules implement the `IHasExtraProperties` interface, so you can store extra properties on these entities. [Extra properties](Entities.md) is a way of storing some additional data on an entity without changing it. The entity should implement the `IHasExtraProperties` interface to allow it. All the aggregate root entities defined in the pre-built modules implement the `IHasExtraProperties` interface, so you can store extra properties on these objects.
Example: Example:
@ -25,11 +25,38 @@ Extra properties are stored as a single `JSON` formatted string value in the dat
See the [entities document](Entities.md) for more about the extra properties system. See the [entities document](Entities.md) for more about the extra properties system.
> It is possible to perform a **business logic** based on the value of an extra property. You can **override** a service method and get or set the value as shown above. Overriding services will be discussed below. > It is possible to perform a **business logic** based on the value of an extra property. You can [override a service method](Customizing-Application-Modules-Overriding-Services.md), then get or set the value as shown above.
## Creating a New Entity Maps to the Same Database Table/Collection ## Entity Extensions (EF Core)
As mentioned above, all extra properties of an entity are stored as a single JSON object in the database table. This is not so natural especially when you want to;
* Create **indexes** and **foreign keys** for an extra property.
* Write **SQL** or **LINQ** using the extra property (search table by the property value, for example).
* Creating your **own entity** maps to the same table, but defines an extra property as a **regular property** in the entity (see the [EF Core migration document](Entity-Framework-Core-Migrations.md) for more).
To overcome the difficulties described above, ABP Framework entity extension system for the Entity Framework Core that allows you to use the same extra properties API defined above, but store a desired property as a separate field in the database table.
While using the extra properties approach is **easy to use** and suitable for some scenarios, it has some drawbacks described in the [entities document](Entities.md). Assume that you want to add a `SocialSecurityNumber` to the `IdentityUser` entity of the [Identity Module](Modules/Identity.md). You can use the `ObjectExtensionManager`:
````csharp
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
"SocialSecurityNumber",
b => { b.HasMaxLength(32); }
);
````
* You provide the `IdentityUser` as the entity name, `string` as the type of the new property, `SocialSecurityNumber` as the property name (also, the field name in the database table).
* You also need to provide an action that defines the database mapping properties using the [EF Core Fluent API](https://docs.microsoft.com/en-us/ef/core/modeling/entity-properties).
> This code part must be executed before the related `DbContext` used. The [application startup template](Startup-Templates/Application.md) defines a static class named `YourProjectNameEfCoreEntityExtensionMappings`. You can define your extensions in this class to ensure that it is executed in the proper time. Otherwise, you should handle it yourself.
Once you define an entity extension, you then need to use the standard [Add-Migration](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#add-migration) and [Update-Database](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#update-database) commands of the EF Core to create a code first migration class and update your database.
You can then use the same extra properties system defined in the previous section to manipulate the property over the entity.
## Creating a New Entity Maps to the Same Database Table/Collection
Another approach can be **creating your own entity** mapped to **the same database table** (or collection for a MongoDB database). Another approach can be **creating your own entity** mapped to **the same database table** (or collection for a MongoDB database).
@ -146,4 +173,5 @@ public class MyDistributedIdentityUserCreatedEventHandler :
## See Also ## See Also
* [Customizing the Existing Modules](Customizing-Application-Modules-Guide.md) * [Migration System for the EF Core](Entity-Framework-Core-Migrations.md)
* [Customizing the Existing Modules](Customizing-Application-Modules-Guide.md)

6
docs/en/Customizing-Application-Modules-Guide.md

@ -1,6 +1,6 @@
# Customizing the Existing Modules # Customizing the Existing Modules
ABP Framework provides was designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application. ABP Framework has been designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application.
For example, you can **re-use** the [Identity Management Module](Modules/Identity.md) to add user, role and permission management to your application. The [application startup template](Startup-Templates/Application.md) already comes with Identity and some other modules **pre-installed**. For example, you can **re-use** the [Identity Management Module](Modules/Identity.md) to add user, role and permission management to your application. The [application startup template](Startup-Templates/Application.md) already comes with Identity and some other modules **pre-installed**.
@ -21,7 +21,7 @@ This approach has the following benefits:
However, there is a drawback: However, there is a drawback:
* You may not able to **customize** the module source code as it is in your own solution. * You may not able to **customize** the module because the module source is not in your solution.
This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations. This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations.
@ -59,4 +59,4 @@ Also, see the following documents:
* See [the localization document](Localization.md) to learn how to extend existing localization resources. * See [the localization document](Localization.md) to learn how to extend existing localization resources.
* See [the settings document](Settings.md) to learn how to change setting definitions of a depended module. * See [the settings document](Settings.md) to learn how to change setting definitions of a depended module.
* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module. * See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module.

156
docs/en/Customizing-Application-Modules-Overriding-Services.md

@ -10,7 +10,7 @@ You have different options can be used based on your requirement those will be e
## Replacing an Interface ## Replacing an Interface
If given service defines an interface, like the `IdentityUserAppService` class implements the `IIdentityAppService`, you can re-implement the same interface and replace the current implementation by your class. Example: If given service defines an interface, like the `IdentityUserAppService` class implements the `IIdentityUserAppService`, you can re-implement the same interface and replace the current implementation by your class. Example:
````csharp ````csharp
public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency
@ -59,7 +59,9 @@ In most cases, you will want to change one or a few methods of the current imple
### Example: Overriding an Application Service ### Example: Overriding an Application Service
````csharp ````csharp
//[RemoteService(IsEnabled = false)] // If you use dynamic controller feature you can disable remote service. Prevent creating duplicate controller for the application service.
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService))]
public class MyIdentityUserAppService : IdentityUserAppService public class MyIdentityUserAppService : IdentityUserAppService
{ {
//... //...
@ -105,30 +107,33 @@ You could completely **re-write** the entire business logic for a user creation
[ExposeServices(typeof(IdentityUserManager))] [ExposeServices(typeof(IdentityUserManager))]
public class MyIdentityUserManager : IdentityUserManager public class MyIdentityUserManager : IdentityUserManager
{ {
public MyIdentityUserManager( public MyIdentityUserManager(
IdentityUserStore store, IdentityUserStore store,
IOptions<IdentityOptions> optionsAccessor, IIdentityRoleRepository roleRepository,
IPasswordHasher<IdentityUser> passwordHasher, IIdentityUserRepository userRepository,
IEnumerable<IUserValidator<IdentityUser>> userValidators, IOptions<IdentityOptions> optionsAccessor,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators, IPasswordHasher<IdentityUser> passwordHasher,
ILookupNormalizer keyNormalizer, IEnumerable<IUserValidator<IdentityUser>> userValidators,
IdentityErrorDescriber errors, IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
IServiceProvider services, ILookupNormalizer keyNormalizer,
ILogger<IdentityUserManager> logger, IdentityErrorDescriber errors,
ICancellationTokenProvider cancellationTokenProvider IServiceProvider services,
) : base( ILogger<IdentityUserManager> logger,
store, ICancellationTokenProvider cancellationTokenProvider) :
optionsAccessor, base(store,
passwordHasher, roleRepository,
userValidators, userRepository,
passwordValidators, optionsAccessor,
keyNormalizer, passwordHasher,
errors, userValidators,
services, passwordValidators,
logger, keyNormalizer,
cancellationTokenProvider) errors,
{ services,
} logger,
cancellationTokenProvider)
{
}
public override async Task<IdentityResult> CreateAsync(IdentityUser user) public override async Task<IdentityResult> CreateAsync(IdentityUser user)
{ {
@ -161,6 +166,105 @@ Check the [localization system](Localization.md) to learn how to localize the er
Overriding controllers, framework services, view component classes and any other type of classes registered to dependency injection can be overridden just like the examples above. Overriding controllers, framework services, view component classes and any other type of classes registered to dependency injection can be overridden just like the examples above.
## Extending Data Transfer Objects
**Extending [entities](Entities.md)** is possible as described in the [Extending Entities document](Customizing-Application-Modules-Extending-Entities.md). In this way, you can add **custom properties** to entities and perform **additional business logic** by overriding the related services as described above.
It is also possible to extend Data Transfer Objects (**DTOs**) used by the application services. In this way, you can get extra properties from the UI (or client) and return extra properties from the service.
### Example
Assuming that you've already added a `SocialSecurityNumber` as described in the [Extending Entities document](Customizing-Application-Modules-Extending-Entities.md) and want to include this information while getting the list of users from the `GetListAsync` method of the `IdentityUserAppService`.
You can use the [object extension system](Object-Extensions.md) to add the property to the `IdentityUserDto`. Write this code inside the `YourProjectNameDtoExtensions` class comes with the application startup template:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserDto, string>(
"SocialSecurityNumber"
);
````
This code defines a `SocialSecurityNumber` to the `IdentityUserDto` class as a `string` type. That's all. Now, if you call the `/api/identity/users` HTTP API (which uses the `IdentityUserAppService` internally) from a REST API client, you will see the `SocialSecurityNumber` value in the `extraProperties` section.
````json
{
"totalCount": 1,
"items": [{
"tenantId": null,
"userName": "admin",
"name": "admin",
"surname": null,
"email": "admin@abp.io",
"emailConfirmed": false,
"phoneNumber": null,
"phoneNumberConfirmed": false,
"twoFactorEnabled": false,
"lockoutEnabled": true,
"lockoutEnd": null,
"concurrencyStamp": "b4c371a0ab604de28af472fa79c3b70c",
"isDeleted": false,
"deleterId": null,
"deletionTime": null,
"lastModificationTime": "2020-04-09T21:25:47.0740706",
"lastModifierId": null,
"creationTime": "2020-04-09T21:25:46.8308744",
"creatorId": null,
"id": "8edecb8f-1894-a9b1-833b-39f4725db2a3",
"extraProperties": {
"SocialSecurityNumber": "123456789"
}
}]
}
````
Manually added the `123456789` value to the database for now.
All pre-built modules support extra properties in their DTOs, so you can configure easily.
### Definition Check
When you [define](Customizing-Application-Modules-Extending-Entities.md) an extra property for an entity, it doesn't automatically appear in all the related DTOs, because of the security. The extra property may contain a sensitive data and you may not want to expose it to the clients by default.
So, you need to explicitly define the same property for the corresponding DTO if you want to make it available for the DTO (as just done above). If you want to allow to set it on user creation, you also need to define it for the `IdentityUserCreateDto`.
If the property is not so secure, this can be tedious. Object extension system allows you to ignore this definition check for a desired property. See the example below:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
options.MapEfCore(b => b.HasMaxLength(32));
options.CheckPairDefinitionOnMapping = false;
}
);
````
This is another approach to define a property for an entity (`ObjectExtensionManager` has more, see [its document](Object-Extensions.md)). This time, we set `CheckPairDefinitionOnMapping` to false to skip definition check while mapping entities to DTOs and vice verse.
If you don't like this approach but want to add a single property to multiple objects (DTOs) easier, `AddOrUpdateProperty` can get an array of types to add the extra property:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<string>(
new[]
{
typeof(IdentityUserDto),
typeof(IdentityUserCreateDto),
typeof(IdentityUserUpdateDto)
},
"SocialSecurityNumber"
);
````
### About the User Interface
This system allows you to add extra properties to entities and DTOs and execute custom business code, however it does nothing related to the User Interface.
See [Overriding the User Interface](Customizing-Application-Modules-Overriding-User-Interface.md) guide for the UI part.
## How to Find the Services? ## How to Find the Services?
[Module documents](Modules/Index.md) includes the list of the major services they define. In addition, you can investigate [their source code](https://github.com/abpframework/abp/tree/dev/modules) to explore all the services. [Module documents](Modules/Index.md) includes the list of the major services they define. In addition, you can investigate [their source code](https://github.com/abpframework/abp/tree/dev/modules) to explore all the services.

5
docs/en/Customizing-Application-Modules-Overriding-User-Interface.md

@ -3,7 +3,4 @@
You may want to override a page, a component, a JavaScript, CSS or an image file of your depended module. Overriding the UI completely depends on the UI framework you're using. Select the UI framework to continue: You may want to override a page, a component, a JavaScript, CSS or an image file of your depended module. Overriding the UI completely depends on the UI framework you're using. Select the UI framework to continue:
* [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md) * [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md)
* [Angular](UI/Angular/Customization-User-Interface.md) * [Angular](UI/Angular/Customization-User-Interface.md)

21
docs/en/Dapper.md

@ -1,22 +1,26 @@
# Dapper Integration # Dapper Integration
Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface. Dapper is a light-weight and simple database provider. The major benefit of using Dapper is writing T-SQL queries. It provides some extension methods for `IDbConnection` interface.
Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository<TDbContext>` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository<TDbContext>` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper.
These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md). `IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md).
## Installation ## Installation
Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer): `Volo.Abp.Dapper` is the main nuget package for the Dapper integration.
You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper
Install it to your project (for a layered application, to your data/infrastructure layer):
```shell ```shell
Install-Package Volo.Abp.Dapper Install-Package Volo.Abp.Dapper
``` ```
Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md): Then add `AbpDapperModule` module dependency (with `DependsOn` attribute) to your [module](Module-Development-Basics.md):
````C# ````C#
using Volo.Abp.Dapper; using Volo.Abp.Dapper;
@ -34,9 +38,10 @@ namespace MyCompany.MyProject
## Implement Dapper Repository ## Implement Dapper Repository
The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods. The following code creates the `PersonRepository`, which requires EF Core's `DbContext` (MyAppDbContext).
You can inject `PersonDapperRepository` to your services for your database operations.
`DbConnection` and `DbTransaction` are from the `DapperRepository` base class. `DbConnection` and `DbTransaction` comes from the `DapperRepository` base class.
```C# ```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency

3
docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md

@ -0,0 +1,3 @@
# Distributed Event Bus RabbitMQ Integration
TODO

11
docs/en/Entities.md

@ -373,16 +373,19 @@ So, you can directly use the `ExtraProperties` property to use the dictionary A
The way to store this dictionary in the database depends on the database provider you're using. The way to store this dictionary in the database depends on the database provider you're using.
* For [Entity Framework Core](Entity-Framework-Core.md), it is stored in a single `ExtraProperties` field as a `JSON` string. Serializing to `JSON` and deserializing from the `JSON` are automatically done by the ABP Framework using the [value conversions](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions) system of the EF Core. * For [Entity Framework Core](Entity-Framework-Core.md), here are two type of configurations;
* By default, it is stored in a single `ExtraProperties` field as a `JSON` string (that means all extra properties stored in a single database table field). Serializing to `JSON` and deserializing from the `JSON` are automatically done by the ABP Framework using the [value conversions](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions) system of the EF Core.
* If you want, you can use the `ObjectExtensionManager` to define a separate table field for a desired extra property. Properties those are not configured through the `ObjectExtensionManager` will continue to use a single `JSON` field as described above. This feature is especially useful when you are using a pre-built [application module](Modules/Index.md) and want to [extend its entities](Customizing-Application-Modules-Extending-Entities.md). See the [EF Core integration document](Entity-Framework-Core.md) to learn how to use the `ObjectExtensionManager`.
* For [MongoDB](MongoDB.md), it is stored as a **regular field**, since MongoDB naturally supports this kind of [extra elements](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) system. * For [MongoDB](MongoDB.md), it is stored as a **regular field**, since MongoDB naturally supports this kind of [extra elements](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) system.
### Discussion for the Extra Properties ### Discussion for the Extra Properties
Extra Properties system is especially useful if you are using a **re-usable module** that defines an entity inside and you want to get/set some data related to this entity in an easy way. You normally **don't need** to this system for your own entities, because it has the following drawbacks: Extra Properties system is especially useful if you are using a **re-usable module** that defines an entity inside and you want to get/set some data related to this entity in an easy way.
* It is **not fully type safe**. You normally **don't need** to this system for your own entities, because it has the following drawbacks:
* It is **not fully type safe** since it works with strings as property names.
* It is **not easy to [auto map](Object-To-Object-Mapping.md)** these properties from/to other objects. * It is **not easy to [auto map](Object-To-Object-Mapping.md)** these properties from/to other objects.
* It **doesn't create fields** in the database table for EF Core, so it will not be easy to create indexes or search/order by this field in the database side.
### Extra Properties Behind Entities ### Extra Properties Behind Entities

111
docs/en/Entity-Framework-Core-Migrations.md

@ -6,7 +6,7 @@ This document begins by **introducing the default structure** provided by [the a
### Source Code ### Source Code
You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp/tree/dev/samples/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code. You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code.
## About the EF Core Code First Migrations ## About the EF Core Code First Migrations
@ -93,7 +93,7 @@ From the database point of view, there are three important projects those will b
This project has the `DbContext` class (`BookStoreDbContext` for this sample) of your application. This project has the `DbContext` class (`BookStoreDbContext` for this sample) of your application.
**Every module uses its own `DbContext` class** to access to the database. Likewise, your application has its own `DbContext`. You typically use this `DbContext` in your application code (in your custom [repositories](Repositories.md) if you follow the best practices). It is almost an empty `DbContext` since your application don't have any entities at the beginning, except the pre-defined `AppUser` entity: **Every module uses its own `DbContext` class** to access to the database. Likewise, your application has its own `DbContext`. You typically use this `DbContext` in your application code (in your [repositories](Repositories.md) if you follow the best practices). It is almost an empty `DbContext` since your application don't have any entities at the beginning, except the pre-defined `AppUser` entity:
````csharp ````csharp
[ConnectionStringName("Default")] [ConnectionStringName("Default")]
@ -117,15 +117,15 @@ public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
builder.Entity<AppUser>(b => builder.Entity<AppUser>(b =>
{ {
//Sharing the same table "AbpUsers" with the IdentityUser //Sharing the same Users table with the IdentityUser
b.ToTable("AbpUsers"); b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users");
//Configure base properties
b.ConfigureByConvention(); b.ConfigureByConvention();
b.ConfigureAbpUser(); b.ConfigureAbpUser();
//Moved customization of the "AbpUsers" table to an extension method /* Configure mappings for your additional properties
b.ConfigureCustomUserProperties(); * Also see the MyProjectNameEntityExtensions class
*/
}); });
/* Configure your own tables/entities inside the ConfigureBookStore method */ /* Configure your own tables/entities inside the ConfigureBookStore method */
@ -188,12 +188,6 @@ public class BookStoreMigrationsDbContext : AbpDbContext<BookStoreMigrationsDbCo
builder.ConfigureFeatureManagement(); builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement(); builder.ConfigureTenantManagement();
/* Configure customizations for entities from the modules included */
builder.Entity<IdentityUser>(b =>
{
b.ConfigureCustomUserProperties();
});
/* Configure your own tables/entities inside the ConfigureBookStore method */ /* Configure your own tables/entities inside the ConfigureBookStore method */
builder.ConfigureBookStore(); builder.ConfigureBookStore();
} }
@ -274,7 +268,7 @@ In this way, the mapping configuration of a module can be shared between `DbCont
You may want to **reuse a table** of a depended module in your application. In this case, you have two options: You may want to **reuse a table** of a depended module in your application. In this case, you have two options:
1. You can **directly use the entity** defined by the module. 1. You can **directly use the entity** defined by the module (you can still [extend the entity](Customizing-Application-Modules-Extending-Entities.md) in some level).
2. You can **create a new entity** mapping to the same database table. 2. You can **create a new entity** mapping to the same database table.
###### Use the Entity Defined by a Module ###### Use the Entity Defined by a Module
@ -376,10 +370,8 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<AppRole>(b => builder.Entity<AppRole>(b =>
{ {
b.ToTable("AbpRoles"); b.ToTable("AbpRoles");
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.Title).HasMaxLength(128);
b.ConfigureCustomRoleProperties();
}); });
... ...
@ -395,69 +387,45 @@ We added the following lines:
````csharp ````csharp
builder.Entity<AppRole>(b => builder.Entity<AppRole>(b =>
{ {
b.ToTable("AbpRoles"); b.ToTable("AbpRoles");
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.Title).HasMaxLength(128);
b.ConfigureCustomRoleProperties();
}); });
```` ````
* It maps to the same `AbpRoles` table shared with the `IdentityRole` entity. * It maps to the same `AbpRoles` table shared with the `IdentityRole` entity.
* `ConfigureByConvention()` configures the standard/base properties (like `TenantId`) and recommended to always call it. * `ConfigureByConvention()` configures the standard/base properties (like `TenantId`) and recommended to always call it.
`ConfigureCustomRoleProperties()` has not exists yet. Define it inside the `BookStoreDbContextModelCreatingExtensions` class (near to your `DbContext` in the `.EntityFrameworkCore` project): You've configured the custom property for your `DbContext` that is used by your application on the runtime. We also need to configure the `MigrationsDbContext`.
````csharp
public static void ConfigureCustomRoleProperties<TRole>(this EntityTypeBuilder<TRole> b)
where TRole : class, IEntity<Guid>
{
b.Property<string>(nameof(AppRole.Title)).HasMaxLength(128);
}
````
* This method only defines the **custom properties** of your entity.
* Unfortunately, we can not utilize the fully **type safety** here (by referencing the `AppRole` entity). The best we can do is to use the `Title` name as type safe. This is because of EF Core migration system can not map two unrelated entity classes to the same database table.
You've configured the custom property for your `DbContext` used by your application on the runtime. We also need to configure the `MigrationsDbContext`. Instead of directly changing the `MigrationsDbContext`, we should use the entity extension system of the ABP Framework. Find the `YourProjectNameEntityExtensions` class in the `.EntityFrameworkCore` project of your solution (`BookStoreEntityExtensions` for this example) and change it as shown below:
Open the `MigrationsDbContext` (`BookStoreMigrationsDbContext` for this example) and change as shown below:
````csharp ````csharp
protected override void OnModelCreating(ModelBuilder builder) public static class MyProjectNameEntityExtensions
{ {
base.OnModelCreating(builder); private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
/* Include modules to your migration db context */ public static void Configure()
...
/* Configure customizations for entities from the modules included */
//CONFIGURE THE CUSTOM ROLE PROPERTIES
builder.Entity<IdentityRole>(b =>
{ {
b.ConfigureCustomRoleProperties(); OneTimeRunner.Run(() =>
}); {
ObjectExtensionManager.Instance
... .MapEfCoreProperty<IdentityRole, string>(
"Title",
/* Configure your own tables/entities inside the ConfigureBookStore method */ builder => { builder.HasMaxLength(64); }
);
builder.ConfigureBookStore(); });
}
} }
```` ````
Only added the following lines: > Instead of hard-coded "Title" string, we suggest to use `nameof(AppRole.Title)` or use a constant string.
````csharp `ObjectExtensionManager` is used to add properties to existing entities. Since `ObjectExtensionManager.Instance` is a static instance (singleton), we should call it once. `OneTimeRunner` is a simple utility class defined by the ABP Framework.
builder.Entity<IdentityRole>(b =>
{ See the [EF Core integration documentation](Entity-Framework-Core.md) for more about the entity extension system.
b.ConfigureCustomRoleProperties();
});
````
In this way, we re-used the extension method that is used to configure custom property mappings for the role. But, this time, did the same customization for the `IdentityRole` entity. > We've repeated a similar database mapping code, like `HasMaxLength(128)`, in both classes.
Now, you can add a new EF Core database migration using the standard `Add-Migration` command in the Package Manager Console (remember to select `.EntityFrameworkCore.DbMigrations` as the Default Project in the PMC and make sure that the `.Web` project is still the startup project): Now, you can add a new EF Core database migration using the standard `Add-Migration` command in the Package Manager Console (remember to select `.EntityFrameworkCore.DbMigrations` as the Default Project in the PMC and make sure that the `.Web` project is still the startup project):
@ -536,7 +504,7 @@ Instead of creating a new entity class to add a custom property, you can use the
###### Using the ExtraProperties ###### Using the ExtraProperties
All entities derived from the `AggregateRoot ` class can store name-value pairs in their `ExtraProperties` property, which is a `Dictionary<string, object>` serialized to JSON in the database table. So, you can add values to this dictionary and query again without changing the entity. All entities derived from the `AggregateRoot ` class can store name-value pairs in their `ExtraProperties` property (because they implement the `IHasExtraProperties` interface), which is a `Dictionary<string, object>` serialized to JSON in the database table. So, you can add values to this dictionary and query again without changing the entity.
For example, you can store query the title Property inside an `IdentityRole` instead of creating a new entity. Example: For example, you can store query the title Property inside an `IdentityRole` instead of creating a new entity. Example:
@ -553,16 +521,13 @@ public class IdentityRoleExtendingService : ITransientDependency
public async Task<string> GetTitleAsync(Guid id) public async Task<string> GetTitleAsync(Guid id)
{ {
var role = await _identityRoleRepository.GetAsync(id); var role = await _identityRoleRepository.GetAsync(id);
return role.GetProperty<string>("Title"); return role.GetProperty<string>("Title");
} }
public async Task SetTitleAsync(Guid id, string newTitle) public async Task SetTitleAsync(Guid id, string newTitle)
{ {
var role = await _identityRoleRepository.GetAsync(id); var role = await _identityRoleRepository.GetAsync(id);
role.SetProperty("Title", newTitle); role.SetProperty("Title", newTitle);
await _identityRoleRepository.UpdateAsync(role); await _identityRoleRepository.UpdateAsync(role);
} }
} }
@ -575,12 +540,22 @@ In this way, you can easily attach any type of value to an entity of a depended
* All the extra properties are stored as **a single JSON object** in the database. They are not stored as new table fields, as you may expect. Creating database table indexes and using SQL queries against these properties will be harder compared to simple table fields. * All the extra properties are stored as **a single JSON object** in the database. They are not stored as new table fields, as you may expect. Creating database table indexes and using SQL queries against these properties will be harder compared to simple table fields.
* Property names are strings, so they are **not type safe**. It is recommended to define constants for these kind of properties to prevent typo errors. * Property names are strings, so they are **not type safe**. It is recommended to define constants for these kind of properties to prevent typo errors.
###### Using the Entity Extensions System
Entity extension system solves the main problem of the extra properties: It can store an extra property in a **standard table field** in the database.
All you need to do is to use the `ObjectExtensionManager` to define the extra property as explained above, in the `AppRole` example. Then you can continue to use the same `GetProperty` and `SetProperty` methods defined above to get/set the related property on the entity, but this time stored as a separate field in the database.
See the [entity extension system](Customizing-Application-Modules-Extending-Entities.md) for details.
###### Creating a New Table ###### Creating a New Table
Instead of creating a new entity and mapping to the same table, you can also create **your own table** to store your properties. You typically duplicate some values of the original entity. For example, you can add `Name` field to your own table which is a duplication of the `Name` field in the original table. Instead of creating a new entity and mapping to the same table, you can also create **your own table** to store your properties. You typically duplicate some values of the original entity. For example, you can add `Name` field to your own table which is a duplication of the `Name` field in the original table.
In this case, you don't deal with migration problems, however you need to deal with the problems of data duplication. When the duplicated value changes, you should reflect the same change in your table. You can use local or distributed [event bus](Event-Bus.md) to subscribe to the change events for the original entity. This is the recommended way of depending on a microservice's data from another microservice, especially if they have separate physical databases (you can search on the web on data sharing on a microservice design, it is a wide topic to cover here). In this case, you don't deal with migration problems, however you need to deal with the problems of data duplication. When the duplicated value changes, you should reflect the same change in your table. You can use local or distributed [event bus](Event-Bus.md) to subscribe to the change events for the original entity. This is the recommended way of depending on a microservice's data from another microservice, especially if they have separate physical databases (you can search on the web on data sharing on a microservice design, it is a wide topic to cover here).
> See the "[extending entities](Customizing-Application-Modules-Extending-Entities.md)" guide for more details on extending entities, including data duplication and synchronization tips.
#### Discussion of an Alternative Scenario: Every Module Manages Its Own Migration Path #### Discussion of an Alternative Scenario: Every Module Manages Its Own Migration Path
As mentioned before, `.EntityFrameworkCore.DbMigrations` merges all the database mappings of all the modules (plus your application's mappings) to create a unified migration path. As mentioned before, `.EntityFrameworkCore.DbMigrations` merges all the database mappings of all the modules (plus your application's mappings) to create a unified migration path.
@ -909,4 +884,4 @@ This document explains how to split your databases and manage your database migr
## Source Code ## Source Code
You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp/tree/dev/samples/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code. You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code.

17
docs/en/Entity-Framework-Core-MySQL.md

@ -25,23 +25,6 @@ MySQL connection strings are different than SQL Server connection strings. So, c
You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure.
## Change the Migrations DbContext
MySQL DBMS has some slight differences than the SQL Server. Some module database mapping configuration (especially the field lengths) causes problems with MySQL. For example, some of the the [IdentityServer module](Modules/IdentityServer.md) tables has such problems and it provides an option to configure the fields based on your DBMS.
The startup template contains a *YourProjectName*MigrationsDbContext which is responsible to maintain and migrate the database schema. This DbContext basically calls extension methods of the depended modules to configure their database tables.
Open the *YourProjectName*MigrationsDbContext and change the `builder.ConfigureIdentityServer();` line as shown below:
````csharp
builder.ConfigureIdentityServer(options =>
{
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});
````
Then `ConfigureIdentityServer()` method will set the field lengths to not exceed the MySQL limits. Refer to related module documentation if you have any problem while creating or executing the database migrations.
## Re-Generate the Migrations ## Re-Generate the Migrations
The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core Migrations depend on the selected DBMS provider. So, changing the DBMS provider will cause the migration fails. The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core Migrations depend on the selected DBMS provider. So, changing the DBMS provider will cause the migration fails.

62
docs/en/Entity-Framework-Core-Oracle.md

@ -0,0 +1,62 @@
# Switch to EF Core Oracle Provider
This document explains how to switch to the **Oracle** database provider for **[the application startup template](Startup-Templates/Application.md)** which comes with SQL Server provider pre-configured.
> This document uses a paid library of [Devart](https://www.devart.com/dotconnect/oracle/) company, because it is the only library for Oracle that supports EF Core 3.x.
## Replace the Volo.Abp.EntityFrameworkCore.SqlServer Package
`.EntityFrameworkCore` project in the solution depends on the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package. Remove this package and add the same version of the [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) package.
## Replace the Module Dependency
Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`).
## UseOracle()
Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files:
* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project.
* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project.
In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block
```csharp
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
```
with this one
```csharp
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>)
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle
(
configuration.GetConnectionString("Default")
);
```
> Depending on your solution structure, you may find more code files need to be changed.
## Change the Connection Strings
Oracle connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/oracle/ ) for details of Oracle connection string options.
You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure.
## Re-Generate the Migrations
The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default.
EF Core Migrations depend on the selected DBMS provider. Changing the DBMS provider, may not work with the existing migrations.
* Delete the `Migrations` folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution.
* Run `Add-Migration "Initial"` on the Package Manager Console window (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console).
This will scaffold a new migration for Oracle.
Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data.
## Run the Application
It is ready. Just run the application and enjoy coding.

17
docs/en/Entity-Framework-Core-Other-DBMS.md

@ -6,6 +6,7 @@ ABP framework provides **integration packages** for some common DBMSs to make th
* [MySQL](Entity-Framework-Core-MySQL.md) * [MySQL](Entity-Framework-Core-MySQL.md)
* [PostgreSQL](Entity-Framework-Core-PostgreSQL.md) * [PostgreSQL](Entity-Framework-Core-PostgreSQL.md)
* [Oracle](Entity-Framework-Core-Oracle.md)
* [SQLite](Entity-Framework-Core-SQLite.md) * [SQLite](Entity-Framework-Core-SQLite.md)
However, you can configure your DBMS provider **without** these integration packages. While using the integration package is always recommended (it also makes standard for the depended version across different modules), you can do it manually if there is no integration package for your DBMS provider. However, you can configure your DBMS provider **without** these integration packages. While using the integration package is always recommended (it also makes standard for the depended version across different modules), you can do it manually if there is no integration package for your DBMS provider.
@ -91,4 +92,18 @@ Run the `.DbMigrator` project to create the database and seed the initial data.
## Run the Application ## Run the Application
It is ready. Just run the application and enjoy coding. It is ready. Just run the application and enjoy coding.
## DBMS restrictions
Different DBMS may have some restrictions, such as the maximum length of field names, index length, etc.
The module may provide some built-in solutions. You can configure it via `ModelBuilder`. eg: `Identity Server` module.
```csharp
builder.ConfigureIdentityServer(options =>
{
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});
```
Related discussions: https://github.com/abpframework/abp/issues/1920

4
docs/en/Entity-Framework-Core-PostgreSQL.md

@ -10,9 +10,9 @@ This document explains how to switch to the **PostgreSQL** database provider for
Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCorePostgreSqlModule)` (also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.PostgreSql;`). Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCorePostgreSqlModule)` (also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.PostgreSql;`).
## UsePostgreSql() ## UseNpgsql()
Find `UseSqlServer()` call in *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project and replace with `UsePostgreSql()`. Find `UseSqlServer()` call in *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project and replace with `UseNpgsql()`.
Find `UseSqlServer()` call in *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project and replace with `UseNpgsql()`. Find `UseSqlServer()` call in *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project and replace with `UseNpgsql()`.

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

@ -58,6 +58,53 @@ namespace MyCompany.MyProject
} }
```` ````
### About the EF Core Fluent Mapping
The [application startup template](Startup-Templates/Application.md) has been configured to use the [EF Core fluent configuration API](https://docs.microsoft.com/en-us/ef/core/modeling/) to map your entities to your database tables.
You can still use the **data annotation attributes** (like `[Required]`) on the properties of your entity while the ABP documentation generally follows the **fluent mapping API** approach. It is up to you.
ABP Framework has some **base entity classes** and **conventions** (see the [entities document](Entities.md)) and it provides some useful **extension methods** to configure the properties inherited from the base entity classes.
#### ConfigureByConvention Method
`ConfigureByConvention()` is the main extension method that **configures all the base properties** and conventions for your entities. So, it is a **best practice** to call this method for all your entities, in your fluent mapping code.
**Example**: Assume that you've a `Book` entity derived from `AggregateRoot<Guid>` base class:
````csharp
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
}
````
You can override the `OnModelCreating` method in your `DbContext` and configure the mapping as shown below:
````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
//Always call the base method
base.OnModelCreating(builder);
builder.Entity<Book>(b =>
{
b.ToTable("Books");
//Configure the base properties
b.ConfigureByConvention();
//Configure other properties (if you are using the fluent API)
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
````
* Calling `b.ConfigureByConvention()` is important here to properly **configure the base properties**.
* You can configure the `Name` property here or you can use the **data annotation attributes** (see the [EF Core document](https://docs.microsoft.com/en-us/ef/core/modeling/entity-properties)).
> While there are many extension methods to configure your base properties, `ConfigureByConvention()` internally calls them if necessary. So, it is enough to call it.
### Configure the Connection String Selection ### Configure the Connection String Selection
If you have multiple databases in your application, you can configure the connection string name for your DbContext using the `[ConnectionStringName]` attribute. Example: If you have multiple databases in your application, you can configure the connection string name for your DbContext using the `[ConnectionStringName]` attribute. Example:
@ -225,7 +272,7 @@ public override async Task DeleteAsync(
} }
```` ````
### Access to the EF Core API ## Access to the EF Core API
In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example:
@ -251,9 +298,60 @@ public class BookService
> Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the DbContext. This breaks encapsulation, but this is what you want in that case. > Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the DbContext. This breaks encapsulation, but this is what you want in that case.
### Advanced Topics ## Extra Properties & Object Extension Manager
Extra Properties system allows you to set/get dynamic properties to entities those implement the `IHasExtraProperties` interface. It is especially useful when you want to add custom properties to the entities defined in an [application module](Modules/Index.md), when you use the module as package reference.
By default, all the extra properties of an entity are stored as a single `JSON` object in the database.
#### Set Default Repository Classes Entity extension system allows you to to store desired extra properties in separate fields in the related database table. For more information about the extra properties & the entity extension system, see the following documents:
* [Customizing the Application Modules: Extending Entities](Customizing-Application-Modules-Extending-Entities.md)
* [Entities](Entities.md)
This section only explains the EF Core related usage of the `ObjectExtensionManager`.
### ObjectExtensionManager.Instance
`ObjectExtensionManager` implements the singleton pattern, so you need to use the static `ObjectExtensionManager.Instance` to perform all the operations.
### MapEfCoreProperty
`MapEfCoreProperty` is a shortcut extension method to define an extension property for an entity and map to the database.
**Example**: Add `Title` property (database field) to the `IdentityRole` entity:
````csharp
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityRole, string>(
"Title",
builder => { builder.HasMaxLength(64); }
);
````
If the related module has implemented this feature (by using the `ConfigureEfCoreEntity` explained below), then the new property is added to the model. Then you need to run the standard `Add-Migration` and `Update-Database` commands to update your database to add the new field.
>`MapEfCoreProperty` method must be called before using the related `DbContext`. It is a static method. The best way is to use it in your application as earlier as possible. The application startup template has a `YourProjectNameEntityExtensions` class that is safe to use this method inside.
### ConfigureEfCoreEntity
If you are building a reusable module and want to allow application developers to add properties to your entities, you can use the `ConfigureEfCoreEntity` extension method in your entity mapping. However, there is a shortcut extension method `ConfigureObjectExtensions` that can be used while configuring the entity mapping:
````csharp
builder.Entity<YourEntity>(b =>
{
b.ConfigureObjectExtensions();
//...
});
````
> If you call `ConfigureByConvention()` extension method (like `b.ConfigureByConvention()` for this example), ABP Framework internally calls the `ConfigureObjectExtensions` method. It is a **best practice** to use the `ConfigureByConvention()` method since it also configures database mapping for base properties by convention.
See the "*ConfigureByConvention Method*" section above for more information.
## Advanced Topics
### Set Default Repository Classes
Default generic repositories are implemented by `EfCoreRepository` class by default. You can create your own implementation and use it for all the default repository implementations. Default generic repositories are implemented by `EfCoreRepository` class by default. You can create your own implementation and use it for all the default repository implementations.
@ -299,7 +397,7 @@ context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
}); });
``` ```
#### Set Base DbContext Class or Interface for Default Repositories ### Set Base DbContext Class or Interface for Default Repositories
If your DbContext inherits from another DbContext or implements an interface, you can use that base class or interface as DbContext for default repositories. Example: If your DbContext inherits from another DbContext or implements an interface, you can use that base class or interface as DbContext for default repositories. Example:
@ -331,7 +429,7 @@ public class BookRepository : EfCoreRepository<IBookStoreDbContext, Book, Guid>,
One advantage of using an interface for a DbContext is then it will be replaceable by another implementation. One advantage of using an interface for a DbContext is then it will be replaceable by another implementation.
#### Replace Other DbContextes ### Replace Other DbContextes
Once you properly define and use an interface for DbContext, then any other implementation can replace it using the `ReplaceDbContext` option: Once you properly define and use an interface for DbContext, then any other implementation can replace it using the `ReplaceDbContext` option:
@ -344,3 +442,7 @@ 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. 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.
## See Also
* [Entities](Entities.md)

52
docs/en/Exception-Handling.md

@ -1,4 +1,4 @@
## Exception Handling # Exception Handling
ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application. ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application.
@ -7,7 +7,7 @@ ABP provides a built-in infrastructure and offers a standard model for handling
* Provides a configurable way to **localize** exception messages. * Provides a configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions. * Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions.
### Automatic Exception Handling ## Automatic Exception Handling
`AbpExceptionFilter` handles an exception if **any of the following conditions** are met: `AbpExceptionFilter` handles an exception if **any of the following conditions** are met:
@ -17,7 +17,7 @@ ABP provides a built-in infrastructure and offers a standard model for handling
If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client. If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client.
#### Error Message Format ### Error Message Format
Error Message is an instance of the `RemoteServiceErrorResponse` class. The simplest error JSON has a **message** property as shown below: Error Message is an instance of the `RemoteServiceErrorResponse` class. The simplest error JSON has a **message** property as shown below:
@ -83,11 +83,11 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic. `AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic.
#### Logging ### Logging
Caught exceptions are automatically logged. Caught exceptions are automatically logged.
##### Log Level #### Log Level
Exceptions are logged with the `Error` level by default. The Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example: Exceptions are logged with the `Error` level by default. The Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example:
@ -100,7 +100,7 @@ public class MyException : Exception, IHasLogLevel
} }
```` ````
##### Self Logging Exceptions #### Self Logging Exceptions
Some exception types may need to write additional logs. They can implement the `IExceptionWithSelfLogging` if needed. Example: Some exception types may need to write additional logs. They can implement the `IExceptionWithSelfLogging` if needed. Example:
@ -116,7 +116,7 @@ public class MyException : Exception, IExceptionWithSelfLogging
> `ILogger.LogException` extension methods is used to write exception logs. You can use the same extension method when needed. > `ILogger.LogException` extension methods is used to write exception logs. You can use the same extension method when needed.
### Business Exceptions ## Business Exceptions
Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception. Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception.
@ -145,11 +145,11 @@ Volo.Qa:010002
* You can **directly throw** a `BusinessException` or **derive** your own exception types from it when needed. * You can **directly throw** a `BusinessException` or **derive** your own exception types from it when needed.
* All properties are optional for the `BusinessException` class. But you generally set either `ErrorCode` or `Message` property. * All properties are optional for the `BusinessException` class. But you generally set either `ErrorCode` or `Message` property.
### Exception Localization ## Exception Localization
One problem with throwing exceptions is how to localize error messages while sending it to the client. ABP offers two models and their variants. One problem with throwing exceptions is how to localize error messages while sending it to the client. ABP offers two models and their variants.
#### User Friendly Exception ### User Friendly Exception
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client. If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client.
@ -192,7 +192,7 @@ Then the localization text can be:
* The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class. * The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class.
#### Using Error Codes ### Using Error Codes
`UserFriendlyException` is fine, but it has a few problems in advanced usages: `UserFriendlyException` is fine, but it has a few problems in advanced usages:
@ -230,7 +230,7 @@ throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer);
* Throwing any exception implementing the `IHasErrorCode` interface behaves the same. So, the error code localization approach is not unique to the `BusinessException` class. * Throwing any exception implementing the `IHasErrorCode` interface behaves the same. So, the error code localization approach is not unique to the `BusinessException` class.
* Defining localized string is not required for an error message. If it's not defined, ABP sends the default error message to the client. It does not use the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface). * Defining localized string is not required for an error message. If it's not defined, ABP sends the default error message to the client. It does not use the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface).
##### Using Message Parameters #### Using Message Parameters
If you have a parameterized error message, then you can set it with the exception's `Data` property. For example: If you have a parameterized error message, then you can set it with the exception's `Data` property. For example:
@ -265,7 +265,7 @@ Then the localized text can contain the `UserName` parameter:
* `WithData` can be chained with more than one parameter (like `.WithData(...).WithData(...)`). * `WithData` can be chained with more than one parameter (like `.WithData(...).WithData(...)`).
### HTTP Status Code Mapping ## HTTP Status Code Mapping
ABP tries to automatically determine the most suitable HTTP status code for common exception types by following these rules: ABP tries to automatically determine the most suitable HTTP status code for common exception types by following these rules:
@ -280,7 +280,7 @@ ABP tries to automatically determine the most suitable HTTP status code for comm
The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. The default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended as needed. The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. The default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended as needed.
#### Custom Mappings ### Custom Mappings
Automatic HTTP status code determination can be overrided by custom mappings. For example: Automatic HTTP status code determination can be overrided by custom mappings. For example:
@ -291,12 +291,32 @@ services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>
}); });
```` ````
### Built-In Exceptions ## Subscribing to the Exceptions
It is possible to be informed when the ABP Framework **handles an exception**. It automatically **logs** all the exceptions to the standard [logger](Logging.md), but you may want to do more.
In this case, create a class derived from the `ExceptionSubscriber` class in your application:
````csharp
public class MyExceptionSubscriber : ExceptionSubscriber
{
public override async Task HandleAsync(ExceptionNotificationContext context)
{
//TODO...
}
}
````
The `context` object contains necessary information about the exception occurred.
> You can have multiple subscribers, each gets a copy of the exception. Exceptions thrown by your subscriber is ignored (but still logged).
## Built-In Exceptions
Some exception types are automatically thrown by the framework: Some exception types are automatically thrown by the framework:
- `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See authorization document (TODO: link) for more. - `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See [authorization](Authorization.md) for more.
- `AbpValidationException` is thrown if the input of the current request is not valid. See validation document (TODO: link) for more. - `AbpValidationException` is thrown if the input of the current request is not valid. See [validation](Validation.md) for more.
- `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md). - `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md).
You can also throw these type of exceptions in your code (although it's rarely needed). You can also throw these type of exceptions in your code (although it's rarely needed).

128
docs/en/Getting-Started-Angular-Template.md

@ -1,126 +1,8 @@
## Getting Started With the Angular Application Template # Getting Started with the Startup Templates
This tutorial explains how to create a new Angular application using the startup template, configure and run it. See the following tutorials to learn how to get started with the ABP Framework using the pre-built application startup templates:
### Creating a New Project * [Getting Started With the ASP.NET Core MVC / Razor Pages UI](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Getting Started with the Angular UI](Getting-Started?UI=NG&DB=EF&Tiered=No)
This tutorial uses **ABP CLI** to create a new project. See the [Get Started](https://abp.io/get-started) page for other options. <!-- TODO: this document has been moved, it should be deleted in the future. -->
Install the ABP CLI using a command line window, if you've not installed before:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Use `abp new` command in an empty folder to create your project:
````bash
abp new Acme.BookStore -u angular
````
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
`-u angular` option specifies the UI framework to be Angular. Default database provider is EF Core. See the [CLI documentation](CLI.md) for all available options.
#### Pre Requirements
The created solution requires;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### The Solution Structure
Open the solution in **Visual Studio**:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-for-spa.png)
The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
> See the [Application Template Document](Startup-Templates/Application.md) to understand the solution structure in details.
### Database Connection String
Check the **connection string** in the `appsettings.json` file under the `.HttpApi.Host` project:
````json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. Change the connection string if you need.
### Create Database & Apply Database Migrations
You have two options to create the database.
#### Using the DbMigrator Application
The solution contains a console application (named `Acme.BookStore.DbMigrator` in this sample) that can create database, apply migrations and seed initial data. It is useful on development as well as on production environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![set-as-startup-project](images/db-migrator-app.png)
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations. Right click to the `.HttpApi.Host` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![pcm-update-database](images/pcm-update-database-v2.png)
This will create a new database based on the configured connection string.
> Using the `.DbMigrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
### Running the Application
#### Run the API Host (Server Side)
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
![bookstore-homepage](images/bookstore-swagger-ui-host.png)
You can see the application APIs and test them here. Get [more info](https://swagger.io/tools/swagger-ui/) about the Swagger UI.
##### Authorization for the Swagger UI
Most of the application APIs require authentication & authorization. If you want to test authorized APIs, manually go to the `/Account/Login` page, enter `admin` as the username and `1q2w3E*` as the password to login to the application. Then you will be able to execute authorized APIs too.
#### Run the Angular Application (Client Side)
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest the [yarn](https://yarnpkg.com) package manager while `npm install` will also work in most cases)
````bash
yarn
````
Once all node modules are loaded, execute `yarn start` or `npm start` command:
````bash
yarn start
````
Open your favorite browser and go to `localhost:4200` URL. Initial username is `admin` and password is `1q2w3E*`.
The startup template includes the **identity management** and **tenant management** modules. Once you login, the Administration menu will be available where you can manage **tenants**, **roles**, **users** and their **permissions**.
> We recommend [Visual Studio Code](https://code.visualstudio.com/) as the editor for the Angular project, but you are free to use your favorite editor.
### What's Next?
* [Application development tutorial](Tutorials/Part-1)

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

@ -57,7 +57,7 @@ namespace BasicAspNetCoreApplication
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseMvcWithDefaultRouteAndArea(); app.UseConfiguredEndpoints();
} }
} }
} }
@ -153,5 +153,5 @@ namespace BasicAspNetCoreApplication
## Source Code ## Source Code
Get source code of the sample project created in this tutorial from [here](https://github.com/abpframework/abp/tree/master/samples/BasicAspNetCoreApplication). Get source code of the sample project created in this tutorial from [here](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication).

106
docs/en/Getting-Started-AspNetCore-MVC-Template.md

@ -1,104 +1,8 @@
## Getting Started With the ASP.NET Core MVC Template # Getting Started with the Startup Templates
This tutorial explains how to create a new ASP.NET Core MVC web application using the startup template, configure and run it. See the following tutorials to learn how to get started with the ABP Framework using the pre-built application startup templates:
### Creating a New Project * [Getting Started With the ASP.NET Core MVC / Razor Pages UI](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Getting Started with the Angular UI](Getting-Started?UI=NG&DB=EF&Tiered=No)
This tutorial uses **ABP CLI** to create a new project. See the [Get Started](https://abp.io/get-started) page for other options. <!-- TODO: this document has been moved, it should be deleted in the future. -->
Install the ABP CLI using a command line window, if you've not installed before:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Use `abp new` command in an empty folder to create your project:
````bash
abp new Acme.BookStore
````
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
`new` command creates a **layered MVC application** with **Entity Framework Core** as the database provider. However, it has additional options. See the [CLI documentation](CLI.md) for all available options.
#### Pre Requirements
The created solution requires;
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/)
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
### The Solution Structure
Open the solution in **Visual Studio**:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
> See [Application template document](Startup-Templates/Application.md) to understand the solution structure in details.
### Database Connection String
Check the **connection string** in the `appsettings.json` file under the `.Web` project:
````json
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. Change the connection string if you need.
### Create Database & Apply Database Migrations
You have two options to create the database.
#### Using the DbMigrator Application
The solution contains a console application (named `Acme.BookStore.DbMigrator` in this sample) that can create database, apply migrations and seed initial data. It is useful on development as well as on production environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![set-as-startup-project](images/db-migrator-app.png)
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations. Right click to the `.Web` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![pcm-update-database](images/pcm-update-database-v2.png)
This will create a new database based on the configured connection string.
> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
### Running the Application
Ensure that the `.Web` project is the startup project. Run the application which will open the **home** page in your browser:
![bookstore-homepage](images/bookstore-homepage.png)
Click the **Login** button, enter `admin` as the username and `1q2w3E*` as the password to login to the application.
The startup template includes the **identity management** and **tenant management** modules. Once you login, the Administration menu will be available where you can manage **tenants**, **roles**, **users** and their **permissions**. User management page is shown below:
![bookstore-user-management](images/bookstore-user-management-v2.png)
### What's Next?
* [Application development tutorial](Tutorials/Part-1.md)

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

@ -178,4 +178,4 @@ Just called `options.UseAutofac()` method in the `AbpApplicationFactory.Create`
## Source Code ## Source Code
Get source code of the sample project created in this tutorial from [here](https://github.com/abpframework/abp/tree/master/samples/BasicConsoleApplication). Get source code of the sample project created in this tutorial from [here](https://github.com/abpframework/abp-samples/tree/master/BasicConsoleApplication).

8
docs/en/Getting-Started-With-Startup-Templates.md

@ -0,0 +1,8 @@
# Getting Started with the Startup Templates
See the following tutorials to learn how to get started with the ABP Framework using the pre-built application startup templates:
* [Getting Started With the ASP.NET Core MVC / Razor Pages UI](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Getting Started with the Angular UI](Getting-Started?UI=NG&DB=EF&Tiered=No)
<!-- TODO: this document has been moved, it should be deleted in the future. -->

413
docs/en/Getting-Started.md

@ -0,0 +1,413 @@
# Getting started
````json
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF", "Mongo"],
"Tiered": ["Yes", "No"]
}
````
This tutorial explains how to create a new {{if UI == "MVC"}} ASP.NET Core MVC web {{else if UI == "NG"}} Angular {{end}} application using the startup template, configure and run it.
## Setup your development environment
First things first! Let's setup your development environment before creating the first project.
### Pre-requirements
The following tools should be installed on your development machine:
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
{{ if Tiered == "Yes" }}
* [Redis](https://redis.io/): The applications use Redis as as [distributed cache](../Caching.md). So, you need to have Redis installed & running.
{{ end }}
> You can use another editor instead of Visual Studio as long as it supports .NET Core and ASP.NET Core.
### Install the ABP CLI
[ABP CLI](./CLI.md) is a command line interface that is used to authenticate and automate some tasks for ABP based applications.
> ABP CLI is a free & open source tool for [the ABP framework](https://abp.io/).
First, you need to install the ABP CLI using the following command:
````shell
dotnet tool install -g Volo.Abp.Cli
````
If you've already installed, you can update it using the following command:
````shell
dotnet tool update -g Volo.Abp.Cli
````
## Create a new project
> 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.
### Using the ABP CLI to create a new project
Use the `new` command of the ABP CLI to create a new project:
````shell
abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
````
* `-t` argument specifies the [startup template](Startup-Templates/Application.md) name. `app` is the startup template that contains the essential [ABP Modules](Modules/Index.md) pre-installed and configured for you.
{{ if UI == "NG" }}
* `-u` argument specifies the UI framework, `angular` in this case.
{{ if Tiered == "Yes" }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint.
{{ end }}
{{ end }}
{{ if DB == "Mongo" }}
* `-d` argument specifies the database provider, `mongodb` in this case.
{{ end }}
{{ if Tiered == "Yes" && UI != "NG" }}
* `--tiered` argument is used to create N-tiered solution where authentication server, UI and API layers are physically separated.
{{ end }}
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
#### ABP CLI commands & options
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
## The solution structure
{{ if UI == "MVC" }}
After creating your project, you will have the following solution folders & files:
![](images/solution-files-mvc.png)
You will see the following solution structure when you open the `.sln` file in the Visual Studio:
{{if DB == "Mongo"}}
![vs-default-app-solution-structure](images/vs-app-solution-structure-mongodb.png)
{{else}}
![vs-default-app-solution-structure](images/vs-app-solution-structure{{if Tiered == "Yes"}}-tiered{{end}}.png)
{{end}}
{{ else if UI == "NG" }}
There are three folders in the created solution:
![](images/solution-files-non-mvc.png)
* `angular` folder contains the Angular UI application.
* `aspnet-core` folder contains the backend solution.
* `react-native` folder contains the React Native UI application.
Open the `.sln` (Visual Studio solution) file under the `aspnet-core` folder:
![vs-angular-app-backend-solution-structure](images/vs-spa-app-backend-structure{{if DB == "Mongo"}}-mongodb{{end}}.png)
{{ end }}
> ###### About the projects in your solution
>
> Your solution may have slightly different structure based on your **UI**, **database** and other preferences.
The solution has a layered structure (based on [Domain Driven Design](./Domain-Driven-Design.md)) and also contains unit & integration test projects.
{{ if DB == "EF" }}
Integration tests projects are properly configured to work with **EF Core** & **SQLite in-memory** database.
{{ else if DB == "Mongo" }}
Integration tests projects are properly configured to work with in-memory **MongoDB** database created per test (used [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library).
{{ end }}
> See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
## Create the database
### Database connection string
Check the **connection string** in the `appsettings.json` file under the {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}`.Web` project{{end}}{{else if UI == "NG" }}`.HttpApi.Host` project{{end}}:
{{ if DB == "EF" }}
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) to learn how to switch to another DBMS.
### Apply the migrations
The solution uses the [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). So, you need to apply migrations to create the database. There are two ways of applying the database migrations.
#### Apply migrations using the DbMigrator
The solution comes with a `.DbMigrator` console application which applies migrations and also seed the initial data. It is useful on development as well as on production environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial seed data creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
#### Using EF Core Update-Database command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations.
{{ if UI == "MVC" }}
Right click to the {{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}} project and select **Set as StartUp project**:
{{ else if UI != "MVC" }}
Right click to the `.HttpApi.Host` project and select **Set as StartUp Project**:
{{ end }}
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![package-manager-console-update-database](images/package-manager-console-update-database.png)
This will create a new database based on the configured connection string.
> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
{{ else if DB == "Mongo" }}
````json
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/BookStore"
}
````
The solution is configured to use **MongoDB** in your local computer, so you need to have a MongoDB server instance up and running or change the connection string to another MongoDB server.
### Seed initial data
The solution comes with a `.DbMigrator` console application which seeds the initial data. It is useful on development as well as on production environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![db-migrator-output](images/db-migrator-output.png)
> Initial seed data creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
{{ end }}
## Run the application
{{ if UI == "MVC" }}
{{ if Tiered == "Yes" }}
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
> 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.
You can login, but you cannot enter to the main application here. This is just the authentication server.
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a **Swagger UI** in your browser.
![swagger-ui](images/swagger-ui.png)
This is the API application that is used by the web application.
Lastly, ensure that the `.Web` 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)
Click to the **login** button which will redirect you to the `Identity Server` to login to the application:
![bookstore-login](images/bookstore-login.png)
{{ else }}
Ensure that the `.Web` project is the startup project. Run the application which will open the **login** page in your browser:
> 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)
{{ end }}
{{ else if UI != "MVC" }}
#### Running the HTTP API Host (server-side)
{{ if Tiered == "Yes" }}
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
> 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.
You can login, but you cannot enter to the main application here. This is just the authentication server.
{{ end }}
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
{{ if Tiered == "No" }}
> 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.
{{ end }}
![swagger-ui](images/swagger-ui.png)
You can see the application APIs and test them here. Get [more info](https://swagger.io/tools/swagger-ui/) about the Swagger UI.
> ##### Authorization for the Swagger UI
>
> Most of the HTTP APIs require authentication & authorization. If you want to test authorized APIs, manually go to the `/Account/Login` page, enter `admin` as the username and `1q2w3E*` as the password to login to the application. Then you will be able to execute authorized APIs too.
{{ end }}
{{ if UI == "NG" }}
#### Running the Angular application (client-side)
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work in most cases)
```bash
yarn
```
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
Wait `Angular CLI` to launch `Webpack` dev-server with `BrowserSync`.
This will take care of compiling your `TypeScript` code, and automatically reloading your browser.
After it finishes, `Angular Live Development Server` will be listening on localhost:4200,
open your web browser and navigate to [localhost:4200](http://localhost:4200/)
![bookstore-login](images/bookstore-login.png)
{{ end }}
Enter **admin** as the username and **1q2w3E*** as the password to login to the application:
![bookstore-home](images/bookstore-home.png)
The application is up and running. You can start developing your application based on this startup template.
#### Mobile Development
ABP platform provide [React Native](https://reactnative.dev/) template to develop mobile applications.
>The solution includes the React Native application in the `react-native` folder as default. If you don't plan to develop a mobile application with React Native, you can ignore this step and delete the `react-native` folder.
The React Native application running on an Android emulator or a physical phone cannot connect to the backend on `localhost`. To fix this problem, it is necessary to run backend on the local IP.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `SelfUrl` and `Authority` properties with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* Open the `appsettings.json` in the `.IdentityServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
* Open the `launchSettings.json` in the `.IdentityServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `Authority` property with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ end }}
Run the backend as described in the [**Running the HTTP API Host (server-side)**](#running-the-http-api-host-server-side) section.
> React Native application does not trust the auto-generated .NET HTTPS certificate, you should use the HTTP during development.
Go to the `react-native` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work in most cases):
```bash
yarn
```
* Open the `Environment.js` in the `react-native` folder and replace the `localhost` address on the `apiUrl` and `issuer` properties with your local IP address as shown below:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ if Tiered == "Yes" }}
> Make sure that `issuer` matches the running address of the `.IdentityServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` project.
{{else}}
> Make sure that `issuer` and `apiUrl` matches the running address of the `.HttpApi.Host` project.
{{ end }}
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
Wait Expo CLI to start. Expo CLI opens the management interface on the `http://localhost:19002/` address.
![expo-interface](images/rn-expo-interface.png)
In the above management interface, you can start the application with an Android emulator, an iOS simulator or a physical phone by the scan the QR code with the [Expo Client](https://expo.io/tools#client).
> See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/), [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) documents on expo.io.
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
Enter **admin** as the username and **1q2w3E*** as the password to login to the application.
The application is up and running. You can continue to develop your application based on this startup template.
> The [application startup template](Startup-Templates/Application.md) includes the TenantManagement and Identity modules.
## What's next?
[Application development tutorial](Tutorials/Part-1.md)

212
docs/en/How-To/Azure-Active-Directory-Authentication-MVC.md

@ -0,0 +1,212 @@
# How to Use the Azure Active Directory Authentication for MVC / Razor Page Applications
This guide demonstrates how to integrate AzureAD to an ABP application that enables users to sign in using OAuth 2.0 with credentials from **Azure Active Directory**.
Adding Azure Active Directory is pretty straightforward in ABP framework. Couple of configurations needs to be done correctly.
Two different **alternative approaches** for AzureAD integration will be demonstrated for better coverage.
1. **AddAzureAD**: This approach uses Microsoft [AzureAD UI nuget package](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.AzureAD.UI/) which is very popular when users search the web about how to integrate AzureAD to their web application.
2. **AddOpenIdConnect**: This approach uses default [OpenIdConnect](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect/) which can be used for not only AzureAD but for all OpenId connections.
> There is **no difference** in functionality between these approaches. AddAzureAD is an abstracted way of OpenIdConnection ([source](https://github.com/dotnet/aspnetcore/blob/c56aa320c32ee5429d60647782c91d53ac765865/src/Azure/AzureAD/Authentication.AzureAD.UI/src/AzureADAuthenticationBuilderExtensions.cs#L122)) with predefined cookie settings.
>
> However there are key differences in integration to ABP applications because of default configurated signin schemes which will be explained below.
## 1. AddAzureAD
This approach uses the most common way to integrate AzureAD by using the [Microsoft AzureAD UI nuget package](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.AzureAD.UI/).
If you choose this approach, you will need to install `Microsoft.AspNetCore.Authentication.AzureAD.UI` package to your **.Web** project. Also, since AddAzureAD extension uses [configuration binding](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#default-configuration), you need to update your appsettings.json file located in your **.Web** project.
#### **Updating `appsettings.json`**
You need to add a new section to your `appsettings.json` which will be binded to configuration when configuring the `OpenIdConnectOptions`:
````json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-client-id>",
"Domain": "domain.onmicrosoft.com",
"CallbackPath": "/signin-azuread-oidc"
}
````
> Important configuration here is the CallbackPath. This value must be the same with one of your Azure AD-> app registrations-> Authentication -> RedirectUri.
Then, you need to configure the `OpenIdConnectOptions` to complete the integration.
#### Configuring OpenIdConnectOptions
In your **.Web** project, locate your **ApplicationWebModule** and modify `ConfigureAuthentication` method with the following:
````csharp
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
context.Services.AddAuthentication()
.AddIdentityServerAuthentication(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = false;
options.ApiName = "Acme.BookStore";
})
.AddAzureAD(options => configuration.Bind("AzureAd", options));
context.Services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.ClientId = configuration["AzureAd:ClientId"];
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters.ValidateIssuer = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.SignInScheme = IdentityConstants.ExternalScheme;
options.Scope.Add("email");
});
}
````
> **Don't forget to:**
>
> * Add `.AddAzureAD(options => configuration.Bind("AzureAd", options))` after `.AddAuthentication()`. This binds your AzureAD appsettings and easy to miss out.
> * Add `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear()`. This will disable the default Microsoft claim type mapping.
> * Add `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier)`. Mapping this to [ClaimTypes.NameIdentifier](https://github.com/dotnet/runtime/blob/6d395de48ac718a913e567ae80961050f2a9a4fa/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimTypes.cs#L59) is important since default SignIn Manager behavior uses this claim type for external login information.
> * Add `options.SignInScheme = IdentityConstants.ExternalScheme` since [default signin scheme is `AzureADOpenID`](https://github.com/dotnet/aspnetcore/blob/c56aa320c32ee5429d60647782c91d53ac765865/src/Azure/AzureAD/Authentication.AzureAD.UI/src/AzureADOpenIdConnectOptionsConfiguration.cs#L35).
> * Add `options.Scope.Add("email")` if you are using **v2.0** endpoint of AzureAD since v2.0 endpoint doesn't return the `email` claim as default. The [Account Module](../Modules/Account.md) uses `email` claim to [register external users](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs#L215).
You are done and integration is completed.
## 2. Alternative Approach: AddOpenIdConnect
If you don't want to use an extra nuget package in your application, you can use the straight default [OpenIdConnect](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect/) which can be used for all OpenId connections including AzureAD external authentication.
You don't have to use `appsettings.json` configuration but it is a good practice to set AzureAD information in the `appsettings.json`.
To get the AzureAD information from `appsettings.json`, which will be used in `OpenIdConnectOptions` configuration, simply add a new section to `appsettings.json` located in your **.Web** project:
````json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-client-id>",
"Domain": "domain.onmicrosoft.com",
"CallbackPath": "/signin-azuread-oidc"
}
````
Then, In your **.Web** project; you can modify the `ConfigureAuthentication` method located in your **ApplicationWebModule** with the following:
````csharp
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
context.Services.AddAuthentication()
.AddIdentityServerAuthentication(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = false;
options.ApiName = "BookStore";
})
.AddOpenIdConnect("AzureOpenId", "Azure Active Directory OpenId", options =>
{
options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
options.ClientId = configuration["AzureAd:ClientId"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("email");
});
}
````
And that's it, integration is completed. Keep on mind that you can connect any other external authentication providers.
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
# FAQ
* Help! `GetExternalLoginInfoAsync` returns `null`!
* There can be 2 reasons for this;
1. You are trying to authenticate against wrong scheme. Check if you set **SignInScheme** to `IdentityConstants.ExternalScheme`:
````csharp
options.SignInScheme = IdentityConstants.ExternalScheme;
````
2. Your `ClaimTypes.NameIdentifier` is `null`. Check if you added claim mapping:
````csharp
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
````
* Help! I am getting ***System.ArgumentNullException: Value cannot be null. (Parameter 'userName')*** error!
* This occurs when you use Azure Authority **v2.0 endpoint** without requesting `email` scope. [Abp checks unique email to create user](https://github.com/abpframework/abp/blob/037ef9abe024c03c1f89ab6c933710bcfe3f5c93/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs#L208). Simply add
````csharp
options.Scope.Add("email");
````
to your openid configuration.
* Help! I keep getting ***AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application*** error!
* If you set your **CallbackPath** in appsettings as:
````csharp
"AzureAd": {
...
"CallbackPath": "/signin-azuread-oidc"
}
````
your **Redirect URI** of your application in azure portal must be <u>with domain</u> like `https://localhost:44320/signin-azuread-oidc`, not only `/signin-azuread-oidc`.
* Help! I keep getting ***AADSTS700051: The response_type 'token' is not enabled for the application.*** error!
* This error occurs when you request **token** (access token) along with **id_token** without enabling Access tokens on Azure portal app registrations. Simply tick **Access tokens** checkbox located on top of ID tokens to be able to request token aswell.
* Help! I keep getting ***AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret*** error!
* This error occurs when you request **code** along with **id_token**. You need to add **client secret** on azure portal app registrations, under **Certificates & secrets** menu. Afterwards, you need to add openid configuration option like:
````csharp
options.ClientSecret = "Value of your secret on azure portal";
````
* How can I **debug/watch** which claims I get before they get mapped?
* You can add a simple event under openid configuration to debug before mapping like:
````csharp
options.Events.OnTokenValidated = (async context =>
{
var claimsFromOidcProvider = context.Principal.Claims.ToList();
await Task.CompletedTask;
});
````
## See Also
* [How to Customize the Login Page for MVC / Razor Page Applications](Customize-Login-Page-MVC.md).
* [How to Customize the SignIn Manager for ABP Applications](Customize-SignIn-Manager.md).

113
docs/en/How-To/Customize-Login-Page-MVC.md

@ -0,0 +1,113 @@
# How to Customize the Login Page for MVC / Razor Page Applications
When you create a new application using the [application startup template](../Startup-Templates/Application.md), source code of the login page will not be inside your solution, so you can not directly change it. The login page comes from the [Account Module](../Modules/Account.md) that is used a [NuGet package](https://www.nuget.org/packages/Volo.Abp.Account.Web) reference.
This document explains how to customize the login page for your own application.
## Create a Login PageModel
Create a new class inheriting from the [LoginModel](https://github.com/abpframework/abp/blob/037ef9abe024c03c1f89ab6c933710bcfe3f5c93/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs) of the Account module.
````csharp
public class CustomLoginModel : LoginModel
{
public CustomLoginModel(
Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemeProvider,
Microsoft.Extensions.Options.IOptions<Volo.Abp.Account.Web.AbpAccountOptions> accountOptions)
: base(schemeProvider, accountOptions)
{
}
}
````
> Naming convention is important here. If your class name doesn't end with `LoginModel`, you need to manually replace the `LoginModel` using the [dependency injection](../Dependency-Injection.md) system.
Then you can override any method you need and add new methods and properties needed by the UI.
## Overriding the Login Page UI
Create folder named **Account** under **Pages** directory and create a **Login.cshtml** under this folder. It will automatically override the `Login.cshtml` file defined in the Account Module thanks to the [Virtual File System](../Virtual-File-System.md).
A good way to customize a page is to copy its source code. [Click here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml) for the source code of the login page. At the time this document has been written, the source code was like below:
````xml
@page
@using Volo.Abp.Account.Settings
@using Volo.Abp.Settings
@model Acme.BookStore.Web.Pages.Account.CustomLoginModel
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage
@inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin)
{
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["Login"]</h4>
@if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled))
{
<strong>
@L["AreYouANewUser"]
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Register"]</a>
</strong>
}
<form method="post" class="mt-4">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<label asp-for="LoginInput.UserNameOrEmailAddress"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LoginInput.Password"></label>
<input asp-for="LoginInput.Password" class="form-control" />
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input" />
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-block btn-lg mt-3">@L["Login"]</abp-button>
</form>
</div>
<div class="card-footer text-center border-0">
<abp-button type="button" button-type="Link" name="Action" value="Cancel" class="px-2 py-0">@L["Cancel"]</abp-button> @* TODO: Only show if identity server is used *@
</div>
</div>
}
@if (Model.VisibleExternalProviders.Any())
{
<div class="col-md-6">
<h4>@L["UseAnotherServiceToLogIn"]</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
}
````
Just changed the `@model` to `Acme.BookStore.Web.Pages.Account.CustomLoginModel` to use the customized `PageModel` class. You can change it however your application needs.
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
## See Also
* [ASP.NET Core (MVC / Razor Pages) User Interface Customization Guide](../UI/AspNetCore/Customization-User-Interface.md).

101
docs/en/How-To/Customize-SignIn-Manager.md

@ -0,0 +1,101 @@
# How to Customize the SignIn Manager for ABP Applications
After creating a new application using the [application startup template](../Startup-Templates/Application.md), you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP [Account Module](../Modules/Account.md) uses the [Identity Management Module](../Modules/Identity.md) for SignIn Manager and the [Identity Management Module](../Modules/Identity.md) uses default [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) ([see here](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs#L17)).
To write your Custom SignIn Manager, you need to extend [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) class and register it to the DI container.
This document explains how to customize the SignIn Manager for your own application.
## Create a CustomSignInManager
Create a new class inheriting the [SignInMager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) of Microsoft Identity package.
````csharp
public class CustomSignInManager : Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>
{
public CustomSignInManager(
Microsoft.AspNetCore.Identity.UserManager<Volo.Abp.Identity.IdentityUser> userManager,
Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor,
Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser> claimsFactory,
Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>> logger,
Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes,
Microsoft.AspNetCore.Identity.IUserConfirmation<Volo.Abp.Identity.IdentityUser> confirmation)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
{
}
}
````
> It is important to use **Volo.Abp.Identity.IdentityUser** type for SignInManager to inherit, not the AppUser of your application.
Afterwards you can override any of the SignIn Manager methods you need and add new methods and properties needed for your authentication or registration flow.
## Overriding the GetExternalLoginInfoAsync Method
In this case we'll be overriding the `GetExternalLoginInfoAsync` method which is invoked when a third party authentication is implemented.
A good way to override a method is copying its [source code](https://github.com/dotnet/aspnetcore/blob/c56aa320c32ee5429d60647782c91d53ac765865/src/Identity/Core/src/SignInManager.cs#L638-L674). In this case, we will be using a minorly modified version of the source code which explicitly shows the namespaces of the methods and properties to help better understanding of the concept.
````csharp
public override async Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
{
var auth = await Context.AuthenticateAsync(Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme);
var items = auth?.Properties?.Items;
if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey))
{
return null;
}
if (expectedXsrf != null)
{
if (!items.ContainsKey(XsrfKey))
{
return null;
}
var userId = items[XsrfKey] as string;
if (userId != expectedXsrf)
{
return null;
}
}
var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var provider = items[LoginProviderKey] as string;
if (providerKey == null || provider == null)
{
return null;
}
var providerDisplayName = (await GetExternalAuthenticationSchemesAsync()).FirstOrDefault(p => p.Name == provider)?.DisplayName
?? provider;
return new Microsoft.AspNetCore.Identity.ExternalLoginInfo(auth.Principal, provider, providerKey, providerDisplayName)
{
AuthenticationTokens = auth.Properties.GetTokens()
};
}
````
To get your overridden method invoked and your customized SignIn Manager class to work, you need to register your class to the [Dependency Injection System](../Dependency-Injection.md).
## Register to Dependency Injection
Registering `CustomSignInManager` should be done with adding **AddSignInManager** extension method of the [IdentityBuilderExtensions](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/IdentityBuilderExtensions.cs) of the [IdentityBuilder](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/IdentityBuilder.cs).
Inside your `.Web` project, locate the `YourProjectNameWebModule` and add the following code under the `PreConfigureServices` method to replace the old `SignInManager` with your customized one:
````csharp
PreConfigure<IdentityBuilder>(identityBuilder =>
{
identityBuilder.AddSignInManager<CustomSignInManager>();
});
````
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
## See Also
* [How to Customize the Login Page for MVC / Razor Page Applications](Customize-Login-Page-MVC.md).
* [Identity Management Module](../Modules/Identity.md).

9
docs/en/How-To/Index.md

@ -0,0 +1,9 @@
# "How To" Guides
This section contains "how to" guides for some specific questions frequently asked. While some of them are common development tasks and not directly related to the ABP Framework, we think it is useful to have some concrete examples those directly work with your ABP based applications.
## Authentication
* [How to Customize the Login Page for MVC / Razor Page Applications](Customize-Login-Page-MVC.md)
* [How to Use the Azure Active Directory Authentication for MVC / Razor Page Applications](Azure-Active-Directory-Authentication-MVC.md)
* [How to Customize the SignIn Manager for ABP Applications](Customize-SignIn-Manager.md)

4
docs/en/Index.md

@ -8,8 +8,8 @@ Explore the left navigation menu to deep dive in the documentation.
Easiest way to start a new project with ABP is to use the startup templates: Easiest way to start a new project with ABP is to use the startup templates:
* [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started-AspNetCore-MVC-Template.md) * [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Angular UI Startup Template](Getting-Started-Angular-Template.md) * [Angular UI Startup Template](Getting-Started?UI=NG&DB=EF&Tiered=No)
If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials: If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials:

52
docs/en/Localization.md

@ -87,6 +87,21 @@ A JSON localization file content is shown below:
* Every localization file should define the `culture` code for the file (like "en" or "en-US"). * Every localization file should define the `culture` code for the file (like "en" or "en-US").
* `texts` section just contains key-value collection of the localization strings (keys may have spaces too). * `texts` section just contains key-value collection of the localization strings (keys may have spaces too).
### Default Resource
`AbpLocalizationOptions.DefaultResourceType` can be set to a resource type, so it is used when the localization resource was not specified:
````csharp
Configure<AbpLocalizationOptions>(options =>
{
options.DefaultResourceType = typeof(TestResource);
});
````
> The [application startup template](Startup-Templates/Application.md) sets `DefaultResourceType` to the localization resource of the application.
See the *Client Side* section below for a use case.
### Short Localization Resource Name ### Short Localization Resource Name
Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example: Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example:
@ -166,6 +181,10 @@ public class MyService
} }
```` ````
##### Format Arguments
Format arguments can be passed after the localization key. If your message is `Hello {0}, welcome!`, then you can pass the `{0}` argument to the localizer like `_localizer["HelloMessage", "John"]`
#### Simplest Usage In A Razor View/Page #### Simplest Usage In A Razor View/Page
````c# ````c#
@ -180,18 +199,47 @@ Refer to the [Microsoft's localization documentation](https://docs.microsoft.com
ABP provides JavaScript services to use the same localized texts in the client side. ABP provides JavaScript services to use the same localized texts in the client side.
Get a localization resource: #### getResource
`abp.localization.getResource` function is used to get a localization resource:
````js ````js
var testResource = abp.localization.getResource('Test'); var testResource = abp.localization.getResource('Test');
```` ````
Localize a string: Then you can localize a string based on this resource:
````js ````js
var str = testResource('HelloWorld'); var str = testResource('HelloWorld');
```` ````
#### localize
`abp.localization.localize` function is a shortcut where you can both specify the text name and the resource name:
````js
var str = abp.localization.localize('HelloWorld', 'Test');
````
`HelloWorld` is the text to localize, where `Test` is the localization resource name here.
If you don't specify the localization resource name, it uses the default localization resource defined on the `AbpLocalizationOptions` (see the *Default Resource* section above). Example:
````js
var str = abp.localization.localize('HelloWorld'); //uses the default resource
````
##### Format Arguments
If your localized string contains arguments, like `Hello {0}, welcome!`, you can pass arguments to the localization methods. Examples:
````js
var str1 = abp.localization.getResource('Test')('HelloWelcomeMessage', 'John');
var str2 = abp.localization.localize('HelloWorld', 'Test', 'John');
````
Both of the samples above produce the output `Hello John, welcome!`.
## See Also ## See Also
* [Localization in Angular UI](UI/Angular/Localization.md) * [Localization in Angular UI](UI/Angular/Localization.md)

4
docs/en/Microservice-Architecture.md

@ -23,8 +23,8 @@ One common advise to start a new solution is **always to start with a monolith**
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts. However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts.
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration. ABP can help you in that point by offerring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
## Microservice Demo Solution ## Microservice Demo Solution
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework. The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework.

2
docs/en/Modules/Docs.md

@ -134,7 +134,7 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<PermissionOptions>(options => Configure<AbpPermissionOptions>(options =>
{ {
options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>(); options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>();
}); });

1
docs/en/Modules/Index.md

@ -23,6 +23,7 @@ There are some **free and open source** application modules developed and mainta
* **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md). * **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md).
* **Tenant Management**: Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application. * **Tenant Management**: Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application.
* **Users**: Abstract users, so other modules can depend on this module instead of the Identity module. * **Users**: Abstract users, so other modules can depend on this module instead of the Identity module.
* [**Virtual File Explorer**](Virtual-File-Explorer.md): Provided a simple UI to view files in [virtual file system](../Virtual-File-System.md).
See [the GitHub repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules. See [the GitHub repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules.

80
docs/en/Modules/Virtual-File-Explorer.md

@ -0,0 +1,80 @@
# Virtual File Explorer Module
## What is Virtual File Explorer Module?
Virtual File Explorer Module provided a simple UI to view all files in [virtual file system](../Virtual-File-System.md).
> Virtual File Explorer Module is not installed for [the startup templates](../Startup-Templates/Index.md). So, you need to manually add this module to your application.
### Installation
#### 1- Referencing Virtual File Explorer Module Packages
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.VirtualFileExplorer`
Or you can also manually install nuget package to `Acme.MyProject.Web` project:
* Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project.
`Install-Package Volo.Abp.VirtualFileExplorer.Web`
#### 2- Adding Module Dependencies
* Open `MyProjectWebModule.cs`and add `typeof(AbpVirtualFileExplorerWebModule)` as shown below;
```csharp
[DependsOn(
typeof(AbpVirtualFileExplorerWebModule),
typeof(MyProjectApplicationModule),
typeof(MyProjectEntityFrameworkCoreModule),
typeof(AbpAutofacModule),
typeof(AbpIdentityWebModule),
typeof(AbpAccountWebModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule)
)]
public class MyProjectWebModule : AbpModule
{
//...
}
```
#### 3- Adding NPM Package
* Open `package.json` and add `@abp/virtual-file-explorer": "^2.9.0` as shown below:
```json
{
"version": "1.0.0",
"name": "my-app",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0",
"@abp/virtual-file-explorer": "^2.9.0"
}
}
```
Then open the command line terminal in the `Acme.MyProject.Web` project folder and run the following command:
1. `yarn`
2. `gulp`
That's all,Now run the application and Navigate to `/VirtualFileExplorer`. You will see virtual file explorer page:
![Virtual-File-Explorer](../images/virtual-file-explorer.png)
### Options
You can disabled virtual file explorer module via `AbpVirtualFileExplorerOptions` options:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpVirtualFileExplorerOptions>(options =>
{
options.IsEnabled = false;
});
}
```

26
docs/en/Multi-Tenancy.md

@ -4,7 +4,7 @@ ABP Multi-tenancy module provides base functionality to create multi tenant appl
Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as like that: Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as like that:
> Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants. > Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants.
### Volo.Abp.MultiTenancy Package ### Volo.Abp.MultiTenancy Package
@ -172,11 +172,11 @@ namespace MyCompany.MyProject
{ {
options.Tenants = new[] options.Tenants = new[]
{ {
new TenantInformation( new TenantConfiguration(
Guid.Parse("446a5211-3d72-4339-9adc-845151f8ada0"), //Id Guid.Parse("446a5211-3d72-4339-9adc-845151f8ada0"), //Id
"tenant1" //Name "tenant1" //Name
), ),
new TenantInformation( new TenantConfiguration(
Guid.Parse("25388015-ef1c-4355-9c18-f6b6ddbaf89d"), //Id Guid.Parse("25388015-ef1c-4355-9c18-f6b6ddbaf89d"), //Id
"tenant2" //Name "tenant2" //Name
) )
@ -252,7 +252,7 @@ TODO: This package implements ITenantStore using a real database...
#### Tenant Information #### Tenant Information
ITenantStore works with **TenantInformation** class that has several properties for a tenant: ITenantStore works with **TenantConfiguration** class that has several properties for a tenant:
* **Id**: Unique Id of the tenant. * **Id**: Unique Id of the tenant.
* **Name**: Unique name of the tenant. * **Name**: Unique name of the tenant.
@ -302,11 +302,11 @@ TODO:...
Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box: Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box:
* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be stay as the first contributor for security**. * **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for security**.
* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default. * **QueryStringTenantResolveContributor**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default.
* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route. * **RouteTenantResolveContributor**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route.
* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default. * **HeaderTenantResolveContributor**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default.
* **CookieTenantResolver**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default. * **CookieTenantResolveContributor**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default.
> If you use nginx as a reverse proxy server, please note that if `TenantKey` contains an underscore or other special characters, there may be a problem, please refer to: > If you use nginx as a reverse proxy server, please note that if `TenantKey` contains an underscore or other special characters, there may be a problem, please refer to:
http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers
@ -343,8 +343,10 @@ namespace MyCompany.MyProject
{ {
Configure<AbpTenantResolveOptions>(options => Configure<AbpTenantResolveOptions>(options =>
{ {
//Subdomain format: {0}.mydomain.com (adding as the highest priority resolver) //Subdomain format: {0}.mydomain.com
options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com")); //Adding as the second highest priority resolver after 'CurrentUserTenantResolveContributor' to
//ensure the user cannot impersonate a different tenant.
options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));
}); });
//... //...
@ -355,7 +357,7 @@ namespace MyCompany.MyProject
{0} is the the placeholder to determine current tenant's unique name. {0} is the the placeholder to determine current tenant's unique name.
Instead of ``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut: Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));`` you can use this shortcut:
````C# ````C#
options.AddDomainTenantResolver("{0}.mydomain.com"); options.AddDomainTenantResolver("{0}.mydomain.com");

15
docs/en/Nightly-Builds.md

@ -24,3 +24,18 @@ Now, you can install preview / nightly packages to your project from Nuget Brows
3. Search a package. You will see prereleases of the package formatted as `(VERSION)-preview(DATE)` (like *v0.16.0-preview20190401* in this sample). 3. Search a package. You will see prereleases of the package formatted as `(VERSION)-preview(DATE)` (like *v0.16.0-preview20190401* in this sample).
4. You can click to the `Install` button to add package to your project. 4. You can click to the `Install` button to add package to your project.
## Install & Uninstall Preview NPM Packages
The latest version of preview NPM packages can be installed by the running below command in the root folder of application:
```bash
abp switch-to-preview
```
If you're using the ABP Framework preview packages, you can switch back to stable version using this command:
```bash
abp switch-to-stable
```
See the [ABP CLI documentation](./CLI.md) for more information.

412
docs/en/Object-Extensions.md

@ -0,0 +1,412 @@
# Object Extensions
ABP Framework provides an **object extension system** to allow you to **add extra properties** to an existing object **without modifying** the related class. This allows to extend functionalities implemented by a depended [application module](Modules/Index.md), especially when you want to [extend entities](Customizing-Application-Modules-Extending-Entities.md) and [DTOs](Customizing-Application-Modules-Overriding-Services.md) defined by the module.
> Object extension system normally is not needed for your own objects since you can easily add regular properties to your own classes.
## IHasExtraProperties Interface
This is the interface to make a class extensible. It simply defines a `Dictionary` property:
````csharp
Dictionary<string, object> ExtraProperties { get; }
````
Then you can add or get extra properties using this dictionary.
### Base Classes
`IHasExtraProperties` interface is implemented by several base classes by default:
* Implemented by the `AggregateRoot` class (see [entities](Entities.md)).
* Implemented by `ExtensibleEntityDto`, `ExtensibleAuditedEntityDto`... base [DTO](Data-Transfer-Objects.md) classes.
* Implemented by the `ExtensibleObject`, which is a simple base class can be inherited for any type of object.
So, if you inherit from these classes, your class will also be extensible. If not, you can always implement it manually.
### Fundamental Extension Methods
While you can directly use the `ExtraProperties` property of a class, it is suggested to use the following extension methods while working with the extra properties.
#### SetProperty
Used to set the value of an extra property:
````csharp
user.SetProperty("Title", "My Title");
user.SetProperty("IsSuperUser", true);
````
`SetProperty` returns the same object, so you can chain it:
````csharp
user.SetProperty("Title", "My Title")
.SetProperty("IsSuperUser", true);
````
#### GetProperty
Used to read the value of an extra property:
````csharp
var title = user.GetProperty<string>("Title");
if (user.GetProperty<bool>("IsSuperUser"))
{
//...
}
````
* `GetProperty` is a generic method and takes the object type as the generic parameter.
* Returns the default value if given property was not set before (default value is `0` for `int`, `false` for `bool`... etc).
##### Non Primitive Property Types
If your property type is not a primitive (int, bool, enum, string... etc) type, then you need to use non-generic version of the `GetProperty` which returns an `object`.
#### HasProperty
Used to check if the object has a property set before.
#### RemoveProperty
Used to remove a property from the object. Use this methods instead of setting a `null` value for the property.
### Some Best Practices
Using magic strings for the property names is dangerous since you can easily type the property name wrong - it is not type safe. Instead;
* Define a constant for your extra property names
* Create extension methods to easily set your extra properties.
Example:
````csharp
public static class IdentityUserExtensions
{
private const string TitlePropertyName = "Title";
public static void SetTitle(this IdentityUser user, string title)
{
user.SetProperty(TitlePropertyName, title);
}
public static string GetTitle(this IdentityUser user)
{
return user.GetProperty<string>(TitlePropertyName);
}
}
````
Then you can easily set or get the `Title` property:
````csharp
user.SetTitle("My Title");
var title = user.GetTitle();
````
## Object Extension Manager
While you can set arbitrary properties to an extensible object (which implements the `IHasExtraProperties` interface), `ObjectExtensionManager` is used to explicitly define extra properties for extensible classes.
Explicitly defining an extra property has some use cases:
* Allows to control how the extra property is handled on object to object mapping (see the section below).
* Allows to define metadata for the property. For example, you can map an extra property to a table field in the database while using the [EF Core](Entity-Framework-Core.md).
> `ObjectExtensionManager` implements the singleton pattern (`ObjectExtensionManager.Instance`) and you should define object extensions before your application startup. The [application startup template](Startup-Templates/Application.md) has some pre-defined static classes to safely define object extensions inside.
### AddOrUpdate
`AddOrUpdate` is the main method to define a extra properties or update extra properties for an object.
Example: Define extra properties for the `IdentityUser` entity:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdate<IdentityUser>(options =>
{
options.AddOrUpdateProperty<string>("SocialSecurityNumber");
options.AddOrUpdateProperty<bool>("IsSuperUser");
}
);
````
### AddOrUpdateProperty
While `AddOrUpdateProperty` can be used on the `options` as shown before, if you want to define a single extra property, you can use the shortcut extension method too:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>("SocialSecurityNumber");
````
Sometimes it would be practical to define a single extra property to multiple types. Instead of defining one by one, you can use the following code:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<string>(
new[]
{
typeof(IdentityUserDto),
typeof(IdentityUserCreateDto),
typeof(IdentityUserUpdateDto)
},
"SocialSecurityNumber"
);
````
### Property Configuration
`AddOrUpdateProperty` can also get an action that can perform additional configuration on the property definition:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
//Configure options...
});
````
> `options` has a dictionary, named `Configuration` which makes the object extension definitions even extensible. It is used by the EF Core to map extra properties to table fields in the database. See the [extending entities](Customizing-Application-Modules-Extending-Entities.md) document.
The following sections explain the fundamental property configuration options.
#### Default Value
A default value is automatically set for the new property, which is the natural default value for the property type, like `null` for `string`, `false` for `bool` or `0` for `int`.
There are two ways to override the default value:
##### DefaultValue Option
`DefaultValue` option can be set to any value:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, int>(
"MyIntProperty",
options =>
{
options.DefaultValue = 42;
});
````
##### DefaultValueFactory Options
`DefaultValueFactory` can be set to a function that returns the default value:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, DateTime>(
"MyDateTimeProperty",
options =>
{
options.DefaultValueFactory = () => DateTime.Now;
});
````
`options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` .
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option.
#### CheckPairDefinitionOnMapping
Controls how to check property definitions while mapping two extensible objects. See the "Object to Object Mapping" section to understand the `CheckPairDefinitionOnMapping` option better.
## Validation
You may want to add some **validation rules** for the extra properties you've defined. `AddOrUpdateProperty` method options allows two ways of performing validation:
1. You can add **data annotation attributes** for a property.
2. You can write an action (code block) to perform a **custom validation**.
Validation works when you use the object in a method that is **automatically validated** (e.g. controller actions, page handler methods, application service methods...). So, all extra properties are validated whenever the extended object is being validated.
### Data Annotation Attributes
All of the standard data annotation attributes are valid for extra properties. Example:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserCreateDto, string>(
"SocialSecurityNumber",
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
}
);
});
````
With this configuration, `IdentityUserCreateDto` objects will be invalid without a valid `SocialSecurityNumber` value provided.
#### Default Validation Attributes
There are some attributes **automatically added** when you create certain type of properties;
* `RequiredAttribute` is added for non nullable primitive property types (e.g. `int`, `bool`, `DateTime`...) and `enum` types.
* `EnumDataTypeAttribute` is added for enum types, to prevent to set invalid enum values.
Use `options.Attributes.Clear();` if you don't want these attributes.
### Custom Validation
If you need, you can add a custom action that is executed to validate the extra properties. Example:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserCreateDto, string>(
"SocialSecurityNumber",
options =>
{
options.Validators.Add(context =>
{
var socialSecurityNumber = context.Value as string;
if (socialSecurityNumber == null ||
socialSecurityNumber.StartsWith("X"))
{
context.ValidationErrors.Add(
new ValidationResult(
"Invalid social security number: " + socialSecurityNumber,
new[] { "SocialSecurityNumber" }
)
);
}
});
});
````
`context.ServiceProvider` can be used to resolve a service dependency for advanced scenarios.
In addition to add custom validation logic for a single property, you can add a custom validation logic that is executed in object level. Example:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdate<IdentityUserCreateDto>(objConfig =>
{
//Define two properties with their own validation rules
objConfig.AddOrUpdateProperty<string>("Password", propertyConfig =>
{
propertyConfig.Attributes.Add(new RequiredAttribute());
});
objConfig.AddOrUpdateProperty<string>("PasswordRepeat", propertyConfig =>
{
propertyConfig.Attributes.Add(new RequiredAttribute());
});
//Write a common validation logic works on multiple properties
objConfig.Validators.Add(context =>
{
if (context.ValidatingObject.GetProperty<string>("Password") !=
context.ValidatingObject.GetProperty<string>("PasswordRepeat"))
{
context.ValidationErrors.Add(
new ValidationResult(
"Please repeat the same password!",
new[] { "Password", "PasswordRepeat" }
)
);
}
});
});
````
## Object to Object Mapping
Assume that you've added an extra property to an extensible entity object and used auto [object to object mapping](Object-To-Object-Mapping.md) to map this entity to an extensible DTO class. You need to be careful in such a case, because the extra property may contain a **sensitive data** that should not be available to clients.
This section offers some **good practices** to control your extra properties on object mapping.
### MapExtraPropertiesTo
`MapExtraPropertiesTo` is an extension method provided by the ABP Framework to copy extra properties from an object to another in a controlled manner. Example usage:
````csharp
identityUser.MapExtraPropertiesTo(identityUserDto);
````
`MapExtraPropertiesTo` **requires to define properties** (as described above) in **both sides** (`IdentityUser` and `IdentityUserDto` in this case) in order to copy the value to the target object. Otherwise, it doesn't copy the value even if it does exists in the source object (`identityUser` in this example). There are some ways to overload this restriction.
#### MappingPropertyDefinitionChecks
`MapExtraPropertiesTo` gets an additional parameter to control the definition check for a single mapping operation:
````csharp
identityUser.MapExtraPropertiesTo(
identityUserDto,
MappingPropertyDefinitionChecks.None
);
````
> Be careful since `MappingPropertyDefinitionChecks.None` copies all extra properties without any check. `MappingPropertyDefinitionChecks` enum has other members too.
If you want to completely disable definition check for a property, you can do it while defining the extra property (or update an existing definition) as shown below:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
options.CheckPairDefinitionOnMapping = false;
});
````
#### Ignored Properties
You may want to ignore some properties on a specific mapping operation:
````csharp
identityUser.MapExtraPropertiesTo(
identityUserDto,
ignoredProperties: new[] {"MySensitiveProp"}
);
````
Ignored properties are not copied to the target object.
#### AutoMapper Integration
If you're using the [AutoMapper](https://automapper.org/) library, the ABP Framework also provides an extension method to utilize the `MapExtraPropertiesTo` method defined above.
You can use the `MapExtraProperties()` method inside your mapping profile.
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<IdentityUser, IdentityUserDto>()
.MapExtraProperties();
}
}
````
It has the same parameters with the `MapExtraPropertiesTo` method.
## Entity Framework Core Database Mapping
If you're using the EF Core, you can map an extra property to a table field in the database. Example:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
options.MapEfCore(b => b.HasMaxLength(32));
}
);
````
See the [Entity Framework Core Integration document](Entity-Framework-Core.md) for more.

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

Loading…
Cancel
Save