From 798da7473c264c28b6b15026fe2dca8a39406898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Wed, 4 Mar 2020 15:54:30 +0300 Subject: [PATCH 01/17] Create Background-Workers.md --- docs/en/Background-Workers.md | 83 +++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/en/Background-Workers.md diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md new file mode 100644 index 0000000000..22e7c66e4d --- /dev/null +++ b/docs/en/Background-Workers.md @@ -0,0 +1,83 @@ +# Background Workers + +## Introduction + +Background workers are used to execute some tasks in the background. You may need background workers for several reasons. + +### Create a Background Worker + +A background worker is a class that derives from the `AsyncPeriodicBackgroundWorkerBase` or `PeriodicBackgroundWorkerBase` class. Both base classes are derived from `ISingletonDependency`. + +### Status Checker +This example is used to simple check remote application status. Just suppose that, we want to check and store some web applications are running or not? + +````csharp +public class AppStatusService : ITransientDependency +{ + . + . + public void CheckAppStatus() + { + var ping = new System.Net.NetworkInformation.Ping(); + + var result = ping.Send("www.google.com"); + + // save the result + } + . + . +} +```` + +Then create a background worker class that derived from the `PeriodicBackgroundWorkerBase`: + +````csharp +. +. +using Volo.Abp.BackgroundWorkers; + +namespace Volo.Www.Application +{ + public class AppStatusCheckingWorker : PeriodicBackgroundWorkerBase + { + private readonly IAppStatusService _appStatusService; + + public AppStatusCheckingWorker( + AbpTimer timer, + IServiceScopeFactory scopeFactory, + IAppStatusService appStatusService) + : base(timer, scopeFactory) + { + _appStatusService = appStatusService; + Timer.Period = 10_000; // 10 secs + } + + protected override void DoWork(PeriodicBackgroundWorkerContext workerContext) + { + _appStatusService.CheckAppStatus(); + } + } +} +```` + +This worker will call DoWorkAsync() method every 10 seconds while the application is running. + +### Configuration + +Add your BackgroundWorker at `OnApplicationInitialization` in your [module class](Module-Development-Basics.md). The example below initialize the background worker to your module: + +````csharp +using Volo.Abp.BackgroundWorkers; + +public class MyModule : AbpModule +{ + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + context.ServiceProvider + .GetRequiredService() + .Add( + context.ServiceProvider.GetRequiredService() + ); + } +} +```` \ No newline at end of file From 6d1e72f4846197ad138b288f6ef58911c1948421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Wed, 4 Mar 2020 15:56:16 +0300 Subject: [PATCH 02/17] Update Background-Workers.md --- docs/en/Background-Workers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 22e7c66e4d..7c0e58b501 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -2,7 +2,7 @@ ## Introduction -Background workers are used to execute some tasks in the background. You may need background workers for several reasons. +Background workers are used to execute some tasks in the background periodically. ### Create a Background Worker From 13628e20ba6e54463c8a02cc058b41af366d7eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Wed, 4 Mar 2020 15:56:38 +0300 Subject: [PATCH 03/17] Update Background-Workers.md --- docs/en/Background-Workers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 7c0e58b501..10dc18745a 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -20,7 +20,7 @@ public class AppStatusService : ITransientDependency { var ping = new System.Net.NetworkInformation.Ping(); - var result = ping.Send("www.google.com"); + var result = ping.Send("www.github.com"); // save the result } From cfa735d9ddf6592ded63174f03ea5b661ba2e9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 16:37:30 +0300 Subject: [PATCH 04/17] background workers documentation added --- docs/en/Background-Jobs.md | 5 +- docs/en/Background-Workers.md | 90 ++++++++++++++++------------------- 2 files changed, 45 insertions(+), 50 deletions(-) diff --git a/docs/en/Background-Jobs.md b/docs/en/Background-Jobs.md index 70f06bbadb..b5dce34ddf 100644 --- a/docs/en/Background-Jobs.md +++ b/docs/en/Background-Jobs.md @@ -171,4 +171,7 @@ Background job system is extensible and you can change the default background jo See pre-built job manager alternatives: * [Hangfire Background Job Manager](Background-Jobs-Hangfire.md) -* [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md) \ No newline at end of file +* [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md) + +## See More +* [Background Workers](Background-Workers.md) \ No newline at end of file diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 10dc18745a..4f7faffc71 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -2,73 +2,56 @@ ## Introduction -Background workers are used to execute some tasks in the background periodically. +Background workers are different than [background jobs](Background-Jobs.md). They 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 is a class that derives from the `AsyncPeriodicBackgroundWorkerBase` or `PeriodicBackgroundWorkerBase` class. Both base classes are derived from `ISingletonDependency`. +A background worker is a `Singleton Depency` class that derives from the `AsyncPeriodicBackgroundWorkerBase` or `PeriodicBackgroundWorkerBase`. -### Status Checker -This example is used to simple check remote application status. Just suppose that, we want to check and store some web applications are running or not? +Assume that we want to make a user passive, if he did not login to the application in last 30 days. See the code: ````csharp -public class AppStatusService : ITransientDependency +public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase { - . - . - public void CheckAppStatus() + private readonly IUserRepository _userRepository; + private readonly ILogger _logger; + + public PassiveUserCheckerWorker( + AbpTimer timer, + IServiceScopeFactory serviceScopeFactory, + IUserRepository userRepository, + ILogger logger + ) : base(timer, serviceScopeFactory) { - var ping = new System.Net.NetworkInformation.Ping(); - - var result = ping.Send("www.github.com"); - - // save the result + _userRepository = userRepository; + _logger = logger; + Timer.Period = 5_000; //5 seconds (good for tests) } - . - . -} -```` -Then create a background worker class that derived from the `PeriodicBackgroundWorkerBase`: - -````csharp -. -. -using Volo.Abp.BackgroundWorkers; - -namespace Volo.Www.Application -{ - public class AppStatusCheckingWorker : PeriodicBackgroundWorkerBase + protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) { - private readonly IAppStatusService _appStatusService; + _logger.LogInformation($"{nameof(PassiveUserCheckerWorker)} started to work."); + + // UserRepository sets statuses of inactive users as a passive. + await _userRepository.UpdateInactiveUserStatusesAsync(); - public AppStatusCheckingWorker( - AbpTimer timer, - IServiceScopeFactory scopeFactory, - IAppStatusService appStatusService) - : base(timer, scopeFactory) - { - _appStatusService = appStatusService; - Timer.Period = 10_000; // 10 secs - } - - protected override void DoWork(PeriodicBackgroundWorkerContext workerContext) - { - _appStatusService.CheckAppStatus(); - } + _logger.LogInformation($"{nameof(PassiveUserCheckerWorker)} finished it's work."); } } ```` -This worker will call DoWorkAsync() method every 10 seconds while the application is running. +* If your background worker derive from `PeriodicBackgroundWorkerBase`, you should implement the `DoWork` method to perform your periodic working code. +* If you directly implement IBackgroundWorker, you will override/implement the Start and Stop methods. -### Configuration +### Register Background Worker -Add your BackgroundWorker at `OnApplicationInitialization` in your [module class](Module-Development-Basics.md). The example below initialize the background worker to your module: +After creating a background worker, add it to the IBackgroundWorkerManager. The most common place is the `OnApplicationInitialization` method of your module: ````csharp -using Volo.Abp.BackgroundWorkers; - public class MyModule : AbpModule { public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -76,8 +59,17 @@ public class MyModule : AbpModule context.ServiceProvider .GetRequiredService() .Add( - context.ServiceProvider.GetRequiredService() + context.ServiceProvider.GetRequiredService() ); } } -```` \ No newline at end of file +```` + +While we generally add workers in OnApplicationInitialization, there are no restrictions on that. You can inject IBackgroundWorkerManager anywhere and add workers at runtime. IBackgroundWorkerManager will stop and release all registered workers when your application is being shut down. + +## Making Your Application Always Run + +Background jobs and 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. + +## See More +* [Background Jobs](Background-Jobs.md) \ No newline at end of file From 9e6f8411943096bdf201692528748ca7e3b7df35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 16:41:57 +0300 Subject: [PATCH 05/17] Update Background-Workers.md --- docs/en/Background-Workers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 4f7faffc71..9e9886b320 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -45,7 +45,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase ```` * If your background worker derive from `PeriodicBackgroundWorkerBase`, you should implement the `DoWork` method to perform your periodic working code. -* If you directly implement IBackgroundWorker, you will override/implement the Start and Stop methods. +* If you directly implement IBackgroundWorker, you will override/implement the `StartAsync` and `StopAsync` methods. ### Register Background Worker From c7527cb6267c4f0b58b7d1ec66ac2ea3595e8dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 16:47:57 +0300 Subject: [PATCH 06/17] updated --- docs/en/Background-Jobs.md | 2 +- docs/en/Background-Workers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Background-Jobs.md b/docs/en/Background-Jobs.md index b5dce34ddf..a0ea708896 100644 --- a/docs/en/Background-Jobs.md +++ b/docs/en/Background-Jobs.md @@ -173,5 +173,5 @@ See pre-built job manager alternatives: * [Hangfire Background Job Manager](Background-Jobs-Hangfire.md) * [RabbitMQ Background Job Manager](Background-Jobs-RabbitMq.md) -## See More +## See Also * [Background Workers](Background-Workers.md) \ No newline at end of file diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 9e9886b320..2257c0c2c2 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -71,5 +71,5 @@ While we generally add workers in OnApplicationInitialization, there are no rest Background jobs and 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. -## See More +## See Also * [Background Jobs](Background-Jobs.md) \ No newline at end of file From 7021afc0f9cc3e549caf68a8cc76bbc9fb7d4f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 20:45:18 +0300 Subject: [PATCH 07/17] Create Buttons.md --- docs/en/AspNetCore/Tag-Helpers/Buttons.md | 70 +++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/en/AspNetCore/Tag-Helpers/Buttons.md diff --git a/docs/en/AspNetCore/Tag-Helpers/Buttons.md b/docs/en/AspNetCore/Tag-Helpers/Buttons.md new file mode 100644 index 0000000000..a0685c8d3b --- /dev/null +++ b/docs/en/AspNetCore/Tag-Helpers/Buttons.md @@ -0,0 +1,70 @@ +# Buttons + +ABP framework has a special Tag Helper to create bootstrap button easily. + +`` + +## Attributes + +`` has 7 different attribute. + +* [`button-type`](#`button-type`) +* [`size`](#`size`) +* [`busy-text`](#`busy-text`) +* [`text`](#`text`) +* `icon` +* `disabled` +* `icon-type` + + +### `button-type` + +`button-type` is a selectable parameter. + +`Button` + +You can choose one of the button type listed below. + +* `Default` +* `Primary` +* `Secondary` +* `Success` +* `Danger` +* `Warning` +* `Info` +* `Light` +* `Dark` +* `Outline_Primary` +* `Outline_Secondary` +* `Outline_Success` +* `Outline_Danger` +* `Outline_Warning` +* `Outline_Info` +* `Outline_Light` +* `Outline_Dark` +* `Link` + +### `size` + +`size` is a selectable parameter. + +`Button` + +You can choose one of the size type listed below. + +* `Default` +* `Small` +* `Medium` +* `Large` +* `Block` +* `Block_Small` +* `Block_Medium` +* `Block_Large` + +### `busy-text` + +`busy-text` is a string parameter. IT shows the text while the button is busy. + +### `text` + +`text` is a string parameter that displaying on button. \ No newline at end of file From df4d3dc6d63cc333780b349d1847212fc8b1aaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 21:11:30 +0300 Subject: [PATCH 08/17] Update Background-Workers.md --- docs/en/Background-Workers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 2257c0c2c2..5da2c7532e 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -45,7 +45,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase ```` * If your background worker derive from `PeriodicBackgroundWorkerBase`, you should implement the `DoWork` method to perform your periodic working code. -* If you directly implement IBackgroundWorker, you will override/implement the `StartAsync` and `StopAsync` methods. + ### Register Background Worker From 642caf4376da4ea021b74f35855d9ca31295a1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Fri, 6 Mar 2020 21:24:52 +0300 Subject: [PATCH 09/17] Delete Buttons.md --- docs/en/AspNetCore/Tag-Helpers/Buttons.md | 70 ----------------------- 1 file changed, 70 deletions(-) delete mode 100644 docs/en/AspNetCore/Tag-Helpers/Buttons.md diff --git a/docs/en/AspNetCore/Tag-Helpers/Buttons.md b/docs/en/AspNetCore/Tag-Helpers/Buttons.md deleted file mode 100644 index a0685c8d3b..0000000000 --- a/docs/en/AspNetCore/Tag-Helpers/Buttons.md +++ /dev/null @@ -1,70 +0,0 @@ -# Buttons - -ABP framework has a special Tag Helper to create bootstrap button easily. - -`` - -## Attributes - -`` has 7 different attribute. - -* [`button-type`](#`button-type`) -* [`size`](#`size`) -* [`busy-text`](#`busy-text`) -* [`text`](#`text`) -* `icon` -* `disabled` -* `icon-type` - - -### `button-type` - -`button-type` is a selectable parameter. - -`Button` - -You can choose one of the button type listed below. - -* `Default` -* `Primary` -* `Secondary` -* `Success` -* `Danger` -* `Warning` -* `Info` -* `Light` -* `Dark` -* `Outline_Primary` -* `Outline_Secondary` -* `Outline_Success` -* `Outline_Danger` -* `Outline_Warning` -* `Outline_Info` -* `Outline_Light` -* `Outline_Dark` -* `Link` - -### `size` - -`size` is a selectable parameter. - -`Button` - -You can choose one of the size type listed below. - -* `Default` -* `Small` -* `Medium` -* `Large` -* `Block` -* `Block_Small` -* `Block_Medium` -* `Block_Large` - -### `busy-text` - -`busy-text` is a string parameter. IT shows the text while the button is busy. - -### `text` - -`text` is a string parameter that displaying on button. \ No newline at end of file From da15614f2737181bde028ffee9a84a6ca554accc Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 17 Mar 2020 21:14:46 +0800 Subject: [PATCH 10/17] Update document --- docs/zh-Hans/Application-Services.md | 55 ++++++++++++++++++++++++++-- docs/zh-Hans/CLI.md | 2 +- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/zh-Hans/Application-Services.md b/docs/zh-Hans/Application-Services.md index 41dac8edf7..e51f0af87b 100644 --- a/docs/zh-Hans/Application-Services.md +++ b/docs/zh-Hans/Application-Services.md @@ -200,7 +200,7 @@ public async Task GetAsync(Guid id) ### CRUD应用服务 -如果需要创建具有Create,Update,Delete和Get方法的简单CRUD应用服务,则可以使用ABP的基类轻松构建服务. 你可以继承CrudAppService. +如果需要创建具有Create,Update,Delete和Get方法的简单**CRUD应用服务**,则可以使用ABP的基类轻松构建服务. 你可以继承CrudAppService. 示例: @@ -218,7 +218,9 @@ public interface IBookAppService : } ```` -* ICrudAppService有泛型参数来获取实体的主键类型和CRUD操作的DTO类型(它不获取实体类型,因为实体类型未向客户端公开使用此接口). +`ICrudAppService` 有泛型参数来获取实体的主键类型和CRUD操作的DTO类型(它不获取实体类型,因为实体类型未向客户端公开使用此接口). + +> 为应用程序服务创建一个接口是最佳做法,但是ABP框架并不强制你这么做,你可以跳过接口部分. `ICrudAppService`声明以下方法: @@ -291,7 +293,52 @@ public class BookAppService : `CrudAppService`实现了`ICrudAppService`接口中声明的所有方法. 然后,你可以添加自己的自定义方法或覆盖和自定义实现. -### 生命周期 +> `CrudAppService` 有不同数量泛型参数的版本,你可以选择适合的使用. + +### AbstractKeyCrudAppService + +`CrudAppService` 要求你的实体拥有一个Id属性做为主键. 如果你使用的是复合主键,那么你无法使用它. + +`AbstractKeyCrudAppService` 实现了相同的 `ICrudAppService` 接口,但它没有假设你的主键. + +#### 示例 + +假设你有实体 `District`,它的`CityId` 和 `Name` 做为复合主键,使用 `AbstractKeyCrudAppService` 时需要你自己实现 `DeleteByIdAsync` 和 `GetEntityByIdAsync` 方法: + +````csharp +public class DistrictAppService + : AbstractKeyCrudAppService +{ + public DistrictAppService(IRepository repository) + : base(repository) + { + } + + protected override async Task DeleteByIdAsync(DistrictKey id) + { + await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name); + } -应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统. + protected override async Task GetEntityByIdAsync(DistrictKey id) + { + return await AsyncQueryableExecuter.FirstOrDefaultAsync( + Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name) + ); + } +} +```` + +这个实现需要你创建一个类做为复合键: + +````csharp +public class DistrictKey +{ + public Guid CityId { get; set; } + + public string Name { get; set; } +} +```` + +### 生命周期 +应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统. \ No newline at end of file diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 071ef0f47a..4a23d726f0 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -148,7 +148,7 @@ abp switch-to-stable [options] #### Options -`--solution-path` 或 `-sp`: 指定解决方案(.sln)文件路径. 如果未指定,CLI试寻找当前目录中的.sln文件. +`--solution-directory` 或 `-sd`: 指定解决方案文件夹. 解决方案应该在指定文件夹或子文件夹中. 如果未指定,默认为当前目录. ### login From 6dc03b67ab929cd814e5fdb96ce6f0c67b3a0a99 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 17 Mar 2020 16:36:16 +0300 Subject: [PATCH 11/17] minor refactor --- .../Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs | 4 ++-- .../Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs index 1a85471529..97132e8f79 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs @@ -17,9 +17,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building { case MobileApp.ReactNative: return "react-native"; - default: - return null; } + + throw new Exception("Mobile app folder name is not set!"); } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index 9df2c03ee4..07ada2fe3d 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -77,7 +77,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App if (context.BuildArgs.MobileApp != MobileApp.ReactNative) { - steps.Add(new RemoveFolderStep(MobileApp.ReactNative.GetFolderName()?.EnsureStartsWith('/'))); + steps.Add(new RemoveFolderStep(MobileApp.ReactNative.GetFolderName().EnsureStartsWith('/'))); } } From 1d31a1415387ef81aea26a81b2ec987cd9d9e1bf Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 17 Mar 2020 16:45:16 +0300 Subject: [PATCH 12/17] cli mobile app download documentation https://github.com/abpframework/abp/issues/2699 --- docs/en/CLI.md | 5 ++++- .../Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/en/CLI.md b/docs/en/CLI.md index 13110c5ae5..dcab1b672e 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -48,7 +48,10 @@ abp new Acme.BookStore * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. * `none`: Without UI. There are some additional options for this template: * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. - * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: + * `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks: + * `none`: no mobile application. + * `react-native`: React Native. + * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `ef`: Entity Framework Core. * `mongodb`: MongoDB. * `module`: [Module template](Startup-Templates/Module.md). Additional options: diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index f25a4ad89f..04b9168a44 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -162,6 +162,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(""); sb.AppendLine("-t|--template (default: app)"); sb.AppendLine("-u|--ui (if supported by the template)"); + sb.AppendLine("-m|--mobile (if supported by the template)"); sb.AppendLine("-d|--database-provider (if supported by the template)"); sb.AppendLine("-o|--output-folder (default: current folder)"); sb.AppendLine("-v|--version (default: latest version)"); @@ -177,6 +178,8 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(" abp new Acme.BookStore --tiered"); sb.AppendLine(" abp new Acme.BookStore -u angular"); sb.AppendLine(" abp new Acme.BookStore -u angular -d mongodb"); + sb.AppendLine(" abp new Acme.BookStore -m none"); + sb.AppendLine(" abp new Acme.BookStore -m react-native"); sb.AppendLine(" abp new Acme.BookStore -d mongodb"); sb.AppendLine(" abp new Acme.BookStore -d mongodb -o d:\\my-project"); sb.AppendLine(" abp new Acme.BookStore -t module"); From 5773c46e124b30d1cbbcbe41ad633762f7dff635 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 17 Mar 2020 23:41:33 +0800 Subject: [PATCH 13/17] Translate customizing application modules guide doc --- .../Customizing-Application-Modules-Guide.md | 2 +- ...ation-Modules-Overriding-User-Interface.md | 5 +- .../AspNet-Boilerplate-Migration-Guide.md | 3 + docs/zh-Hans/Authorization.md | 4 ++ docs/zh-Hans/CLI.md | 3 + ...-Application-Modules-Extending-Entities.md | 3 + .../Customizing-Application-Modules-Guide.md | 62 +++++++++++++++++++ ...Application-Modules-Overriding-Services.md | 3 + ...ation-Modules-Overriding-User-Interface.md | 6 ++ .../Customization-User-Interface.md | 3 + docs/zh-Hans/docs-nav.json | 38 +++++++++--- 11 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md create mode 100644 docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md create mode 100644 docs/zh-Hans/Customizing-Application-Modules-Guide.md create mode 100644 docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md create mode 100644 docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md create mode 100644 docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md diff --git a/docs/en/Customizing-Application-Modules-Guide.md b/docs/en/Customizing-Application-Modules-Guide.md index 7f913f366a..25f9daf0b3 100644 --- a/docs/en/Customizing-Application-Modules-Guide.md +++ b/docs/en/Customizing-Application-Modules-Guide.md @@ -21,7 +21,7 @@ This approach has the following benefits: However, there is a drawback: -* You may not able to **customize** the module source code as it is in your own solution. +* You may not able to **customize** the module because the module source is not in your solution. This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations. diff --git a/docs/en/Customizing-Application-Modules-Overriding-User-Interface.md b/docs/en/Customizing-Application-Modules-Overriding-User-Interface.md index afb701eacd..321ed2befd 100644 --- a/docs/en/Customizing-Application-Modules-Overriding-User-Interface.md +++ b/docs/en/Customizing-Application-Modules-Overriding-User-Interface.md @@ -3,7 +3,4 @@ You may want to override a page, a component, a JavaScript, CSS or an image file of your depended module. Overriding the UI completely depends on the UI framework you're using. Select the UI framework to continue: * [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md) -* [Angular](UI/Angular/Customization-User-Interface.md) - - - +* [Angular](UI/Angular/Customization-User-Interface.md) \ No newline at end of file diff --git a/docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md b/docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md new file mode 100644 index 0000000000..21cba49bfb --- /dev/null +++ b/docs/zh-Hans/AspNet-Boilerplate-Migration-Guide.md @@ -0,0 +1,3 @@ +# ASP.NET Boilerplate v5+ 迁移到 ABP Framework + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Authorization.md b/docs/zh-Hans/Authorization.md index 7ae525e1f4..fd3ec72943 100644 --- a/docs/zh-Hans/Authorization.md +++ b/docs/zh-Hans/Authorization.md @@ -205,6 +205,10 @@ public class AuthorAppService : ApplicationService, IAuthorAppService 参阅 [基于策略的授权](https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/policies) 文档了解如何自定义策略. +### 更改依赖模块的权限定义 + +从 `PermissionDefinitionProvider` 派生的类(就像上面的示例一样) 可以获取现有的权限定义(由依赖[模块](Module-Development-Basics.md)定义)并更改其定义. + ## IAuthorizationService ASP.NET Core 提供了 `IAuthorizationService` 用于检查权限. 注入后使用它进行条件控制权限. diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 4a23d726f0..c65312e0d5 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -48,6 +48,9 @@ abp new Acme.BookStore * `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点. * `none`: 无UI. 这个模板还有一些额外的选项: * `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点. + * `--mobile` 或者 `-m`: 指定移动应用程序框架. 默认框架是 `react-native`. 其他选项: + * `none`: 不包含移动应用程序. + * `react-native`: React Native. * `--database-provider` 或者 `-d`: 指定数据库提供程序.默认是 `ef`.其他选项: * `ef`: Entity Framework Core. * `mongodb`: MongoDB. diff --git a/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md new file mode 100644 index 0000000000..ea5345f822 --- /dev/null +++ b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md @@ -0,0 +1,3 @@ +# 自定义应用模块: 扩展实体 + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Customizing-Application-Modules-Guide.md b/docs/zh-Hans/Customizing-Application-Modules-Guide.md new file mode 100644 index 0000000000..7e799c885d --- /dev/null +++ b/docs/zh-Hans/Customizing-Application-Modules-Guide.md @@ -0,0 +1,62 @@ +# 自定义现有模块 + +ABP框架提供的设计旨在支持构建完全[模块化的应用程序](Module-Development-Basics.md)和系统. 它还提供了一些可以在任何类型的应用程序中**使用**的[预构建应用模块](Modules/Index.md) + +例如,你可以在你的应用程序中**重用**[身份管理模块](Modules/Identity.md)去添加用户,角色和权限管理. [应用程序启动模板](Startup-Templates/Application.md)已经**预装**了Identity和其他模块. + +## 复用应用模块 + +你有两个选项去复用应用模块: + +### 添加包引用 + +你可以添加相关模块的 **NuGet** 和 **NPM** 包引用到你的应用程序,并配置模块(根据它的文档)集成到你的应用程序中. + +正如前面提到,[应用程序启动模板](Startup-Templates/Application.md)已经**预装了一些基本模块**,它引用模块的NuGet和NPM包. + +这种方法具有以下优点: + +* 你的解决方案会非常**干净**,只包含你**自己的应用程序代码**. +* 你可以**很简单的**升级模块到最新的可用模板. `abp update` [CLI](CLI.md) 命令会使更新变的更加简单. 通过这种方式, 你可以获得**最新功能和Bus修复**. + +然而有一个缺点: + +* 你可能无法**自定义**模块,因为模块源码没有在你的解决方案中. + +本文档介绍了 **或者自定义或扩展** 依赖模块并且无需更改其源码,尽快与更改完整的源码比起是有限的,但仍有一些好的方法可以自定义. + +如果你不认为自己会对预构建的模块进行重大更改,那么使用包引用的方法复用模块是推荐的方法. + +### 包含源码 + +如果你想要在预构建的模块上进行**重大**更改或添加**主要功能**,但是可用的扩展点不够使用,那么可以考虑直接使用依赖模块的源码. + +这种情况下,你通常**添加模块源码**到你的解决方案中,并将**包引用替换**为本地项目引用. **[ABP CLI](CLI.md)** 可以为你自动化这一过程. + +#### 分离模块解决方案 + +你可能不希望将模块源代码**直接包含在解决方案**中. 每个模块都包含十多个项目文件,添加**多个模块**会使解决方案变的臃肿可能还会影响**开发时的加载速度**,另外你可能有不同的开发团队维护不同模块. + +无论如何,你都可以为需要的模块创建**单独的解决方案**,将依赖模块做为解决方案中的项目引用. 比如在[abp仓库](https://github.com/abpframework/abp/),我们就是这样做的. + +> 我们看到的一个问题是Visual Studio在这种方式下不能很好的工作(解决方案目录之外对本地项目的引用不能很好地支持). 如果在开发过程中出错(对于外部模块),请在Visual Studio打开应用程序的解决方案后,在命令行运行 `dotnet restore`命令. + +#### 发布的自定义模块的包 + +一个备选方案是将重新打包模块的源代码(NuGet/NPM包),使用包引用. 你可以为公司使用本地私人的Nuget/NPM服务器. + +## 模块自定义/扩展途径 + +如果你决定使用预构建模块的NuGet/NPM包引用方式. 下面的文档详细解释了如何自定义/扩展现有模块的方法: + +* [扩展实体](Customizing-Application-Modules-Extending-Entities.md) +* [重写服务](Customizing-Application-Modules-Overriding-Services.md) +* [重写界面](Customizing-Application-Modules-Overriding-User-Interface.md) + +### 另请参阅 + +另外,请参阅以下文档: + +* 参阅 [本地化文档](Localization.md) 学习如何扩展已存在的本地化资源. +* 参阅 [设置文档](Settings.md) 学习如何更改依赖模块的设置定义. +* 参阅 [授权文档](Authorization.md) 学习如何更改依赖模块的权限定义. \ No newline at end of file diff --git a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md new file mode 100644 index 0000000000..083321f7be --- /dev/null +++ b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md @@ -0,0 +1,3 @@ +# 自定义应用模块: 覆盖服务 + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md b/docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md new file mode 100644 index 0000000000..eed56807ea --- /dev/null +++ b/docs/zh-Hans/Customizing-Application-Modules-Overriding-User-Interface.md @@ -0,0 +1,6 @@ +# 重写用户界面 + +你可以想要重写页面,组件,JavaScript,CSS或你依赖模块的图片文件. 重写UI取决于你使用的UI框架. 选择UI框架以继续: + +* [ASP.NET Core (MVC / Razor Pages)](UI/AspNetCore/Customization-User-Interface.md) +* [Angular](UI/Angular/Customization-User-Interface.md) \ No newline at end of file diff --git a/docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md b/docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md new file mode 100644 index 0000000000..dfe4f0bec9 --- /dev/null +++ b/docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md @@ -0,0 +1,3 @@ +# ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南 + +TODO... \ No newline at end of file diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 6835f417cf..3837d2547f 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -42,6 +42,33 @@ } ] }, + { + "text": "指南", + "items": [ + { + "text": "自定义应用模块", + "path": "Customizing-Application-Modules-Guide.md", + "items": [ + { + "text": "扩展实体", + "path": "Customizing-Application-Modules-Extending-Entities.md" + }, + { + "text": "重写服务", + "path": "Customizing-Application-Modules-Overriding-Services.md" + }, + { + "text": "重写用户界面", + "path": "Customizing-Application-Modules-Overriding-User-Interface.md" + } + ] + }, + { + "text": "从ASP.NET Boilerplate迁移", + "path": "AspNet-Boilerplate-Migration-Guide.md" + } + ] + }, { "text": "CLI", "path": "CLI.md" @@ -249,16 +276,7 @@ }, { "text": "Tag Helpers", - "items":[ - { - "text": "在线演示", - "path": "UI/AspNetCore/Tag-Helpers/Index.md" - }, - { - "text": "按钮", - "path": "UI/AspNetCore/Tag-Helpers/Buttons.md" - } - ] + "path": "UI/AspNetCore/Tag-Helpers/Index.md" }, { "text": "仪表板和小部件(Widget)系统", From 99f23a51bc039d4de5d36d821476be73e8536175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 17 Mar 2020 19:30:12 +0300 Subject: [PATCH 14/17] Resolved #2977: Should set default roles for new external users --- .../Volo/Abp/Account/AccountAppService.cs | 10 +-- .../Pages/Account/Login.cshtml.cs | 1 + .../Volo/Abp/Identity/IdentityUserManager.cs | 23 +++++ .../Abp/Identity/IdentityUserManager_Tests.cs | 89 ++++++++++++++++++- 4 files changed, 113 insertions(+), 10 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs index 31de74efde..35d812806f 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs @@ -30,19 +30,11 @@ namespace Volo.Abp.Account (await UserManager.CreateAsync(user, input.Password)).CheckErrors(); await UserManager.SetEmailAsync(user,input.EmailAddress); - - await SetDefaultRolesAsync(user); + await UserManager.AddDefaultRolesAsync(user); return ObjectMapper.Map(user); } - protected virtual async Task SetDefaultRolesAsync(IdentityUser user) - { - var defaultRoles = await _roleRepository.GetDefaultOnesAsync(); - - await UserManager.SetRolesAsync(user, defaultRoles.Select(r => r.Name)); - } - protected virtual async Task CheckSelfRegistrationAsync() { if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled)) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 7857537acb..7d92131eef 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -212,6 +212,7 @@ namespace Volo.Abp.Account.Web.Pages.Account CheckIdentityErrors(await UserManager.CreateAsync(user)); CheckIdentityErrors(await UserManager.SetEmailAsync(user, emailAddress)); CheckIdentityErrors(await UserManager.AddLoginAsync(user, info)); + CheckIdentityErrors(await UserManager.AddDefaultRolesAsync(user)); return user; } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 8723056452..b15d765a0c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; using Volo.Abp.Threading; @@ -15,12 +16,17 @@ namespace Volo.Abp.Identity { public class IdentityUserManager : UserManager, IDomainService { + protected IIdentityRoleRepository RoleRepository { get; } + protected IIdentityUserRepository UserRepository { get; } + protected override CancellationToken CancellationToken => _cancellationTokenProvider.Token; private readonly ICancellationTokenProvider _cancellationTokenProvider; public IdentityUserManager( IdentityUserStore store, + IIdentityRoleRepository roleRepository, + IIdentityUserRepository userRepository, IOptions optionsAccessor, IPasswordHasher passwordHasher, IEnumerable> userValidators, @@ -41,6 +47,8 @@ namespace Volo.Abp.Identity services, logger) { + RoleRepository = roleRepository; + UserRepository = userRepository; _cancellationTokenProvider = cancellationTokenProvider; } @@ -76,5 +84,20 @@ namespace Volo.Abp.Identity return IdentityResult.Success; } + + public virtual async Task AddDefaultRolesAsync([NotNull] IdentityUser user) + { + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, CancellationToken); + + foreach (var role in await RoleRepository.GetDefaultOnesAsync(cancellationToken: CancellationToken)) + { + if (!user.IsInRole(role.Id)) + { + user.AddRole(role.Id); + } + } + + return await UpdateUserAsync(user); + } } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs index 761655deb2..32f344a32e 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; +using Volo.Abp.Castle.DynamicProxy; using Volo.Abp.Uow; using Xunit; @@ -43,7 +45,9 @@ namespace Volo.Abp.Identity using (var uow = _unitOfWorkManager.Begin()) { var user = await _identityUserRepository.FindByNormalizedUserNameAsync( - _lookupNormalizer.NormalizeName("david")); + _lookupNormalizer.NormalizeName("david") + ); + user.ShouldNotBeNull(); var identityResult = await _identityUserManager.SetRolesAsync(user, new List() @@ -83,5 +87,88 @@ namespace Volo.Abp.Identity await uow.CompleteAsync(); } } + + [Fact] + public async Task AddDefaultRolesAsync_In_Same_Uow() + { + await CreateRandomDefaultRoleAsync(); + + using (var uow = _unitOfWorkManager.Begin()) + { + var user = CreateRandomUser(); + + (await _identityUserManager.CreateAsync(user)).CheckErrors(); + + user.Roles.Count.ShouldBe(0); + + await _identityUserManager.AddDefaultRolesAsync(user); + + user.Roles.Count.ShouldBeGreaterThan(0); + + foreach (var roleId in user.Roles.Select(r => r.RoleId)) + { + var role = await _identityRoleRepository.GetAsync(roleId); + role.IsDefault.ShouldBe(true); + } + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task AddDefaultRolesAsync_In_Different_Uow() + { + await CreateRandomDefaultRoleAsync(); + + Guid userId; + + using (var uow = _unitOfWorkManager.Begin()) + { + var user = CreateRandomUser(); + userId = user.Id; + + (await _identityUserManager.CreateAsync(user)).CheckErrors(); + user.Roles.Count.ShouldBe(0); + await uow.CompleteAsync(); + } + + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserManager.GetByIdAsync(userId); + + await _identityUserManager.AddDefaultRolesAsync(user); + user.Roles.Count.ShouldBeGreaterThan(0); + + foreach (var roleId in user.Roles.Select(r => r.RoleId)) + { + var role = await _identityRoleRepository.GetAsync(roleId); + role.IsDefault.ShouldBe(true); + } + + await uow.CompleteAsync(); + } + } + + private async Task CreateRandomDefaultRoleAsync() + { + await _identityRoleRepository.InsertAsync( + new IdentityRole( + Guid.NewGuid(), + Guid.NewGuid().ToString() + ) + { + IsDefault = true + } + ); + } + + private static IdentityUser CreateRandomUser() + { + return new IdentityUser( + Guid.NewGuid(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString() + "@abp.io" + ); + } } } From 48a92cec06b0deb72f7ab2452da8ce45f1eaaa4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 17 Mar 2020 20:25:01 +0300 Subject: [PATCH 15/17] Resolved #3168: Revise the background worker document. --- docs/en/Background-Workers.md | 135 +++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 36 deletions(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 5da2c7532e..eda0f83ed9 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -2,74 +2,137 @@ ## Introduction -Background workers are different than [background jobs](Background-Jobs.md). They are simple independent threads in the application running in the background. Generally, they run periodically to perform some tasks. Examples; +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. +* 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 +## Create a Background Worker -A background worker is a `Singleton Depency` class that derives from the `AsyncPeriodicBackgroundWorkerBase` or `PeriodicBackgroundWorkerBase`. +A background worker should directly or indirectly implement the `IBackgroundWorker` interface. -Assume that we want to make a user passive, if he did not login to the application in last 30 days. See the code: +> 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 PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase +public class MyWorker : BackgroundWorkerBase { - private readonly IUserRepository _userRepository; - private readonly ILogger _logger; + 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, - IUserRepository userRepository, - ILogger logger - ) : base(timer, serviceScopeFactory) + AbpTimer timer, + IServiceScopeFactory serviceScopeFactory + ) : base( + timer, + serviceScopeFactory) { - _userRepository = userRepository; - _logger = logger; - Timer.Period = 5_000; //5 seconds (good for tests) + Timer.Period = 600000; //10 minutes } - protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) + protected override async Task DoWorkAsync( + PeriodicBackgroundWorkerContext workerContext) { - _logger.LogInformation($"{nameof(PassiveUserCheckerWorker)} started to work."); - - // UserRepository sets statuses of inactive users as a passive. - await _userRepository.UpdateInactiveUserStatusesAsync(); + Logger.LogInformation("Starting: Setting status of inactive users..."); + + //Resolve dependencies + var userRepository = workerContext + .ServiceProvider + .GetRequiredService(); - _logger.LogInformation($"{nameof(PassiveUserCheckerWorker)} finished it's work."); + //Do the work + await userRepository.UpdateInactiveUserStatusesAsync(); + + Logger.LogInformation("Completed: Setting status of inactive users..."); } } ```` -* If your background worker derive from `PeriodicBackgroundWorkerBase`, you should implement the `DoWork` method to perform your periodic working code. +* `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 +## Register Background Worker -After creating a background worker, add it to the IBackgroundWorkerManager. The most common place is the `OnApplicationInitialization` method of your module: +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.ServiceProvider - .GetRequiredService() - .Add( - context.ServiceProvider.GetRequiredService() - ); - } + public override void OnApplicationInitialization( + ApplicationInitializationContext context) + { + context.AddBackgroundWorker(); + } } ```` -While we generally add workers in OnApplicationInitialization, there are no restrictions on that. You can inject IBackgroundWorkerManager anywhere and add workers at runtime. IBackgroundWorkerManager will stop and release all registered workers when your application is being shut down. +`context.AddBackgroundWorker(...)` is a shortcut extension method for the expression below: + +```` +context.ServiceProvider + .GetRequiredService() + .Add( + context + .ServiceProvider + .GetRequiredService() + ); +```` + +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` is used to set options for the background workers. There is only one option currently: + +* `IsEnabled` (default: true): Used to enable/disable the background worker system for your application. ## Making Your Application Always Run -Background jobs and 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. +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) \ No newline at end of file From acc2450b09d25ae9898e4b9701b137a073b44cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 17 Mar 2020 20:25:10 +0300 Subject: [PATCH 16/17] Create BackgroundWorkersApplicationInitializationContextExtensions.cs --- ...licationInitializationContextExtensions.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkersApplicationInitializationContextExtensions.cs diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkersApplicationInitializationContextExtensions.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkersApplicationInitializationContextExtensions.cs new file mode 100644 index 0000000000..ccb3beac5d --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkersApplicationInitializationContextExtensions.cs @@ -0,0 +1,38 @@ +using System; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.BackgroundWorkers +{ + public static class BackgroundWorkersApplicationInitializationContextExtensions + { + public static ApplicationInitializationContext AddBackgroundWorker([NotNull] this ApplicationInitializationContext context) + where TWorker : IBackgroundWorker + { + Check.NotNull(context, nameof(context)); + + context.AddBackgroundWorker(typeof(TWorker)); + + return context; + } + + public static ApplicationInitializationContext AddBackgroundWorker([NotNull] this ApplicationInitializationContext context, [NotNull] Type workerType) + { + Check.NotNull(context, nameof(context)); + Check.NotNull(workerType, nameof(workerType)); + + if (!workerType.IsAssignableTo()) + { + throw new AbpException($"Given type ({workerType.AssemblyQualifiedName}) must implement the {typeof(IBackgroundWorker).AssemblyQualifiedName} interface, but it doesn't!"); + } + + context.ServiceProvider + .GetRequiredService() + .Add( + (IBackgroundWorker)context.ServiceProvider.GetRequiredService(workerType) + ); + + return context; + } + } +} From 82801c219fbb3c9928deb9eb4198a067cdb2dec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 17 Mar 2020 20:30:11 +0300 Subject: [PATCH 17/17] Update Background-Workers.md --- docs/en/Background-Workers.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index eda0f83ed9..ff05a4162c 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -112,9 +112,11 @@ While we generally add workers in `OnApplicationInitialization`, there are no re ## Options -`AbpBackgroundWorkerOptions` is used to set options for the background workers. There is only one option currently: +`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. +* `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