first
+ var queryable = await _bookRepository.GetQueryableAsync();
+
+ //Then apply any standard LINQ extension method
+ var query = queryable
+ .Where(x => x.Price > 10)
+ .OrderBy(x => x.Name);
+
+ //Finally, execute the query asynchronously
+ var books = await AsyncExecuter.ToListAsync(query);
+}
+````
+
+ABP may start a database transaction when you get an `IQueryable` (If current [Unit Of Work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) is transactional). In this new way, it is possible to **start the database transaction in an asynchronous way**. Previously, we could not get the advantage of asynchronous while starting the transactions.
+
+> **The new way has a significant performance and scalability gain. The old usage (directly using LINQ over the repositories) will be removed in the next major version.** You have a lot of time for the change, but we recommend to immediately take the action since the old usage has a big scalability problem.
+
+#### About IRepository Async Extension Methods
+
+Using IRepository Async Extension Methods has no such a problem. The examples below are pretty fine:
+
+````csharp
+var countAll = await _personRepository
+ .CountAsync();
+
+var count = await _personRepository
+ .CountAsync(x => x.Name.StartsWith("A"));
+
+var book1984 = await _bookRepository
+ .FirstOrDefaultAsync(x => x.Name == "John");
+````
+
+See the [repository documentation](https://docs.abp.io/en/abp/4.2/Repositories#iqueryable-async-operations) to understand the relation between `IQueryable` and asynchronous operations.
+
+### Repository Bulk Operations
+
+This version adds the following methods to the repositories:
+
+* `InsertManyAsync`
+* `UpdateManyAsync`
+* `DeleteManyAsync`
+
+The purpose of these methods to insert, update or delete many entities in one call with a better performance.
+
+Currently, **MongoDB** provider implements these methods as a single bulk operation since MongoDB API natively supports. But current **Entity Framework Core** implementation is not a real bulk operation. Instead, it does its best with the native API of the EF Core. If you want to implement in a more performant way, you can [customize the bulk operations](https://docs.abp.io/en/abp/4.2/Entity-Framework-Core#customize-bulk-operations) with your own implementation or by using a library. We could find a good open source library for EF Core 5.0 to implement it.
+
+### Selecting DBMS on Template Creation
+
+[ABP CLI](https://docs.abp.io/en/abp/4.2/CLI#new) now has an option to specify the DBMS when you use EF Core as the database provider.
+
+**Example: Select MySQL as the DBMS**
+
+````bash
+abp new BookStore -dbms mysql --preview
+````
+
+Available options: `SqlServer` (default), `MySQL`, `SQLite`, `Oracle-Devart`, `PostgreSQL`. See the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS) to use any other DBMS or switch the DBMS later.
+
+> If you select a DBMS other than the SQL Server, then the database migrations are also created when you first run the `DbMigrator` application.
+
+### Swagger Login
+
+TODO
+
+### Angular Unit Testing
+
+We've improved the modules and the startup template to setup and write unit tests easier with the Angular UI. See the [Angular Unit Testing document](https://docs.abp.io/en/abp/4.2/UI/Angular/Testing) for details.
+
+### Other News
+
+* Improved HTTP **request-response performance** by resolving dependencies in a deferred way in the action/page filters, interceptors and some other services.
+* Removed `MultipleActiveResultSets` from connection strings for new templates for SQL Server, since the new EF Core gives a warning when using it. If you want to use it, you need to change the connection string yourself.
+* Added `HardDeleteAsync` extension method that takes a predicate to delete multiple entities. This extension method is available if the entity [Soft Delete](https://docs.abp.io/en/abp/latest/Data-Filtering).
+* Implemented the [Page Alerts](https://docs.abp.io/en/abp/4.2/UI/Angular/Page-Alerts) for the **Angular UI**.
+* Implemented [Page Progress](https://docs.abp.io/en/abp/4.2/UI/Blazor/Page-Progress) for the **Blazor UI**. It automatically shows an undetermined progress bar on top of the page while performing an AJAX request. It also proves an API to you if you need to show/hide the progress bar in your code.
+
+## What's new with the ABP Commercial 4.2
+
+### Microservice Startup Template
+
+The new [Microservice Startup Template](https://docs.abp.io/en/commercial/4.2/startup-templates/microservice/index) is a generic solution to start a new microservice solution.
+
+While we accept that every microservice solution will be different and every system has its own design requirements and trade-offs, we believe such a startup solution is a very useful starting point for most of the solutions, and a useful example for others.
+
+
+
+*Figure: A simplified overall diagram of the microservice solution.*
+
+You can [follow the documentation](https://docs.abp.io/en/commercial/4.2/startup-templates/microservice/index) to get started with this startup template. **This template should be considered as an early release**. We will improve it and write a lot of guides.
+
+If you want to use the ABP Suite to create your solution, then you need to first upgrade it:
+
+````bash
+abp suite update
+````
+
+If you want, you can directly create a new solution from the command line:
+
+````bash
+abp new Volosoft.MyMicroserviceSystem -t microservice-pro --preview
+````
+
+Company Name is optional. Solution name could be *MyMicroserviceSystem* for this example.
+
+### Public Website in the Startup Templates
+
+As mentioned in the previous release post, we've added a *Public Website* application to the startup templates. It is configured to authenticate through the IdentityServer with a single sign-on system.
+
+You can use this application to create a landing page for your actual application or a corporate website for your business. An example screenshot:
+
+
+
+It uses the same *Lepton Theme*, so you can apply [all the styles](https://commercial.abp.io/themes). The Public Website has a different layout and also has a different setting for the styling (that can be configured in the *Settings / Lepton Theme* page of the main web application).
+
+> *Public Website* is optional and you need to select the "Public Website" option while creating a new solution using the ABP Suite, or use the `--with-public-website` option while using the `abp new` CLI command.
+
+### Easy CRM Blazor UI
+
+Easy CRM is an example application built with the ABP Commercial. MVC (Razor Pages) and Angular UI implementations were already provided. With the version 4.2, we are providing the Blazor UI implementation for this application.
+
+
+
+### Other News
+
+* Implemented Iyzico as a payment gateway provider for the [payment module](https://commercial.abp.io/modules/Volo.Payment) in addition to Paypal, Stripe, 2Checkout and Payu providers.
+
+## ABP Community News
+
+### Sharing Video Contents
+
+[community.abp.io](https://community.abp.io/) is a place to share ABP related contents. It started with publishing articles. Now, it supports to publish video contents. [See this example](https://community.abp.io/articles/be-a-superhero-on-day-1-with-abp.io-wvifcy9s). All you need to do is to create a video and upload to YouTube. Then you can [submit](https://community.abp.io/articles/submit) the YouTube link to the ABP Community website.
+
+### Multi-language support
+
+We planned ABP Community to publish English-only contents. However, we see that people want to share contents in other languages too. Now, **it is possible to submit a content in any language**. Just select the Language option while submitting your content.
+
+**When you submit a non-English content, it is not visible to all the visitors by default**. Visitors can see a non-English content only if their browser language or the selected language matches to the content language (there is a language selection at the end of the website).
+
+### External Contents
+
+If you want to publish your content anywhere else, but want to post a link of your content, you can select *External Content* option while submitting the post. For example, [this article](https://community.abp.io/articles/aspnet-boilerplate-to-abp-framework-xml-to-json-localization-conversion-0mxyjrzj) is an external article and also written in Chinese language.
+
+## About the Next Release
+
+The next feature version will be 4.3.0. It is planned to release the 4.3 RC (Release Candidate) on March 11 and the final version on March 25, 2021.
+
+We decided to slow down the feature development for the [next milestone](https://github.com/abpframework/abp/milestone/49). We will continue to improve the existing features and introduce new ones, sure, but wanted to have more time for the planning, documentation, creating guides and improving the development experience.
+
+## Feedback
+
+Please check out the ABP Framework 4.2.0 RC and [provide feedback](https://github.com/abpframework/abp/issues/new) to help us to release a more stable version. **The planned release date for the [4.2.0 final](https://github.com/abpframework/abp/milestone/48) version is January 28, 2021**.
diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png
new file mode 100644
index 0000000000..02c47777a4
Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png differ
diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png
new file mode 100644
index 0000000000..2c6df5ab74
Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png differ
diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg
new file mode 100644
index 0000000000..b5784519bd
Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg differ
diff --git a/docs/en/CSRF-Anti-Forgery.md b/docs/en/CSRF-Anti-Forgery.md
index 720edc5924..ee684aaa52 100644
--- a/docs/en/CSRF-Anti-Forgery.md
+++ b/docs/en/CSRF-Anti-Forgery.md
@@ -144,4 +144,19 @@ Let's talk about why.
First, take a look at [Angular's code](https://github.com/angular/angular/blob/master/packages/common/http/src/xsrf.ts#L81)
-It does not intercept any request that starts with `http://` or `https://`. There is a good reason for that. Any cross-site request does not need this token for security. This verification is only valid if the request is made to the same domain from which the web page is served. So, simply put, if you serve everything from a single domain, you just use a relative path.
\ No newline at end of file
+It does not intercept any request that starts with `http://` or `https://`. There is a good reason for that. Any cross-site request does not need this token for security. This verification is only valid if the request is made to the same domain from which the web page is served. So, simply put, if you serve everything from a single domain, you just use a relative path.
+
+If you serve your APIs from the root, i.e. no context root (https://testdomain.com/api/identity/users), leave `url` empty as follows:
+
+```typescript
+export const environment = {
+ production: true,
+ // ....
+ apis: {
+ default: {
+ url: '', // <- should be empty string, not '/'
+ // ...
+ },
+ },
+} as Config.Environment;
+```
diff --git a/docs/en/UI/Angular/Authorization.md b/docs/en/UI/Angular/Authorization.md
new file mode 100644
index 0000000000..75d6107341
--- /dev/null
+++ b/docs/en/UI/Angular/Authorization.md
@@ -0,0 +1,28 @@
+# Authorization in Angular UI
+
+OAuth is preconfigured in Angular application templates. So, when you start a project using the CLI (or Suite, for that matter), authorization already works. You can find **OAuth configuration** in the _environment.ts_ files.
+
+```js
+import { Config } from '@abp/ng.core';
+
+const baseUrl = 'http://localhost:4200';
+
+export const environment = {
+ // other options removed for sake of brevity
+
+ oAuthConfig: {
+ issuer: 'https://localhost:44305',
+ redirectUri: baseUrl,
+ clientId: 'MyProjectName_App',
+ responseType: 'code',
+ scope: 'offline_access MyProjectName',
+ },
+
+ // other options removed for sake of brevity
+} as Config.Environment;
+
+```
+
+This configuration results in an [OAuth authorization code flow with PKCE](https://tools.ietf.org/html/rfc7636) and we are using [angular-oauth2-oidc library](https://github.com/manfredsteyer/angular-oauth2-oidc#logging-in) for managing OAuth in the Angular client.
+
+According to this flow, the user is redirected to an external login page which is built with MVC. So, if you need **to customize the login page**, please follow [this community article](https://community.abp.io/articles/how-to-customize-the-login-page-for-mvc-razor-page-applications-9a40f3cd).
diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md
index e7e284bb0d..7622db6de0 100644
--- a/docs/en/UI/Angular/Multi-Tenancy.md
+++ b/docs/en/UI/Angular/Multi-Tenancy.md
@@ -1,4 +1,4 @@
-# Multi Tenancy in Angular UI
+# Multi-Tenancy in Angular UI
ABP Angular UI supports the multi-tenancy. The following features related to multi-tenancy are available in the startup templates.
@@ -8,7 +8,7 @@ ABP Angular UI supports the multi-tenancy. The following features related to mul
On the page above, you can;
-- See the all tenants.
+- See all tenants.
- Create a new tenant.
- Edit an existing tenant.
- Delete a tenant.
@@ -17,9 +17,11 @@ On the page above, you can;
Tenant Switching Component
-You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id to the backend as `__tenant` header on each request.
+You can switch between existing tenants by using the tenant switching box in the child pages of the MVC Account Public Module (like Login page). Angular UI gets selected tenant from `application-configuration` response and sends the tenant id to the backend as `__tenant` header on each request.
-## Domain Tenant Resolver
+## Domain/Subdomain Tenant Resolver
+
+> **Note:** If you are going to implement the steps below, you should also implement the domain/subdomain tenant resolver feature for the backend. See the [Domain/Subdomain Tenant Resolver section in Multi-Tenancy document](../../Multi-Tenancy#domain-subdomain-tenant-resolver) to learn the backend implementation.
Angular UI can get the tenant name from the app running URL. You can determine the current tenant by subdomain (like mytenant1.mydomain.com) or by the whole domain (like mytenant.com). To do this, you need to set the `application.baseUrl` property in the environment:
diff --git a/docs/en/UI/Angular/Page-Alerts.md b/docs/en/UI/Angular/Page-Alerts.md
new file mode 100644
index 0000000000..e59d620432
--- /dev/null
+++ b/docs/en/UI/Angular/Page-Alerts.md
@@ -0,0 +1,64 @@
+# Page Alerts
+
+A page alert is useful for displaying an important message to the user. The ABP Framework provides an easy way to show the following alert to the user.
+
+
+
+You can simply import `PageAlertService` from `@abp/ng.theme.shared` and utilize it as follows:
+
+```typescript
+import { PageAlertService } from '@abp/ng.theme.shared';
+
+@Component({
+ // ...
+})
+export class MyComponent {
+ constructor(private service: PageAlertService) {}
+
+ showWarning() {
+ this.service.show({
+ type: 'warning',
+ message:
+ 'We will have a service interruption between 02:00 AM and 04:00 AM at October 23, 2023!',
+ title: 'Service Interruption',
+ });
+ }
+}
+```
+
+## `SHOW`
+
+The method `show` accepts a single object that is type of `PageAlert`
+
+```typescript
+export interface PageAlert {
+ type: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark';
+ message: string;
+ title?: string;
+ dismissible?: boolean;
+ messageLocalizationParams?: string[];
+ titleLocalizationParams?: string[];
+}
+```
+
+* `type` (Required): Defines what type of alert will be shown
+* `message` (Required): The message who will be shown, also works with localization as well.
+* `title` (Optional): The title of the message. If it is not provided, the title will be hidden.
+* `dismissible` (Optional): Default is `true`. If enabled, a button on the top right corner will be shown to the users so that they can dismiss the message.
+* `messageLocalizationParams` and `titleLocalizationParams` (Optional): If the message and/or the title is a key for localization service and contains some parameters, these fields could be used to pass those parameters.
+
+### An example with Localization
+
+```typescript
+this.service.show({
+ type: 'danger',
+ message: 'AbpAccount::PagerInfo{0}{1}{2}',
+ messageLocalizationParams: ['10', '20', '30'],
+ title: 'AbpAccount::EntityNotFoundErrorMessage',
+ titleLocalizationParams: ['Test', 'id123'],
+});
+```
+
+
+
+
diff --git a/docs/en/UI/Angular/images/page-alert-warning-example.png b/docs/en/UI/Angular/images/page-alert-warning-example.png
new file mode 100644
index 0000000000..253a811a22
Binary files /dev/null and b/docs/en/UI/Angular/images/page-alert-warning-example.png differ
diff --git a/docs/en/UI/Angular/images/page-alert-with-params-example.png b/docs/en/UI/Angular/images/page-alert-with-params-example.png
new file mode 100644
index 0000000000..8fc5236de3
Binary files /dev/null and b/docs/en/UI/Angular/images/page-alert-with-params-example.png differ
diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json
index 250fed91f0..3fff6d67c1 100644
--- a/docs/en/docs-nav.json
+++ b/docs/en/docs-nav.json
@@ -681,6 +681,10 @@
{
"text": "Page Alerts",
"path": "UI/Blazor/Page-Alerts.md"
+ },
+ {
+ "text": "Page Progress",
+ "path": "UI/Blazor/Page-Progress.md"
}
]
},
@@ -754,6 +758,10 @@
"text": "Config State Service",
"path": "UI/Angular/Config-State-Service.md"
},
+ {
+ "text": "Authorization",
+ "path": "UI/Angular/Authorization.md"
+ },
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
@@ -818,6 +826,10 @@
{
"text": "Toast Overlay",
"path": "UI/Angular/Toaster-Service.md"
+ },
+ {
+ "text": "Page Alerts",
+ "path": "UI/Angular/Page-Alerts.md"
}
]
},
diff --git a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
index db434ab365..81755fbf0d 100644
--- a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
+++ b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md
@@ -1767,7 +1767,7 @@ public async Task ChangeTitleAsync(Issue issue, string title)
如前所述,领域驱动设计中的*业务逻辑*分为两部分(各层):领域逻辑和应用逻辑
-
+
领域逻辑是系统的*核心领域规则*组成,而应用逻辑则满足特定的*用例*.
@@ -1783,7 +1783,7 @@ public async Task ChangeTitleAsync(Issue issue, string title)
* 一个**后台管理系统**,UI使用Angular,通过REST API请求数据.内部员工使用这个系统来维护数据(例如,编辑商品说明).
* 一个**移动端应用程序**,它比公开的网站UI上更加简洁.它通过REST API或其它技术(例如,TCP sockets)请求数据.
-
+
每个应用程序都有不同的**需求**,不同的**用例**(应用服务方法),不同的DTO,不同的**验证**和**授权**规则等.
diff --git a/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md b/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md
index fb8ad15787..56a37396ea 100644
--- a/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md
+++ b/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md
@@ -62,22 +62,25 @@ MySQL连接字符串与SQL Server连接字符串不同. 所以检查你的解决
通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构.
-## 更改迁移DbContext
+## 更改迁移DbContext Factory
-MySQL DBMS与SQL Server有一些细微的差异. 某些模块数据库映射配置(尤其是字段长度)会导致MySQL出现问题. 例如某些[IdentityServer模块](Modules/IdentityServer.md)表就存在这样的问题,它提供了一个选项可以根据你的DBMS配置字段.
+启动模板包含***YourProjectName*MigrationsDbContextFactory**类,这是EF Core控制台命令所必须的类(比如[Add-Migration](https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/#generating--running-migrations)和[Update-Database](https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/#generating--running-migrations)),在切换到MySql数据库时,我们同时也需要修改`DbContextOptionsBuilder`
-启动模板包含*YourProjectName*MigrationsDbContext,它负责维护和迁移数据库架构. 此DbContext基本上调用依赖模块的扩展方法来配置其数据库表.
+在 *YourProjectName*MigrationsDbContextFactory 类中找到以下代码:
-打开 *YourProjectName*MigrationsDbContext 更改 `builder.ConfigureIdentityServer();` 行,如下所示:
+````csharp
+var builder = new DbContextOptionsBuilder()
+ .UseSqlServer(configuration.GetConnectionString("Default"));
+````
+
+将其替换为:
````csharp
-builder.ConfigureIdentityServer(options =>
-{
- options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
-});
+var builder = new DbContextOptionsBuilder()
+ .UseMySql(configuration.GetConnectionString("Default"));
````
-然后 `ConfigureIdentityServer()` 方法会将字段长度设置为超过MySQL的限制. 如果在创建或执行数据库迁移时遇到任何问题请参考相关的模块文档.
+如果在创建或执行数据库迁移时遇到任何问题,请参考相关模块文档
## 重新生成迁移
@@ -105,5 +108,6 @@ builder.ConfigureIdentityServer(options =>
options.DatabaseProvider = EfCoreDatabaseProvider.MySql;
});
```
+v2.9+版本无需手动设置 ([版本历史](https://github.com/abpframework/abp/blob/dev/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerModelBuilderConfigurationOptions.cs))
-相关讨论: https://github.com/abpframework/abp/issues/1920
\ No newline at end of file
+相关讨论: https://github.com/abpframework/abp/issues/1920
diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs
index 7fa2038020..255e0ac89b 100644
--- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs
@@ -25,7 +25,12 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
protected override Task GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext)
{
- var hostName = httpContext.Request.Host.Host.RemovePreFix(ProtocolPrefixes);
+ if (!httpContext.Request.Host.HasValue)
+ {
+ return Task.FromResult(null);
+ }
+
+ var hostName = httpContext.Request.Host.Value.RemovePreFix(ProtocolPrefixes);
var extractResult = FormattedStringValueExtracter.Extract(hostName, _domainFormat, ignoreCase: true);
context.Handled = true;
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Localization/nl.json b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Localization/nl.json
new file mode 100644
index 0000000000..6be7515c01
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Localization/nl.json
@@ -0,0 +1,10 @@
+{
+ "culture": "nl",
+ "texts": {
+ "Volo.Authorization:010001": "Autorisatie mislukt! De benodigde policy is niet aan u verleend.",
+ "Volo.Authorization:010002": "Autorisatie mislukt! De benodigde policy: '{PolicyName}' is niet aan u verleend.",
+ "Volo.Authorization:010003": "Autorisatie mislukt! De benodigde policy is niet aan de opgegeven resource: '{ResourceName}' verleend.",
+ "Volo.Authorization:010004": "Autorisatie mislukt! De benodigde requirement is niet aan de opgegeven resource: '{ResourceName}' verleend.",
+ "Volo.Authorization:010005": "Autorisatie mislukt! De benodigde requirements zijn niet aan de opgegeven resource: '{ResourceName}' verleend."
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs
index e9ecb41c57..1f9836d57d 100644
--- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs
+++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs
@@ -10,7 +10,7 @@ namespace Volo.Abp.BackgroundJobs.Quartz
public int RetryCount { get; set; }
public int RetryIntervalMillisecond { get; set; }
-
+
[NotNull]
public Func RetryStrategy
@@ -19,28 +19,28 @@ namespace Volo.Abp.BackgroundJobs.Quartz
set => _retryStrategy = Check.NotNull(value, nameof(value));
}
private Func _retryStrategy;
-
+
public AbpBackgroundJobQuartzOptions()
{
RetryCount = 3;
RetryIntervalMillisecond = 3000;
_retryStrategy = DefaultRetryStrategy;
}
-
+
private async Task DefaultRetryStrategy(int retryIndex, IJobExecutionContext executionContext, JobExecutionException exception)
{
exception.RefireImmediately = true;
-
- var retryCount = executionContext.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryCount));
+
+ var retryCount = executionContext.JobDetail.JobDataMap.GetString(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryCount)).To();
if (retryIndex > retryCount)
{
exception.RefireImmediately = false;
exception.UnscheduleAllTriggers = true;
return;
}
-
- var retryInterval = executionContext.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryIntervalMillisecond));
+
+ var retryInterval = executionContext.JobDetail.JobDataMap.GetString(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryIntervalMillisecond)).To();
await Task.Delay(retryInterval);
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
index 83b74297a5..42e89ffb93 100644
--- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
+++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Quartz;
using Volo.Abp.DependencyInjection;
+using Volo.Abp.Json;
namespace Volo.Abp.BackgroundJobs.Quartz
{
@@ -16,9 +17,12 @@ namespace Volo.Abp.BackgroundJobs.Quartz
protected AbpBackgroundJobQuartzOptions Options { get; }
- public QuartzBackgroundJobManager(IScheduler scheduler, IOptions options)
+ protected IJsonSerializer JsonSerializer { get; }
+
+ public QuartzBackgroundJobManager(IScheduler scheduler, IOptions options, IJsonSerializer jsonSerializer)
{
Scheduler = scheduler;
+ JsonSerializer = jsonSerializer;
Options = options.Value;
}
@@ -33,10 +37,10 @@ namespace Volo.Abp.BackgroundJobs.Quartz
{
var jobDataMap = new JobDataMap
{
- {nameof(TArgs), args},
- {JobDataPrefix+ nameof(Options.RetryCount), retryCount},
- {JobDataPrefix+ nameof(Options.RetryIntervalMillisecond), retryIntervalMillisecond},
- {JobDataPrefix+ RetryIndex, 0}
+ {nameof(TArgs), JsonSerializer.Serialize(args)},
+ {JobDataPrefix+ nameof(Options.RetryCount), retryCount.ToString()},
+ {JobDataPrefix+ nameof(Options.RetryIntervalMillisecond), retryIntervalMillisecond.ToString()},
+ {JobDataPrefix+ RetryIndex, "0"}
};
var jobDetail = JobBuilder.Create>().RequestRecovery().SetJobData(jobDataMap).Build();
diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
index b6759b256e..79776d0726 100644
--- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
+++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Quartz;
+using Volo.Abp.Json;
namespace Volo.Abp.BackgroundJobs.Quartz
{
@@ -16,15 +17,18 @@ namespace Volo.Abp.BackgroundJobs.Quartz
protected AbpBackgroundJobQuartzOptions BackgroundJobQuartzOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
+ protected IJsonSerializer JsonSerializer { get; }
public QuartzJobExecutionAdapter(
IOptions options,
IOptions backgroundJobQuartzOptions,
IBackgroundJobExecuter jobExecuter,
- IServiceScopeFactory serviceScopeFactory)
+ IServiceScopeFactory serviceScopeFactory,
+ IJsonSerializer jsonSerializer)
{
JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory;
+ JsonSerializer = jsonSerializer;
Options = options.Value;
BackgroundJobQuartzOptions = backgroundJobQuartzOptions.Value;
Logger = NullLogger>.Instance;
@@ -34,7 +38,7 @@ namespace Volo.Abp.BackgroundJobs.Quartz
{
using (var scope = ServiceScopeFactory.CreateScope())
{
- var args = (TArgs) context.JobDetail.JobDataMap.Get(nameof(TArgs));
+ var args = JsonSerializer.Deserialize(context.JobDetail.JobDataMap.GetString(nameof(TArgs)));
var jobType = Options.GetJob(typeof(TArgs)).JobType;
var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args);
try
@@ -44,16 +48,16 @@ namespace Volo.Abp.BackgroundJobs.Quartz
catch (Exception exception)
{
var jobExecutionException = new JobExecutionException(exception);
-
- var retryIndex = context.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex);
+
+ var retryIndex = context.JobDetail.JobDataMap.GetString(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex).To();
retryIndex++;
- context.JobDetail.JobDataMap.Put(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex, retryIndex);
-
+ context.JobDetail.JobDataMap.Put(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex, retryIndex.ToString());
+
await BackgroundJobQuartzOptions.RetryStrategy.Invoke(retryIndex, context, jobExecutionException);
-
+
throw jobExecutionException;
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
index de793e078f..9405337b89 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
+++ b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
@@ -12,9 +12,9 @@
-
-
-
+
+
+