diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
index 4543ac1509..acfe23fd00 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
@@ -460,6 +460,7 @@
"ReloadFromSourceConfirmationMessage": "This post will be refreshed from \"{0}\". Do you want to continue?",
"UnitPrice": "Unit Price",
"OverallDiscountAmount": "Overall Discount Amount",
- "DiscountAmount": "Discount Amount"
+ "DiscountAmount": "Discount Amount",
+ "FullChangeHistory": "Full Change History"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index 16ce5acab0..3351b7d8cf 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
@@ -420,14 +420,16 @@
"CreateSolutionFolder": "Create Solution Folder",
"CreateSolutionFolderOption": "Specifies if the project will be in a new folder in the output folder or directly the output folder.",
"BooksPageTitle": "ABP Books",
- "PackageDetailPage_NuGetPackageInstallationOptions": "There are two ways to install {0} NuGet package to your project",
+ "PackageDetailPage_NuGetPackageInstallationOptions": "There are three ways to install {0} NuGet package to your project",
"PackageDetailPage_InstallingWithABPCLI": "1: Installing with the ABP CLI",
"PackageDetailPage_InstallingWithABPCLIDescription1": "If you haven't installed the ABP CLI, first install by executing the following command in a command-line terminal",
"PackageDetailPage_InstallingWithABPCLIDescription2": "Once you have installed the ABP CLI, open a command-line terminal in the location of the project (.csproj file) you want to install it and execute the following command",
"PackageDetailPage_InstallingWithABPCLIDescription3": "It will add the {0} package reference to your project and the {1} dependency to your module class.",
- "PackageDetailPage_ManualInstallation": "2: Manual Installation",
- "PackageDetailPage_ManualInstallationDesription1": "Add {0} NuGet package reference to your project using your IDE or executing the following command",
- "PackageDetailPage_ManualInstallationDesription2": "Then add the {0} dependency to your module class as shown in the following example",
- "PackageDetailPage_SeeDocumentation": "See the documentation to learn how to use this package in your applications."
+ "PackageDetailPage_ManualInstallation": "2: Installing with the Dotnet CLI",
+ "PackageDetailPage_ManualInstallationDescription1": "Add {0} NuGet package reference to your project using your IDE or executing the following command",
+ "PackageDetailPage_ManualInstallationDescription2": "Then add the {0} dependency to your module class as shown in the following example",
+ "PackageDetailPage_SeeDocumentation": "See the documentation to learn how to use this package in your applications.",
+ "PackageDetailPage_InstallingUsingPMC": "3: Installing with the Package Manager Console",
+ "PackageDetailPage_InstallingUsingPMCDescription1": "Open the Package Manager Console in Visual Studio (Tools -> Nuget Package Manager -> Package Manager Console) and execute the following command"
}
}
diff --git a/docs/en/API/Auto-API-Controllers.md b/docs/en/API/Auto-API-Controllers.md
index e139c16d20..1644e2d28a 100644
--- a/docs/en/API/Auto-API-Controllers.md
+++ b/docs/en/API/Auto-API-Controllers.md
@@ -169,3 +169,48 @@ public class PersonAppService : ApplicationService
````
Disabled `IsMetadataEnabled` which hides this service from API explorer and it will not be discoverable. However, it still can be usable for the clients know the exact API path/route.
+
+## Replace or Remove Controllers.
+
+In addition to [Overriding a Controller](../Customizing-Application-Modules-Overriding-Services.md#example-overriding-a-controller), you can also use a completely independent **Controller** to replace the controller in the framework or module.
+
+They have the same [route](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-7.0), but can have **different** input and output parameters.
+
+### Replace built-in AbpApplicationConfigurationController
+
+The `ReplaceControllersAttribute` indicates the replaced controller type.
+
+````csharp
+[ReplaceControllers(typeof(AbpApplicationConfigurationController))]
+[Area("abp")]
+[RemoteService(Name = "abp")]
+public class ReplaceBuiltInController : AbpController
+{
+ [HttpGet("api/abp/application-configuration")]
+ public virtual Task GetAsync(MyApplicationConfigurationRequestOptions options)
+ {
+ return Task.FromResult(new MyApplicationConfigurationDto());
+ }
+}
+
+public class MyApplicationConfigurationRequestOptions : ApplicationConfigurationRequestOptions
+{
+
+}
+
+public class MyApplicationConfigurationDto : ApplicationConfigurationDto
+{
+
+}
+````
+
+### Remove contoller
+
+Configure `ControllersToRemove` of `AbpAspNetCoreMvcOptions` to remove the controllers.
+
+````csharp
+services.Configure(options =>
+{
+ options.ControllersToRemove.Add(typeof(AbpLanguagesController));
+});
+````
diff --git a/docs/en/API/Swagger-Integration.md b/docs/en/API/Swagger-Integration.md
index 56ee1de50e..40660eaab9 100644
--- a/docs/en/API/Swagger-Integration.md
+++ b/docs/en/API/Swagger-Integration.md
@@ -43,7 +43,7 @@ If you want to manually install;
## Configuration
-First, we need to use `AddAbpSwaggerGen` extension to configure Swagger in `ConfigureServices` method of our module.
+First, we need to use `AddAbpSwaggerGen` extension to configure Swagger in `ConfigureServices` method of our module:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
@@ -63,21 +63,21 @@ public override void ConfigureServices(ServiceConfigurationContext context)
}
```
-Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module.
+Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module:
```csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
- //... other configarations.
+ //... other configurations.
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API");
});
- //... other configarations.
+ //... other configurations.
}
```
@@ -103,9 +103,7 @@ For non MVC/Tiered applications, we need to configure Swagger with OAUTH to hand
> ABP Framework uses OpenIddict by default. To get more information about OpenIddict, check this [documentation](../Modules/OpenIddict.md).
-
-
-To do that, we need to use `AddAbpSwaggerGenWithOAuth` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module.
+To do that, we need to use `AddAbpSwaggerGenWithOAuth` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
@@ -130,17 +128,14 @@ public override void ConfigureServices(ServiceConfigurationContext context)
}
```
-Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module.
-
-> Do not forget to set `OAuthClientId` and `OAuthClientSecret`.
-
+Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module:
```csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
- //... other configarations.
+ //... other configurations.
app.UseAbpSwaggerUI(options =>
{
@@ -151,6 +146,8 @@ public override void OnApplicationInitialization(ApplicationInitializationContex
options.OAuthClientSecret("1q2w3e*"); // clientSecret
});
- //... other configarations.
+ //... other configurations.
}
-```
\ No newline at end of file
+```
+
+> Do not forget to set `OAuthClientId` and `OAuthClientSecret`.
diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md
index ca2067980e..1b04be560f 100644
--- a/docs/en/Background-Jobs-Hangfire.md
+++ b/docs/en/Background-Jobs-Hangfire.md
@@ -52,7 +52,7 @@ After you have installed these NuGet packages, you need to configure your projec
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
- //... other configarations.
+ //... other configurations.
ConfigureHangfire(context, configuration);
}
@@ -68,7 +68,7 @@ After you have installed these NuGet packages, you need to configure your projec
> You have to configure a storage for Hangfire.
-2. If you want to use hangfire's dashboard, you can add `UseHangfireDashboard` call in the `OnApplicationInitialization` method in `Module` class
+2. If you want to use hangfire's dashboard, you can add `UseHangfireDashboard` call in the `OnApplicationInitialization` method in `Module` class:
````csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@@ -84,7 +84,7 @@ After you have installed these NuGet packages, you need to configure your projec
### Specifying Queue
-You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue.
+You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue:
````csharp
using System.Threading.Tasks;
diff --git a/docs/en/Contribution/How-to-Contribute-abp.io-as-a-frontend-developer.md b/docs/en/Contribution/How-to-Contribute-abp.io-as-a-frontend-developer.md
index 9b3225a989..719caf844f 100644
--- a/docs/en/Contribution/How-to-Contribute-abp.io-as-a-frontend-developer.md
+++ b/docs/en/Contribution/How-to-Contribute-abp.io-as-a-frontend-developer.md
@@ -55,4 +55,4 @@ Your backend should be running successfully
There is a demo app. The path of the demo app is `npm\ng-packs\apps\dev-app`. The demo app is connected to the packages with local references. Open the terminal in `npm\ng-packs\apps\dev-app` and execute `yarn` or `npm i` in terminal. After the package installed run `npm start` or `yarn start`.
-The repo uses Nx and packages connected with `local references`. The packages path is `”npm\ng-packs\packages`
+The repo uses Nx and packages connected with `local references`. The packages path is `npm\ng-packs\packages`
diff --git a/docs/en/Distributed-Event-Bus.md b/docs/en/Distributed-Event-Bus.md
index f1b8464d59..550ea6d9f8 100644
--- a/docs/en/Distributed-Event-Bus.md
+++ b/docs/en/Distributed-Event-Bus.md
@@ -185,6 +185,51 @@ If you perform **database operations** and use the [repositories](Repositories.m
> The handler class must be registered to the dependency injection (DI). The sample above uses the `ITransientDependency` to accomplish it. See the [DI document](Dependency-Injection.md) for more options.
+## Monitoring Distributed Events
+
+The ABP Framework allows you to stay informed when your application **receives** or **sends** a distributed event. This capability enables you to track the event flow within your application and take appropriate actions based on the received or sent distributed events.
+
+### Received Events
+
+The `DistributedEventReceived` local event is published when your application receives an event from the distributed event bus. `DistributedEventReceived` class has the following fields:
+
+- **`Source`:** It represents the source of the distributed event. Source can be `Direct`, `Inbox`, `Outbox`.
+- **`EventName`:** It represents the [name](#event-name) of the event received.
+- **`EventData`:** It represents the actual data associated with the event received. Since it is of type `object`, it can hold any type of data.
+
+**Example: Get informed when your application receives an event from the distributed event bus**
+
+```csharp
+public class DistributedEventReceivedHandler : ILocalEventHandler, ITransientDependency
+{
+ public async Task HandleEventAsync(DistributedEventReceived eventData)
+ {
+ // TODO: IMPLEMENT YOUR LOGIC...
+ }
+}
+```
+
+### Sent Events
+
+The `DistributedEventSent` local event is published when your application sends an event to the distributed event bus. `DistributedEventSent` class has the following fields:
+
+- **`Source`:** It represents the source of the distributed event. Source can be `Direct`, `Inbox`, `Outbox`.
+- **`EventName`:** It represents the [name](#event-name) of the event sent.
+- **`EventData`:** It represents the actual data associated with the event sent. Since it is of type `object`, it can hold any type of data.
+
+**Example: Get informed when your application sends an event to the distributed event bus**
+
+```csharp
+public class DistributedEventSentHandler : ILocalEventHandler, ITransientDependency
+{
+ public async Task HandleEventAsync(DistributedEventSent eventData)
+ {
+ // TODO: IMPLEMENT YOUR LOGIC...
+ }
+}
+```
+
+You can seamlessly integrate event-tracking capabilities into your application by subscribing to the `DistributedEventReceived` and `DistributedEventSent` local events as above examples. This empowers you to effectively monitor the messaging flow, diagnose any potential issues, and gain valuable insights into the behavior of your distributed messaging system.
## Pre-Defined Events
diff --git a/docs/en/Image-Manipulation.md b/docs/en/Image-Manipulation.md
index 91f7c11c1e..7aff8cc1df 100644
--- a/docs/en/Image-Manipulation.md
+++ b/docs/en/Image-Manipulation.md
@@ -5,7 +5,7 @@ ABP Framework provides services to compress and resize images and implements the
## Installation
-It is suggested to use the [ABP CLI](CLI.md) to install this package.
+You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach.
### Using the ABP CLI
@@ -116,6 +116,8 @@ public enum ImageResizeMode : byte
}
```
+> See the [ImageSharp documentation](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Processing.ResizeMode.html) for more information about the resize modes.
+
### ImageResizeResult
The `ImageResizeResult` is a generic class that is used to return the result of the image resize operations. It has the following properties:
@@ -196,7 +198,31 @@ public enum ImageProcessState : byte
## Magick.NET Provider
-`Volo.Abp.Imaging.MagickNet` NuGet package implements the image operations using the [Magick.NET](https://github.com/dlemstra/Magick.NET) library. If you want to use that library as image process provider, add the `Volo.Abp.Imaging.MagickNet` NuGet package to your project, then add `AbpImagingMagickNetModule` to your [module](Module-Development-Basics.md)'s dependency list:
+`Volo.Abp.Imaging.MagickNet` NuGet package implements the image operations using the [Magick.NET](https://github.com/dlemstra/Magick.NET) library.
+
+## Installation
+
+You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach.
+
+### Using the ABP CLI
+
+Open a command line terminal in the folder of your project (.csproj file) and type the following command:
+
+```bash
+abp add-package Volo.Abp.Imaging.MagickNet
+```
+
+### Manual Installation
+
+If you want to manually install;
+
+1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project:
+
+```
+Install-Package Volo.Abp.Imaging.MagickNet
+```
+
+2. Add `AbpImagingMagickNetModule` to your [module](Module-Development-Basics.md)'s dependency list:
```csharp
[DependsOn(typeof(AbpImagingMagickNetModule))]
@@ -216,7 +242,32 @@ public class MyModule : AbpModule
## ImageSharp Provider
-`Volo.Abp.Imaging.ImageSharp` NuGet package implements the image operations using the [ImageSharp](https://github.com/SixLabors/ImageSharp) library. If you want to use that library as image process provider, add the `Volo.Abp.Imaging.ImageSharp` NuGet package to your project, then add `AbpImagingImageSharpModule` to your [module](Module-Development-Basics.md)'s dependency list:
+`Volo.Abp.Imaging.ImageSharp` NuGet package implements the image operations using the [ImageSharp](https://github.com/SixLabors/ImageSharp) library.
+
+## Installation
+
+You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach.
+
+### Using the ABP CLI
+
+Open a command line terminal in the folder of your project (.csproj file) and type the following command:
+
+```bash
+abp add-package Volo.Abp.Imaging.ImageSharp
+```
+
+### Manual Installation
+
+If you want to manually install;
+
+1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project:
+
+```
+Install-Package Volo.Abp.Imaging.ImageSharp
+```
+
+2. Add `AbpImagingImageSharpModule` to your [module](Module-Development-Basics.md)'s dependency list:
+
```csharp
[DependsOn(typeof(AbpImagingImageSharpModule))]
@@ -259,7 +310,29 @@ Configure(options =>
`Volo.Abp.Imaging.AspNetCore` NuGet package defines attributes for controller actions that can automatically compress and/or resize uploaded files.
-To use the ASP.NET Core integration, add the `Volo.Abp.Imaging.AspNetCore` NuGet package to your project and add the `AbpImagingAspNetCoreModule` module to your module's dependency list:
+## Installation
+
+You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach.
+
+### Using the ABP CLI
+
+Open a command line terminal in the folder of your project (.csproj file) and type the following command:
+
+```bash
+abp add-package Volo.Abp.Imaging.AspNetCore
+```
+
+### Manual Installation
+
+If you want to manually install;
+
+1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project:
+
+```
+Install-Package Volo.Abp.Imaging.AspNetCore
+```
+
+2. Add `AbpImagingAspNetCoreModule` to your [module](Module-Development-Basics.md)'s dependency list:
```csharp
[DependsOn(typeof(AbpImagingAspNetCoreModule))]
diff --git a/docs/en/Local-Event-Bus.md b/docs/en/Local-Event-Bus.md
index 57c12d4c3b..81c0c5a171 100644
--- a/docs/en/Local-Event-Bus.md
+++ b/docs/en/Local-Event-Bus.md
@@ -149,7 +149,7 @@ namespace AbpDemo
}
````
-That's all. `MyHandler` is **automatically discovered** by the ABP Framework and `HandleEventAsync` is called whenever a `StockCountChangedEvent` occurs. You can inject any service and perform any required logic in your handler class.
+That's all. `MyHandler` is **automatically discovered** by the ABP Framework and `HandleEventAsync` is called whenever a `StockCountChangedEvent` occurs. You can inject any service and perform any required logic in your handler class.
* **One or more handlers** can subscribe to the same event.
* A single event handler class can **subscribe to multiple events** by implementing the `ILocalEventHandler` interface for each event type.
@@ -158,6 +158,29 @@ If you perform **database operations** and use the [repositories](Repositories.m
> The handler class must be registered to the dependency injection (DI). The sample above uses the `ITransientDependency` to accomplish it. See the [DI document](Dependency-Injection.md) for more options.
+### LocalEventHandlerOrder Attribute
+
+`LocalEventHandlerOrder` attribute can be used to set the execution order for the event handlers, which can be helpful if you want to handle your event handlers in a specific order.
+
+````csharp
+[LocalEventHandlerOrder(-1)]
+public class MyHandler
+ : ILocalEventHandler,
+ ITransientDependency
+{
+ public async Task HandleEventAsync(StockCountChangedEvent eventData)
+ {
+ //TODO: your code that does something on the event
+ }
+}
+````
+
+> By default, all event handlers have an order value of 0. Thus, if you want to take certain event handlers to be executed before other event handlers, you can set the order value as a negative value.
+
+#### LocalEventHandlerOrderAttribute Properties
+
+* `Order` (`int`): Used to set the execution order for a certain event handler.
+
### Transaction & Exception Behavior
Event handlers are always executed in the same [unit of work](Unit-Of-Work.md) scope, that means in the same database transaction with the code that published the event. If an event handler throws an exception, the unit of work (database transaction) is rolled back. So, **use try-catch yourself** in the event handler if you want to hide the error.
@@ -205,7 +228,7 @@ The pre-built event types are;
* `EntityDeletedEventData` is published just after an entity was successfully deleted.
* `EntityChangedEventData` is published just after an entity was successfully created, updated or deleted. It can be a shortcut if you need to listen any type of change - instead of subscribing to the individual events.
-#### How It Was Implemented?
+### How It Was Implemented?
Pre-build events are published when you save changes to the database;
diff --git a/docs/zh-Hans/Deployment/Index.md b/docs/zh-Hans/Deployment/Index.md
index 9df24e56f0..6c58358eee 100644
--- a/docs/zh-Hans/Deployment/Index.md
+++ b/docs/zh-Hans/Deployment/Index.md
@@ -6,4 +6,4 @@
## 指南
-* [部署到群集环境](Clustered-Environment.md): 讲解了当你希望同时运行应用程序的多个实例时, 如何来配置应用程序.
+* [部署到集群环境](Clustered-Environment.md): 讲解了当你希望同时运行应用程序的多个实例时, 如何来配置应用程序.
diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index 1cf8068de2..a475aa8aa5 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -439,6 +439,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Ldap.Abstractions"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ddd.Domain.Shared", "src\Volo.Abp.Ddd.Domain.Shared\Volo.Abp.Ddd.Domain.Shared.csproj", "{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MultiTenancy.Abstractions", "src\Volo.Abp.MultiTenancy.Abstractions\Volo.Abp.MultiTenancy.Abstractions.csproj", "{86F3684C-A0A5-4943-8CFA-AE79E8E3E315}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.Abstractions", "src\Volo.Abp.Imaging.Abstractions\Volo.Abp.Imaging.Abstractions.csproj", "{32F3E84B-D02E-42BD-BC5C-0D211564EF30}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.AspNetCore", "src\Volo.Abp.Imaging.AspNetCore\Volo.Abp.Imaging.AspNetCore.csproj", "{78340A37-219E-4F2D-9AC6-40A7B467EEEC}"
@@ -1325,6 +1327,10 @@ Global
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86F3684C-A0A5-4943-8CFA-AE79E8E3E315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86F3684C-A0A5-4943-8CFA-AE79E8E3E315}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86F3684C-A0A5-4943-8CFA-AE79E8E3E315}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86F3684C-A0A5-4943-8CFA-AE79E8E3E315}.Release|Any CPU.Build.0 = Release|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1578,6 +1584,7 @@ Global
{8764DFAF-D13D-449A-9A5E-5D7F0B2D7FEF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0F80E95C-41E6-4F23-94FF-FC9D0B8D5D71} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {86F3684C-A0A5-4943-8CFA-AE79E8E3E315} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{32F3E84B-D02E-42BD-BC5C-0D211564EF30} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{44467427-E0BE-492C-B9B4-82B362C183C3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj
index 4ee09824cf..9f1051fbd5 100644
--- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj
+++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj
@@ -8,10 +8,6 @@
-
-
-
-
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs
index 61c064b376..48065bbac2 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs
@@ -1,7 +1,9 @@
using System;
+using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication.Cookies;
@@ -16,41 +18,67 @@ public static class CookieAuthenticationOptionsExtensions
///
public static CookieAuthenticationOptions IntrospectAccessToken(this CookieAuthenticationOptions options, string oidcAuthenticationScheme = "oidc")
{
- var originalHandler = options.Events.OnValidatePrincipal;
options.Events.OnValidatePrincipal = async principalContext =>
{
- originalHandler?.Invoke(principalContext);
+ if (principalContext.Principal == null || principalContext.Principal.Identity == null || !principalContext.Principal.Identity.IsAuthenticated)
+ {
+ return;
+ }
+
+ var logger = principalContext.HttpContext.RequestServices.GetRequiredService>();
- if (principalContext.Principal != null && principalContext.Principal.Identity != null && principalContext.Principal.Identity.IsAuthenticated)
+ var accessToken = principalContext.Properties.GetTokenValue("access_token");
+ if (!accessToken.IsNullOrWhiteSpace())
{
- var accessToken = principalContext.Properties.GetTokenValue("access_token");
- if (!accessToken.IsNullOrWhiteSpace())
+ var openIdConnectOptions = await GetOpenIdConnectOptions(principalContext, oidcAuthenticationScheme);
+ var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest
+ {
+ Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/introspect",
+ ClientId = openIdConnectOptions.ClientId,
+ ClientSecret = openIdConnectOptions.ClientSecret,
+ Token = accessToken
+ });
+
+ if (response.IsError)
{
- var openIdConnectOptions = principalContext.HttpContext.RequestServices.GetRequiredService>().Get(oidcAuthenticationScheme);
- if (openIdConnectOptions.Configuration == null && openIdConnectOptions.ConfigurationManager != null)
- {
- openIdConnectOptions.Configuration = await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(principalContext.HttpContext.RequestAborted);
- }
-
- var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest
- {
- Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/introspect",
- ClientId = openIdConnectOptions.ClientId,
- ClientSecret = openIdConnectOptions.ClientSecret,
- Token = accessToken
- });
-
- if (response.IsActive)
- {
- return;
- }
+ logger.LogError(response.Error);
+ await SignOutAsync(principalContext);
+ return;
}
- principalContext.RejectPrincipal();
- await principalContext.HttpContext.SignOutAsync(principalContext.Scheme.Name);
+ if (!response.IsActive)
+ {
+ logger.LogError("The access_token is not active.");
+ await SignOutAsync(principalContext);
+ return;
+ }
+
+ logger.LogInformation("The access_token is active.");
+ }
+ else
+ {
+ logger.LogError("The access_token is not found in the cookie properties, Please make sure SaveTokens of OpenIdConnectOptions is set as true.");
+ await SignOutAsync(principalContext);
}
};
return options;
}
+
+ private async static Task GetOpenIdConnectOptions(CookieValidatePrincipalContext principalContext, string oidcAuthenticationScheme)
+ {
+ var openIdConnectOptions = principalContext.HttpContext.RequestServices.GetRequiredService>().Get(oidcAuthenticationScheme);
+ if (openIdConnectOptions.Configuration == null && openIdConnectOptions.ConfigurationManager != null)
+ {
+ openIdConnectOptions.Configuration = await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(principalContext.HttpContext.RequestAborted);
+ }
+
+ return openIdConnectOptions;
+ }
+
+ private async static Task SignOutAsync(CookieValidatePrincipalContext principalContext)
+ {
+ principalContext.RejectPrincipal();
+ await principalContext.HttpContext.SignOutAsync(principalContext.Scheme.Name);
+ }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
index 5ddfd202a5..4618f31b6c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
@@ -23,6 +23,7 @@
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
index b18fdab82e..2657e5bca7 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
@@ -19,7 +19,7 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly;
typeof(AbpAspNetCoreMvcClientCommonModule),
typeof(AbpUiModule),
typeof(AbpAspNetCoreComponentsWebModule)
- )]
+)]
public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs
new file mode 100644
index 0000000000..252012feb5
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus;
+using Volo.Abp.Http;
+
+namespace Volo.Abp.AspNetCore.Components.WebAssembly;
+
+public class ClientProxyExceptionEventHandler : ILocalEventHandler, ITransientDependency
+{
+ protected NavigationManager NavigationManager { get; }
+ protected IAccessTokenProvider AccessTokenProvider { get; }
+
+ public ClientProxyExceptionEventHandler(NavigationManager navigationManager, IAccessTokenProvider accessTokenProvider)
+ {
+ NavigationManager = navigationManager;
+ AccessTokenProvider = accessTokenProvider;
+ }
+
+ public virtual async Task HandleEventAsync(ClientProxyExceptionEventData eventData)
+ {
+ if (eventData.StatusCode == 401)
+ {
+ var result = await AccessTokenProvider.RequestAccessToken();
+ if (result.Status != AccessTokenResultStatus.Success)
+ {
+ NavigationManager.NavigateToLogout("authentication/logout");
+ return;
+ }
+
+ result.TryGetToken(out var token);
+ if (token != null && DateTimeOffset.Now >= token.Expires.AddMinutes(-5))
+ {
+ NavigationManager.NavigateToLogout("authentication/logout");
+ }
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj
index 79d67f8b57..95c23d34ad 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj
+++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj
@@ -18,6 +18,7 @@
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs
index b73432b38a..dd48830a24 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs
@@ -4,6 +4,7 @@ using Volo.Abp.AspNetCore.Components.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Security;
using Volo.Abp.Timing;
@@ -13,7 +14,8 @@ namespace Volo.Abp.AspNetCore.Components;
[DependsOn(
typeof(AbpObjectMappingModule),
typeof(AbpSecurityModule),
- typeof(AbpTimingModule)
+ typeof(AbpTimingModule),
+ typeof(AbpMultiTenancyAbstractionsModule)
)]
public class AbpAspNetCoreComponentsModule : AbpModule
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs
index 364bb76c1f..1cd4b88fb8 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs
@@ -12,6 +12,8 @@ public class AbpAspNetCoreMvcOptions
public HashSet IgnoredControllersOnModelExclusion { get; }
+ public HashSet ControllersToRemove { get; }
+
public bool AutoModelValidation { get; set; }
public bool EnableRazorRuntimeCompilationOnDevelopment { get; set; }
@@ -22,6 +24,7 @@ public class AbpAspNetCoreMvcOptions
{
ConventionalControllers = new AbpConventionalControllerOptions();
IgnoredControllersOnModelExclusion = new HashSet();
+ ControllersToRemove = new HashSet();
AutoModelValidation = true;
EnableRazorRuntimeCompilationOnDevelopment = true;
ChangeControllerModelApiExplorerGroupName = true;
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
index 9a78479b9c..933f325975 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
+using Volo.Abp.AspNetCore.Controllers;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http;
@@ -80,6 +81,36 @@ public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency
{
var controllerModelsToRemove = new List();
+ if (Options.ControllersToRemove.Any())
+ {
+ var removeControllerModels = GetControllers(application)
+ .Where(cm => Options.ControllersToRemove.Contains(cm.ControllerType))
+ .ToArray();
+
+ if (removeControllerModels.Any())
+ {
+ controllerModelsToRemove.AddRange(removeControllerModels);
+ Logger.LogInformation($"Removing the controller{(removeControllerModels.Length > 1 ? "s" : "")} {removeControllerModels.Select(c => c.ControllerType.AssemblyQualifiedName).JoinAsString(", ")} from the application model");
+ }
+ }
+
+ foreach (var controllerModel in GetControllers(application))
+ {
+ var replaceControllersAttr = ReflectionHelper.GetSingleAttributeOrDefault(controllerModel.ControllerType);
+ if (replaceControllersAttr != default)
+ {
+ var replaceControllerModels = GetControllers(application)
+ .Where(cm => replaceControllersAttr.ControllerTypes.Contains(cm.ControllerType))
+ .ToArray();
+
+ if (replaceControllerModels.Any())
+ {
+ controllerModelsToRemove.AddRange(replaceControllerModels);
+ Logger.LogInformation($"Removing the controller{(replaceControllerModels.Length > 1 ? "s" : "")} {replaceControllersAttr.ControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(replaceControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
+ }
+ }
+ }
+
foreach (var controllerModel in GetControllers(application))
{
if (!controllerModel.ControllerType.IsDefined(typeof(ExposeServicesAttribute), false))
@@ -99,8 +130,11 @@ public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
- controllerModelsToRemove.AddRange(exposedControllerModels);
- Logger.LogInformation($"Removing the controller{(exposedControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(exposedControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
+ if (exposedControllerModels.Any())
+ {
+ controllerModelsToRemove.AddRange(exposedControllerModels);
+ Logger.LogInformation($"Removing the controller{(exposedControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(exposedControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
+ }
continue;
}
diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs
new file mode 100644
index 0000000000..f873f3762b
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using IdentityModel.Client;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class CookieAuthenticationOptionsExtensions
+{
+ ///
+ /// Check the access_token is expired or inactive.
+ ///
+ public static CookieAuthenticationOptions CheckTokenExpiration(this CookieAuthenticationOptions options, string oidcAuthenticationScheme = "oidc", TimeSpan? advance = null, TimeSpan? validationInterval = null)
+ {
+ advance ??= TimeSpan.FromMinutes(3);
+ validationInterval ??= TimeSpan.FromMinutes(1);
+ options.Events.OnValidatePrincipal = async principalContext =>
+ {
+ if (principalContext.Principal == null || principalContext.Principal.Identity == null || !principalContext.Principal.Identity.IsAuthenticated)
+ {
+ return;
+ }
+
+ var logger = principalContext.HttpContext.RequestServices.GetRequiredService>();
+
+ var tokenExpiresAt = principalContext.Properties.Items[".Token.expires_at"];
+ if (DateTimeOffset.TryParseExact(tokenExpiresAt, "o", null, DateTimeStyles.RoundtripKind, out var expiresAt) &&
+ expiresAt < DateTimeOffset.UtcNow.Subtract(advance.Value))
+ {
+ logger.LogInformation("The access_token is expired.");
+ await SignOutAsync(principalContext);
+ return;
+ }
+
+ if (principalContext.Properties.IssuedUtc != null && DateTimeOffset.UtcNow.Subtract(principalContext.Properties.IssuedUtc.Value) > validationInterval)
+ {
+ logger.LogInformation($"Check the access_token is active every {validationInterval.Value.TotalSeconds} seconds.");
+ var accessToken = principalContext.Properties.GetTokenValue("access_token");
+ if (!accessToken.IsNullOrWhiteSpace())
+ {
+ var openIdConnectOptions = await GetOpenIdConnectOptions(principalContext, oidcAuthenticationScheme);
+
+ var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest
+ {
+ Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/introspect",
+ ClientId = openIdConnectOptions.ClientId,
+ ClientSecret = openIdConnectOptions.ClientSecret,
+ Token = accessToken
+ });
+
+ if (response.IsError)
+ {
+ logger.LogError(response.Error);
+ await SignOutAsync(principalContext);
+ return;
+ }
+
+ if (!response.IsActive)
+ {
+ logger.LogError("The access_token is not active.");
+ await SignOutAsync(principalContext);
+ return;
+ }
+
+ logger.LogInformation("The access_token is active.");
+ principalContext.ShouldRenew = true;
+ }
+ else
+ {
+ logger.LogError("The access_token is not found in the cookie properties, Please make sure SaveTokens of OpenIdConnectOptions is set as true.");
+ await SignOutAsync(principalContext);
+ }
+ }
+ };
+
+ return options;
+ }
+
+ private async static Task GetOpenIdConnectOptions(CookieValidatePrincipalContext principalContext, string oidcAuthenticationScheme)
+ {
+ var openIdConnectOptions = principalContext.HttpContext.RequestServices.GetRequiredService>().Get(oidcAuthenticationScheme);
+ if (openIdConnectOptions.Configuration == null && openIdConnectOptions.ConfigurationManager != null)
+ {
+ openIdConnectOptions.Configuration = await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(principalContext.HttpContext.RequestAborted);
+ }
+
+ return openIdConnectOptions;
+ }
+
+ private async static Task SignOutAsync(CookieValidatePrincipalContext principalContext)
+ {
+ principalContext.RejectPrincipal();
+ await principalContext.HttpContext.SignOutAsync(principalContext.Scheme.Name);
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
index aa1090e204..0fe01e9f2b 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
+++ b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
@@ -26,6 +26,8 @@
+
+
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Controllers/ReplaceControllersAttribute.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Controllers/ReplaceControllersAttribute.cs
new file mode 100644
index 0000000000..4a7e7d6e4f
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Controllers/ReplaceControllersAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Volo.Abp.AspNetCore.Controllers;
+
+[AttributeUsage(AttributeTargets.Class)]
+public class ReplaceControllersAttribute : Attribute
+{
+ public Type[] ControllerTypes { get; }
+
+ public ReplaceControllersAttribute(params Type[] controllerTypes)
+ {
+ ControllerTypes = controllerTypes ?? Type.EmptyTypes;
+ }
+}
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo.Abp.Authorization.Abstractions.csproj b/framework/src/Volo.Abp.Authorization.Abstractions/Volo.Abp.Authorization.Abstractions.csproj
index 86c690687d..86f6a8498a 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo.Abp.Authorization.Abstractions.csproj
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo.Abp.Authorization.Abstractions.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/AbpAuthorizationAbstractionsModule.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/AbpAuthorizationAbstractionsModule.cs
index 11d6d5a634..e1e40cd205 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/AbpAuthorizationAbstractionsModule.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/AbpAuthorizationAbstractionsModule.cs
@@ -4,7 +4,7 @@ using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization;
[DependsOn(
- typeof(AbpMultiTenancyModule)
+ typeof(AbpMultiTenancyAbstractionsModule)
)]
public class AbpAuthorizationAbstractionsModule : AbpModule
{
diff --git a/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj b/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj
index 02dcdde621..81077c16f9 100644
--- a/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj
+++ b/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj
@@ -20,11 +20,13 @@
+
-
+
+
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
index cb244599c2..d8226bdee5 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
@@ -8,6 +8,7 @@ using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
using Volo.Abp.Security;
using Volo.Abp.VirtualFileSystem;
@@ -16,7 +17,8 @@ namespace Volo.Abp.Authorization;
[DependsOn(
typeof(AbpAuthorizationAbstractionsModule),
typeof(AbpSecurityModule),
- typeof(AbpLocalizationModule)
+ typeof(AbpLocalizationModule),
+ typeof(AbpMultiTenancyModule)
)]
public class AbpAuthorizationModule : AbpModule
{
diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo.Abp.BackgroundJobs.Abstractions.csproj b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo.Abp.BackgroundJobs.Abstractions.csproj
index 6a7eded3d8..3633fd45e3 100644
--- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo.Abp.BackgroundJobs.Abstractions.csproj
+++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo.Abp.BackgroundJobs.Abstractions.csproj
@@ -16,6 +16,7 @@
+
diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs
index 8d3dc6caf5..4bad5cee72 100644
--- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs
+++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs
@@ -3,12 +3,14 @@ using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection;
namespace Volo.Abp.BackgroundJobs;
[DependsOn(
- typeof(AbpJsonModule)
+ typeof(AbpJsonModule),
+ typeof(AbpMultiTenancyAbstractionsModule)
)]
public class AbpBackgroundJobsAbstractionsModule : AbpModule
{
diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo.Abp.BackgroundJobs.csproj b/framework/src/Volo.Abp.BackgroundJobs/Volo.Abp.BackgroundJobs.csproj
index a9450e134c..0ef692a9f2 100644
--- a/framework/src/Volo.Abp.BackgroundJobs/Volo.Abp.BackgroundJobs.csproj
+++ b/framework/src/Volo.Abp.BackgroundJobs/Volo.Abp.BackgroundJobs.csproj
@@ -19,6 +19,7 @@
+
diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs
index 08cc66130c..793cf99634 100644
--- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs
+++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs
@@ -6,6 +6,7 @@ using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
@@ -16,7 +17,8 @@ namespace Volo.Abp.BackgroundJobs;
typeof(AbpBackgroundWorkersModule),
typeof(AbpTimingModule),
typeof(AbpGuidsModule),
- typeof(AbpDistributedLockingAbstractionsModule)
+ typeof(AbpDistributedLockingAbstractionsModule),
+ typeof(AbpMultiTenancyModule)
)]
public class AbpBackgroundJobsModule : AbpModule
{
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
index 3d8597ca41..5fa169715c 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
+++ b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
@@ -14,10 +14,10 @@
-
-
-
-
+
+
+
+
diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj
index e2bfd7549a..33ee65ee04 100644
--- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj
+++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj
@@ -5,6 +5,8 @@
netstandard2.0;netstandard2.1;net7.0
+ enable
+ Nullable
Volo.Abp.Caching.StackExchangeRedis
Volo.Abp.Caching.StackExchangeRedis
$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs
index d6c0d8b916..817c42af18 100644
--- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs
+++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs
@@ -16,7 +16,7 @@ public class AbpCachingStackExchangeRedisModule : AbpModule
var configuration = context.Services.GetConfiguration();
var redisEnabled = configuration["Redis:IsEnabled"];
- if (redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled))
+ if (string.IsNullOrEmpty(redisEnabled) || bool.Parse(redisEnabled))
{
context.Services.AddStackExchangeRedisCache(options =>
{
diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
index ecad8b16d5..768a9ba171 100644
--- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
+++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
@@ -28,8 +28,8 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
private readonly static MethodInfo GetAbsoluteExpirationMethod;
private readonly static MethodInfo GetExpirationInSecondsMethod;
- protected IDatabase RedisDatabase => GetRedisDatabase();
- private IDatabase _redisDatabase;
+ protected IDatabase RedisDatabase => GetRedisDatabase()!;
+ private IDatabase? _redisDatabase;
protected string Instance { get; }
@@ -37,28 +37,27 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
{
var type = typeof(RedisCache);
- RedisDatabaseField = Check.NotNull(type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic), nameof(RedisDatabaseField));
+ RedisDatabaseField = Check.NotNull(type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic), nameof(RedisDatabaseField))!;
- SetScriptField = Check.NotNull(type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic), nameof(SetScriptField));
+ SetScriptField = Check.NotNull(type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic), nameof(SetScriptField))!;
+
+ ConnectMethod = Check.NotNull(type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectMethod))!;
- ConnectMethod = Check.NotNull(type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectMethod));
+ ConnectAsyncMethod = Check.NotNull(type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectAsyncMethod))!;
- ConnectAsyncMethod = Check.NotNull(type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectAsyncMethod));
+ MapMetadataMethod = Check.NotNull(type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static), nameof(MapMetadataMethod))!;
- MapMetadataMethod = Check.NotNull(type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static), nameof(MapMetadataMethod));
+ GetAbsoluteExpirationMethod = Check.NotNull(type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetAbsoluteExpirationMethod))!;
- GetAbsoluteExpirationMethod = Check.NotNull(type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetAbsoluteExpirationMethod));
+ GetExpirationInSecondsMethod = Check.NotNull(type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetExpirationInSecondsMethod))!;
- GetExpirationInSecondsMethod = Check.NotNull(type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetExpirationInSecondsMethod));
+ AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
- AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null).ToString();
+ SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
- SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null).ToString();
+ DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
- DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null).ToString();
-
- // ReSharper disable once PossibleNullReferenceException
- NotPresent = Check.NotNull(type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic), nameof(NotPresent)).GetValue(null).To();
+ NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.To();
}
public AbpRedisCache(IOptions optionsAccessor)
@@ -84,10 +83,10 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return;
}
- await (Task)ConnectAsyncMethod.Invoke(this, new object[] { token });
+ await (Task)ConnectAsyncMethod.Invoke(this, new object[] { token })!;
}
- public byte[][] GetMany(
+ public byte[]?[] GetMany(
IEnumerable keys)
{
keys = Check.NotNull(keys, nameof(keys));
@@ -95,7 +94,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return GetAndRefreshMany(keys, true);
}
- public async Task GetManyAsync(
+ public async Task GetManyAsync(
IEnumerable keys,
CancellationToken token = default)
{
@@ -161,7 +160,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
await RedisDatabase.KeyDeleteAsync(keys.Select(key => (RedisKey)(Instance + key)).ToArray());
}
- protected virtual byte[][] GetAndRefreshMany(
+ protected virtual byte[]?[] GetAndRefreshMany(
IEnumerable keys,
bool getData)
{
@@ -186,7 +185,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return bytes;
}
- protected virtual async Task GetAndRefreshManyAsync(
+ protected virtual async Task GetAndRefreshManyAsync(
IEnumerable keys,
bool getData,
CancellationToken token = default)
@@ -217,7 +216,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
protected virtual Task[] PipelineRefreshManyAndOutData(
string[] keys,
RedisValue[][] results,
- out byte[][] bytes)
+ out byte[]?[] bytes)
{
bytes = new byte[keys.Length][];
var tasks = new Task[keys.Length];
@@ -226,7 +225,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
{
if (results[i].Length >= 2)
{
- MapMetadata(results[i], out DateTimeOffset? absExpr, out TimeSpan? sldExpr);
+ MapMetadata(results[i], out var absExpr, out var sldExpr);
if (sldExpr.HasValue)
{
@@ -295,7 +294,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
out DateTimeOffset? absoluteExpiration,
out TimeSpan? slidingExpiration)
{
- var parameters = new object[] { results, null, null };
+ var parameters = new object?[] { results, null, null };
MapMetadataMethod.Invoke(this, parameters);
absoluteExpiration = (DateTimeOffset?)parameters[1];
@@ -308,7 +307,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
DistributedCacheEntryOptions options)
{
return (long?)GetExpirationInSecondsMethod.Invoke(null,
- new object[] { creationTime, absoluteExpiration, options });
+ new object?[] { creationTime, absoluteExpiration, options });
}
protected virtual DateTimeOffset? GetAbsoluteExpiration(
@@ -318,18 +317,13 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return (DateTimeOffset?)GetAbsoluteExpirationMethod.Invoke(null, new object[] { creationTime, options });
}
- private IDatabase GetRedisDatabase()
+ private IDatabase? GetRedisDatabase()
{
- if (_redisDatabase == null)
- {
- _redisDatabase = RedisDatabaseField.GetValue(this) as IDatabase;
- }
-
- return _redisDatabase;
+ return _redisDatabase ??= RedisDatabaseField.GetValue(this) as IDatabase;
}
-
+
private string GetSetScript()
{
- return SetScriptField?.GetValue(this).ToString();
+ return SetScriptField.GetValue(this)!.ToString()!;
}
}
diff --git a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj
index 52d6a3381d..adc2ef921d 100644
--- a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj
+++ b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj
@@ -5,6 +5,7 @@
netstandard2.0;netstandard2.1;net7.0
+ enable
Volo.Abp.Caching
Volo.Abp.Caching
$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpDistributedCacheOptions.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpDistributedCacheOptions.cs
index 26b2ef506b..9cb42fdf96 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpDistributedCacheOptions.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpDistributedCacheOptions.cs
@@ -25,26 +25,26 @@ public class AbpDistributedCacheOptions
/// List of all cache configurators.
/// (func argument:Name of cache)
///
- public List> CacheConfigurators { get; set; } //TODO: use a configurator interface instead?
+ public List> CacheConfigurators { get; set; } //TODO: use a configurator interface instead?
public AbpDistributedCacheOptions()
{
- CacheConfigurators = new List>();
+ CacheConfigurators = new List>();
GlobalCacheEntryOptions = new DistributedCacheEntryOptions();
KeyPrefix = "";
}
- public void ConfigureCache(DistributedCacheEntryOptions options)
+ public void ConfigureCache(DistributedCacheEntryOptions? options)
{
ConfigureCache(typeof(TCacheItem), options);
}
- public void ConfigureCache(Type cacheItemType, DistributedCacheEntryOptions options)
+ public void ConfigureCache(Type cacheItemType, DistributedCacheEntryOptions? options)
{
ConfigureCache(CacheNameAttribute.GetCacheName(cacheItemType), options);
}
- public void ConfigureCache(string cacheName, DistributedCacheEntryOptions options)
+ public void ConfigureCache(string cacheName, DistributedCacheEntryOptions? options)
{
CacheConfigurators.Add(name => cacheName != name ? null : options);
}
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs
index ff162921c9..c49225acbf 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs
@@ -33,6 +33,6 @@ public class CacheNameAttribute : Attribute
return cacheNameAttribute.Name;
}
- return cacheItemType.FullName.RemovePostFix("CacheItem");
+ return cacheItemType.FullName.RemovePostFix("CacheItem")!;
}
}
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
index a72a369655..03e42b5d92 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -31,62 +30,62 @@ public class DistributedCache :
InternalCache = internalCache;
}
- public TCacheItem Get(string key, bool? hideErrors = null, bool considerUow = false)
+ public TCacheItem? Get(string key, bool? hideErrors = null, bool considerUow = false)
{
return InternalCache.Get(key, hideErrors, considerUow);
}
- public KeyValuePair[] GetMany(IEnumerable keys, bool? hideErrors = null, bool considerUow = false)
+ public KeyValuePair[] GetMany(IEnumerable keys, bool? hideErrors = null, bool considerUow = false)
{
return InternalCache.GetMany(keys, hideErrors, considerUow);
}
- public Task[]> GetManyAsync(IEnumerable keys, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task[]> GetManyAsync(IEnumerable keys, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.GetManyAsync(keys, hideErrors, considerUow, token);
}
- public Task GetAsync(string key, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task GetAsync(string key, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.GetAsync(key, hideErrors, considerUow, token);
}
- public TCacheItem GetOrAdd(string key, Func factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false)
+ public TCacheItem? GetOrAdd(string key, Func factory, Func? optionsFactory = null, bool? hideErrors = null, bool considerUow = false)
{
return InternalCache.GetOrAdd(key, factory, optionsFactory, hideErrors, considerUow);
}
- public Task GetOrAddAsync(string key, Func> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task GetOrAddAsync(string key, Func> factory, Func? optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.GetOrAddAsync(key, factory, optionsFactory, hideErrors, considerUow, token);
}
- public KeyValuePair[] GetOrAddMany(IEnumerable keys, Func, List>> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false)
+ public KeyValuePair[] GetOrAddMany(IEnumerable keys, Func, List>> factory, Func? optionsFactory = null, bool? hideErrors = null, bool considerUow = false)
{
return InternalCache.GetOrAddMany(keys, factory, optionsFactory, hideErrors, considerUow);
}
- public Task[]> GetOrAddManyAsync(IEnumerable keys, Func, Task>>> factory, Func optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task[]> GetOrAddManyAsync(IEnumerable keys, Func, Task>>> factory, Func? optionsFactory = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.GetOrAddManyAsync(keys, factory, optionsFactory, hideErrors, considerUow, token);
}
- public void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false)
+ public void Set(string key, TCacheItem value, DistributedCacheEntryOptions? options = null, bool? hideErrors = null, bool considerUow = false)
{
InternalCache.Set(key, value, options, hideErrors, considerUow);
}
- public Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions? options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.SetAsync(key, value, options, hideErrors, considerUow, token);
}
- public void SetMany(IEnumerable> items, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false)
+ public void SetMany(IEnumerable> items, DistributedCacheEntryOptions? options = null, bool? hideErrors = null, bool considerUow = false)
{
InternalCache.SetMany(items, options, hideErrors, considerUow);
}
- public Task SetManyAsync(IEnumerable> items, DistributedCacheEntryOptions options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
+ public Task SetManyAsync(IEnumerable> items, DistributedCacheEntryOptions? options = null, bool? hideErrors = null, bool considerUow = false, CancellationToken token = default)
{
return InternalCache.SetManyAsync(items, options, hideErrors, considerUow, token);
}
@@ -141,12 +140,13 @@ public class DistributedCache :
/// The type of cache key being used.
public class DistributedCache : IDistributedCache
where TCacheItem : class
+ where TCacheKey : notnull
{
public const string UowCacheName = "AbpDistributedCache";
public ILogger> Logger { get; set; }
- protected string CacheName { get; set; }
+ protected string CacheName { get; set; } = default!;
protected bool IgnoreMultiTenancy { get; set; }
@@ -164,7 +164,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache : IDistributedCacheIndicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The cache item, or null.
- public virtual TCacheItem Get(
+ public virtual TCacheItem? Get(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false)
@@ -250,7 +250,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache[] GetMany(
+ public virtual KeyValuePair[] GetMany(
IEnumerable keys,
bool? hideErrors = null,
bool considerUow = false)
@@ -288,7 +288,7 @@ public class DistributedCache : IDistributedCache();
- var cachedValues = new List>();
+ var cachedValues = new List>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
@@ -297,7 +297,7 @@ public class DistributedCache : IDistributedCache(key, value));
+ cachedValues.Add(new KeyValuePair(key, value));
}
}
@@ -309,7 +309,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache[] GetManyFallback(
+ protected virtual KeyValuePair[] GetManyFallback(
TCacheKey[] keys,
bool? hideErrors = null,
bool considerUow = false)
@@ -340,7 +340,7 @@ public class DistributedCache : IDistributedCache new KeyValuePair(
+ .Select(key => new KeyValuePair(
key,
Get(key, false, considerUow)
)
@@ -358,7 +358,7 @@ public class DistributedCache : IDistributedCache[]> GetManyAsync(
+ public virtual async Task[]> GetManyAsync(
IEnumerable keys,
bool? hideErrors = null,
bool considerUow = false,
@@ -378,7 +378,7 @@ public class DistributedCache : IDistributedCache();
- var cachedValues = new List>();
+ var cachedValues = new List>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
@@ -387,7 +387,7 @@ public class DistributedCache : IDistributedCache(key, value));
+ cachedValues.Add(new KeyValuePair(key, value));
}
}
@@ -399,7 +399,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache[]> GetManyFallbackAsync(
+ protected virtual async Task[]> GetManyFallbackAsync(
TCacheKey[] keys,
bool? hideErrors = null,
bool considerUow = false,
@@ -434,11 +434,11 @@ public class DistributedCache : IDistributedCache>();
+ var result = new List>();
foreach (var key in keys)
{
- result.Add(new KeyValuePair(
+ result.Add(new KeyValuePair(
key,
await GetAsync(key, false, considerUow, token: token))
);
@@ -466,7 +466,7 @@ public class DistributedCache : IDistributedCacheThis will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The for the task.
/// The cache item, or null.
- public virtual async Task GetAsync(
+ public virtual async Task GetAsync(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
@@ -483,7 +483,7 @@ public class DistributedCache : IDistributedCache : IDistributedCacheIndicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The cache item.
- public virtual TCacheItem GetOrAdd(
+ public virtual TCacheItem? GetOrAdd(
TCacheKey key,
Func factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false)
{
@@ -574,10 +574,10 @@ public class DistributedCache : IDistributedCacheThis will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The for the task.
/// The cache item.
- public virtual async Task GetOrAddAsync(
+ public virtual async Task GetOrAddAsync(
TCacheKey key,
Func> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
@@ -618,15 +618,15 @@ public class DistributedCache : IDistributedCache[] GetOrAddMany(
+ public KeyValuePair[] GetOrAddMany(
IEnumerable keys,
Func, List>> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false)
{
- KeyValuePair[] result;
+ KeyValuePair[] result;
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
@@ -641,7 +641,7 @@ public class DistributedCache : IDistributedCache();
- var cachedValues = new List>();
+ var cachedValues = new List>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
@@ -650,7 +650,7 @@ public class DistributedCache : IDistributedCache(key, value));
+ cachedValues.Add(new KeyValuePair(key, value));
}
}
@@ -662,7 +662,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache x.Value != null))
{
- return result;
+ return result!;
}
var missingKeys = new List();
@@ -708,22 +708,22 @@ public class DistributedCache : IDistributedCache[]> GetOrAddManyAsync(
+ public async Task[]> GetOrAddManyAsync(
IEnumerable keys,
Func, Task>>> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
- KeyValuePair[] result;
+ KeyValuePair[] result;
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
@@ -737,7 +737,7 @@ public class DistributedCache : IDistributedCache();
- var cachedValues = new List>();
+ var cachedValues = new List>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
@@ -746,7 +746,7 @@ public class DistributedCache : IDistributedCache(key, value));
+ cachedValues.Add(new KeyValuePair(key, value));
}
}
@@ -758,7 +758,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache : IDistributedCache : IDistributedCache : IDistributedCache> items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false)
{
@@ -1012,7 +1012,7 @@ public class DistributedCache : IDistributedCache[] items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false)
{
@@ -1045,7 +1045,7 @@ public class DistributedCache : IDistributedCache> items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
@@ -1117,7 +1117,7 @@ public class DistributedCache : IDistributedCache[] items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
@@ -1516,19 +1516,19 @@ public class DistributedCache : IDistributedCache[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys)
+ protected virtual KeyValuePair[] ToCacheItems(byte[]?[] itemBytes, TCacheKey[] itemKeys)
{
if (itemBytes.Length != itemKeys.Length)
{
throw new AbpException("count of the item bytes should be same with the count of the given keys");
}
- var result = new List>();
+ var result = new List>();
- for (int i = 0; i < itemKeys.Length; i++)
+ for (var i = 0; i < itemKeys.Length; i++)
{
result.Add(
- new KeyValuePair(
+ new KeyValuePair(
itemKeys[i],
ToCacheItem(itemBytes[i])
)
@@ -1538,8 +1538,7 @@ public class DistributedCache : IDistributedCache : IDistributedCache[] ToCacheItemsWithDefaultValues(TCacheKey[] keys)
+ private static KeyValuePair[] ToCacheItemsWithDefaultValues(TCacheKey[] keys)
{
return keys
- .Select(key => new KeyValuePair(key, default))
+ .Select(key => new KeyValuePair(key, default))
.ToArray();
}
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs
index f11b8b56c6..80857aed17 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs
@@ -7,11 +7,11 @@ namespace Volo.Abp.Caching;
public interface ICacheSupportsMultipleItems
{
- byte[][] GetMany(
+ byte[]?[] GetMany(
IEnumerable keys
);
- Task GetManyAsync(
+ Task GetManyAsync(
IEnumerable keys,
CancellationToken token = default
);
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
index ab3ca560a8..af654c8d9a 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
@@ -33,7 +33,7 @@ public interface IDistributedCache
/// Indicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The cache item, or null.
- TCacheItem Get(
+ TCacheItem? Get(
TCacheKey key,
bool? hideErrors = null,
bool considerUow = false
@@ -50,7 +50,7 @@ public interface IDistributedCache
/// Indicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// List of cache items.
- KeyValuePair[] GetMany(
+ KeyValuePair[] GetMany(
IEnumerable keys,
bool? hideErrors = null,
bool considerUow = false
@@ -69,7 +69,7 @@ public interface IDistributedCache
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// /// The for the task.
/// List of cache items.
- Task[]> GetManyAsync(
+ Task[]> GetManyAsync(
IEnumerable keys,
bool? hideErrors = null,
bool considerUow = false,
@@ -84,7 +84,7 @@ public interface IDistributedCache
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The for the task.
/// The cache item, or null.
- Task GetAsync(
+ Task GetAsync(
[NotNull] TCacheKey key,
bool? hideErrors = null,
bool considerUow = false,
@@ -101,10 +101,10 @@ public interface IDistributedCache
/// Indicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The cache item.
- TCacheItem GetOrAdd(
+ TCacheItem? GetOrAdd(
TCacheKey key,
Func factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false
);
@@ -120,10 +120,10 @@ public interface IDistributedCache
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The for the task.
/// The cache item.
- Task GetOrAddAsync(
+ Task GetOrAddAsync(
[NotNull] TCacheKey key,
Func> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
@@ -139,10 +139,10 @@ public interface IDistributedCache
/// Indicates to throw or hide the exceptions for the distributed cache.
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The cache items.
- KeyValuePair[] GetOrAddMany(
+ KeyValuePair[] GetOrAddMany(
IEnumerable keys,
Func, List>> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false
);
@@ -158,10 +158,10 @@ public interface IDistributedCache
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
/// The for the task.
/// The cache items.
- Task[]> GetOrAddManyAsync(
+ Task[]> GetOrAddManyAsync(
IEnumerable keys,
Func, Task>>> factory,
- Func optionsFactory = null,
+ Func? optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
@@ -178,7 +178,7 @@ public interface IDistributedCache
void Set(
TCacheKey key,
TCacheItem value,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false
);
@@ -196,7 +196,7 @@ public interface IDistributedCache
Task SetAsync(
[NotNull] TCacheKey key,
[NotNull] TCacheItem value,
- [CanBeNull] DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
@@ -212,7 +212,7 @@ public interface IDistributedCache
/// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.
void SetMany(
IEnumerable> items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false
);
@@ -229,7 +229,7 @@ public interface IDistributedCache
/// The indicating that the operation is asynchronous.
Task SetManyAsync(
IEnumerable> items,
- DistributedCacheEntryOptions options = null,
+ DistributedCacheEntryOptions? options = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs
index 2c0622eed2..4dee5d97fc 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs
@@ -8,7 +8,7 @@ public class UnitOfWorkCacheItem
{
public bool IsRemoved { get; set; }
- public TValue Value { get; set; }
+ public TValue? Value { get; set; }
public UnitOfWorkCacheItem()
{
diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs
index 6dc7909fa8..860e9cfb27 100644
--- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs
+++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs
@@ -2,7 +2,7 @@
public static class UnitOfWorkCacheItemExtensions
{
- public static TValue GetUnRemovedValueOrNull(this UnitOfWorkCacheItem item)
+ public static TValue? GetUnRemovedValueOrNull(this UnitOfWorkCacheItem? item)
where TValue : class
{
return item != null && !item.IsRemoved ? item.Value : null;
diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs
index f9f2ca23ad..1956c64f65 100644
--- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs
+++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/Configuration/AbpConfigurationBuilderOptions.cs
@@ -8,13 +8,13 @@ public class AbpConfigurationBuilderOptions
/// Used to set assembly which is used to get the user secret id for the application.
/// Use this or