@ -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. |
|||
@ -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 |
|||
@ -0,0 +1,13 @@ |
|||
{ |
|||
"culture": "ru", |
|||
"texts": { |
|||
"Account": "Аккаунт", |
|||
"Welcome": "Добро пожаловать", |
|||
"UseOneOfTheFollowingLinksToContinue": "Для продолжения используйте одну из следующих ссылок", |
|||
"FrameworkHomePage": "Главная страница фреймворка", |
|||
"FrameworkDocumentation": "Документация фреймворка", |
|||
"OfficialBlog": "Официальный блог", |
|||
"CommercialHomePage": "Главная страница коммерческой версии", |
|||
"CommercialSupportWebSite": "Сайт коммерческой поддержки" |
|||
} |
|||
} |
|||
@ -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": "Мастер модулей" |
|||
} |
|||
} |
|||
@ -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 |
|||
@ -1,3 +0,0 @@ |
|||
## AutoMapper Integration |
|||
|
|||
TODO |
|||
@ -1,3 +1,140 @@ |
|||
# 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) |
|||
@ -0,0 +1,3 @@ |
|||
# Blog Storing |
|||
|
|||
TODO |
|||
@ -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: |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
After Width: | Height: | Size: 541 KiB |
|
After Width: | Height: | Size: 179 KiB |
@ -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: |
|||
|
|||
 |
|||
|
|||
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): |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
|||
 |
|||
|
|||
You can also check history for an entity when you click to the actions menu for the entity: |
|||
|
|||
 |
|||
|
|||
### 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: |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 385 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 135 KiB |
@ -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. |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
Example screenshots from the language management page of the ABP Commercial: |
|||
|
|||
 |
|||
|
|||
(Default style UI) |
|||
|
|||
 |
|||
|
|||
(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. |
|||
|
|||
 |
|||
|
|||
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 |
|||
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 48 KiB |
@ -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: |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
[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. |
|||
|
|||
 |
|||
|
|||
*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): |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
|||
 |
|||
|
|||
*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. |
|||
|
|||
 |
|||
|
|||
*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. |
|||
|
|||
 |
|||
|
|||
*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: |
|||
|
|||
 |
|||
|
|||
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) |
|||
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 267 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 169 KiB |
@ -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 |
|||
@ -0,0 +1,3 @@ |
|||
# Distributed Event Bus RabbitMQ Integration |
|||
|
|||
TODO |
|||
@ -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. |
|||
|
|||
@ -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. |
|||
|
|||
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**: |
|||
|
|||
 |
|||
|
|||
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**: |
|||
|
|||
 |
|||
|
|||
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below: |
|||
|
|||
 |
|||
|
|||
#### 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**: |
|||
|
|||
 |
|||
|
|||
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command: |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
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) |
|||
<!-- TODO: this document has been moved, it should be deleted in the future. --> |
|||
@ -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. |
|||
|
|||
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**: |
|||
|
|||
 |
|||
|
|||
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**: |
|||
|
|||
 |
|||
|
|||
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below: |
|||
|
|||
 |
|||
|
|||
#### 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**: |
|||
|
|||
 |
|||
|
|||
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command: |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
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: |
|||
|
|||
 |
|||
|
|||
### What's Next? |
|||
|
|||
* [Application development tutorial](Tutorials/Part-1.md) |
|||
<!-- TODO: this document has been moved, it should be deleted in the future. --> |
|||
@ -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. --> |
|||
@ -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: |
|||
|
|||
 |
|||
|
|||
You will see the following solution structure when you open the `.sln` file in the Visual Studio: |
|||
|
|||
{{if DB == "Mongo"}} |
|||
|
|||
 |
|||
|
|||
{{else}} |
|||
|
|||
 |
|||
|
|||
{{end}} |
|||
|
|||
{{ else if UI == "NG" }} |
|||
There are three folders in the created solution: |
|||
|
|||
 |
|||
|
|||
* `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: |
|||
|
|||
 |
|||
|
|||
{{ 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** |
|||
|
|||
 |
|||
|
|||
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below: |
|||
|
|||
 |
|||
|
|||
> 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 }} |
|||
|
|||
 |
|||
|
|||
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command: |
|||
|
|||
 |
|||
|
|||
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** |
|||
|
|||
 |
|||
|
|||
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below: |
|||
|
|||
 |
|||
|
|||
> 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. |
|||
|
|||
 |
|||
|
|||
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 |
|||
|
|||
 |
|||
|
|||
Click to the **login** button which will redirect you to the `Identity Server` to login to the application: |
|||
|
|||
 |
|||
|
|||
{{ 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. |
|||
|
|||
 |
|||
|
|||
{{ 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 }} |
|||
|
|||
 |
|||
|
|||
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/) |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
{{ end }} |
|||
|
|||
Enter **admin** as the username and **1q2w3E*** as the password to login to the application: |
|||
|
|||
 |
|||
|
|||
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"}} |
|||
 |
|||
|
|||
* 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" }} |
|||
|
|||
 |
|||
|
|||
* 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: |
|||
|
|||
 |
|||
|
|||
{{ 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. |
|||
|
|||
 |
|||
|
|||
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. |
|||
|
|||
 |
|||
|
|||
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) |
|||
@ -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). |
|||
@ -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). |
|||
@ -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). |
|||
@ -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) |
|||
@ -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: |
|||
|
|||
 |
|||
|
|||
### Options |
|||
|
|||
You can disabled virtual file explorer module via `AbpVirtualFileExplorerOptions` options: |
|||
|
|||
```csharp |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpVirtualFileExplorerOptions>(options => |
|||
{ |
|||
options.IsEnabled = false; |
|||
}); |
|||
} |
|||
``` |
|||
@ -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. |
|||