diff --git a/common.DotSettings b/common.DotSettings
index 5c1cda48fd..f2cc0339ae 100644
--- a/common.DotSettings
+++ b/common.DotSettings
@@ -38,5 +38,6 @@
False
False
False
+ True
True
\ No newline at end of file
diff --git a/docs/en/Deployment/Deploy-Azure-App-Service.md b/docs/en/Deployment/Deploy-Azure-App-Service.md
deleted file mode 100644
index e7536947bf..0000000000
--- a/docs/en/Deployment/Deploy-Azure-App-Service.md
+++ /dev/null
@@ -1,425 +0,0 @@
-# Deploy Abp Webapp to Azure App Service
-
-> In this document, you'll learn how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. Abp web apps are cross-platform and can be hosted on Linux and Windows.
-
-****Prerequisites****
-
-- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
-- A GitHub account [Create an account for free](http://github.com/).
-
-
-## Create a new ABP Framework application
-
-Create a repository on [GitHub.com](https://github.com/) (keep all the default settings)
-
-Open the command prompt and clone the repository into a folder on your computer
-
-```bash
-git clone https://github.com/your-username/your-repository-name.git
-```
-Check your dotnet version. It should be at least 3.1.x
-```bash
-dotnet --version
-```
-Install or update the *ABP CLI* using a command line window
-
-```bash
-dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
-```
-
-Open the command prompt in the *GitHub repository folder* and create a *new abp Blazor solution* with the command below
-
-```bash
-abp new YourAppName -u blazor
-```
-
-Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations
-
-```bash
-dotnet run
-```
-
-Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project
-
-```bash
-dotnet run
-```
-
-Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
-
-Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project
-
-```bash
-dotnet run
-```
-
-Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project. You should get the *ABP Framework Welcome window*
-
-Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
-
-Before the github commit, you have to delete "**/wwwroot/libs/*" at *.gitignore* file.
-
-
-
-Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository
-
-```bash
-git add .
-git commit -m initialcommit
-git push
-```
-
-
-## Create a SQL Database on Azure and change the connection string in the appsettings.json files
-
-* Login into [Azure Portal](https://portal.azure.com/)
-
-* Click **Create a resource**
-
-* Search for *SQL Database*
-
-* Click the **Create** button in the *SQL Database window*
-
-* Create a new resource group. Name it *rg[YourAppName]*
-
-* Enter *[YourAppName]Db* as database name
-
-* Create a new Server and name it *[yourappname]server*
-
-* Enter a serveradmin login and passwords. Click the **OK** button
-
-* Select your *Location*
-
-* Check *Allow Azure services to access server*
-
-* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
-
-* Click the **Review + create** button. Click **Create**
-
-* Click **Go to resource** and click **SQL server** when the SQL Database is created
-
-* Click **Networking** under Security left side menu
-
-* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
-
-* Select **Allow Azure and resources to access this seerver** and save
-
-* Go to your **SQL database**, click **Connection strings** and copy the connection string
-
-* Copy/paste the appsettings.json files of the [YourAppName].HttpApi.Host and the [YourAppName].DbMigrator project
-
-* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
-
-Open the command prompt in the [YourAppName].DbMigrator project again and enter the command below to apply the database migrations
-
-```bash
-dotnet run
-```
-Open the command prompt in the [YourAppName].HttpApi.Host project and enter the command below to check your API is working
-
-```bash
-dotnet run
-```
-
-Stop the [YourAppName].HttpApi.Host by entering CTRL+C
-
-Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
-```bash
-git add .
-git commit -m initialcommit
-git push
-```
-
-
-## Set up the Build pipeline in AzureDevops and publish the Build Artifacts
-
-* Sign in into Azure DevOps
-
-* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
-
-* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
-
-* Select **Public visibility** and click the **Create project** button
-
-* Click the **Pipelines** button to continue
-
-* Click the **Create Pipeline** button
-
- Select GitHub in the Select your repository window
-
-
-
-* Enter the Connection name. [YourAppName]GitHubConnection and click **Authorize using OAuth**
-
-* Select your **GitHub** [YourAppName]repo and click Continue
-
-* Search for **ASP.NET** in the ***Select a template*** window
-
-
-
-* Select the ASP.NET Core template and click the **Apply** button
-
-* Add the below commands block as a first step in the pipeline
-
- ```
- - task: UseDotNet@2
- inputs:
- packageType: 'sdk'
- version: '6.0.106'
- ```
-
-
-
-* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
-
-* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
-
-
-
-* Add the below commands block to the end of the pipeline
-
- ```
- - task: PublishBuildArtifacts@1
- displayName: 'Publish Artifact'
- inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)'
- ArtifactName: '$(Parameters.ArtifactName)'
- condition: succeededOrFailed()
- ```
-
-
-```
-# ASP.NET
-# Build and test ASP.NET projects.
-# Add steps that publish symbols, save build artifacts, deploy, and more:
-# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
-
-trigger:
-- main
-
-pool:
- vmImage: 'windows-latest'
-
-variables:
- solution: '**/*.sln'
- buildPlatform: 'Any CPU'
- buildConfiguration: 'Release'
-
-steps:
-- task: UseDotNet@2
- inputs:
- packageType: 'sdk'
- version: '6.0.106'
-
-- task: NuGetToolInstaller@1
-
-- task: NuGetCommand@2
- inputs:
- command: 'restore'
- restoreSolution: '$(solution)'
- feedsToUse: 'config'
- nugetConfigPath: 'NuGet.config'
-
-- task: VSBuild@1
- inputs:
- solution: '$(solution)'
- msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
- platform: '$(buildPlatform)'
- configuration: '$(buildConfiguration)'
-
-- task: VSTest@2
- inputs:
- platform: '$(buildPlatform)'
- configuration: '$(buildConfiguration)'
-
-- task: PublishBuildArtifacts@1
- displayName: 'Publish Artifact'
- inputs:
- PathtoPublish: '$(build.artifactstagingdirectory)'
- ArtifactName: '$(Parameters.ArtifactName)'
- publishLocation: 'Container'
- condition: succeededOrFailed()
-```
-
-* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
-
-* When the Build pipeline has finished. Click **1 published; 1 consumed**
-
-
-
-## Create a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
-
-* Search for Web App in the *Search the Marketplace* field
-
-* Click the **Create** button in the Web App window
-
-* Select rg[YourAppName] in the *Resource Group* dropdown
-
-* Enter [YourAppName]API in the *Name input* field
-
-* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
-
-* Enter [YourAppName]API in the *Name input* field
-
-* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
-
-* Select Windows as *Operating System*
-
-* Select the same *Region* as in the SQL server you created in Part 3
-
-
-
-* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
-
-* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
-
-
-
-* Click the **Review + create** button. Click the **Create** button
-
-* Click **Go to resource** when the Web App has been created
-
-
-
-## Create a Release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
-
-* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
-
-* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
-
-* Click the **New pipeline** button in the *No release pipelines found* window
-
-* Select *Azure App Service deployment* and click the **Apply** button
-
-
-
-* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
-
-* Click **+ Add an artifact** in the *Pipeline* tab
-
-* Select the **Build** icon as *Source type* in the *Add an artifact* window
-
-* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
-
-
-
-* Click the **Continuous deployment trigger (thunderbolt icon)**
-
-* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
-
-* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
-
-
-
-* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
-
-* Select your subscription in the *Azure subscription* dropdown.
-
-
-
-* Click **Authorize** and enter your credentials in the next screens
-
-* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
-
-* Click the **Deploy Azure App Service** task
-
-* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
-
-
-
-* Click the **Save** icon in the top menu and click **OK**
-
-* Click **Create release** in the top menu. Click **Create** to create a release
-
-* Click the *Pipeline* tab and wait until the Deployment succeeds
-
-
-
-* Open a browser and navigate to the URL of your Web App
-
-```
-https://[YourAppName]api.azurewebsites.net
-```
-
-
-
-
-
-## Create a Web App in Azure Portal to deploy [YourAppName].Blazor project
-
-* Login into [Azure Portal](https://portal.azure.com/)
-
-* Click **Create a resource**
-
-* Search for *Web App* in the *Search the Marketplace* field
-
-* Click the **Create** button in the *Web App* window
-
-* Select *rg[YourAppName]* in the *Resource Group* dropdown
-
-* Enter *[YourAppName]Blazor* in the *Name* input field
-
-* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
-
-* Select *Windows* as *Operating System*
-
-* Select the same region as the SQL server you created in Part 3
-
-* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
-
-
-
-* Click the **Review + create** button. Click **Create** button
-
-* Click **Go to resource** when the Web App has been created
-
-* Copy the URL of the Blazor Web App for later use
-
-```
-https://[YourAppName]blazor.azurewebsites.net
-```
-
-
-## Change the Web App configuration for the Azure App Service
-
-Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
-
-
-
-
-
-
-
-
-
-## Add an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
-
-* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
-
-* Click the **+ Add** link and add a **New Stage**
-
-
-
-* Select *Azure App Service deployment* and click the **Apply** button
-
-* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
-
-* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
-
-* Select your subscription in the *Azure subscription* dropdown
-
-* Select your Blazor Web App in the *App service name* dropdown
-
-* Click the **Deploy Azure App Service task**
-
-* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
-
-
-
-* Click **Save** in the top menu and click the **OK** button after
-
-* Click **Create release** in the top menu and click the **Create** button
-
-
-
-
\ No newline at end of file
diff --git a/docs/en/Deployment/Index.md b/docs/en/Deployment/Index.md
index a1c2e2d5f8..6503f8b664 100644
--- a/docs/en/Deployment/Index.md
+++ b/docs/en/Deployment/Index.md
@@ -7,6 +7,3 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
-
-
-* [Deploy Abp Webapp to Azure App Service](Deploy-Azure-App-Service.md): Explains how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview).
\ No newline at end of file
diff --git a/docs/en/Modules/OpenIddict.md b/docs/en/Modules/OpenIddict.md
index 6dc1224e59..2c8ae7572e 100644
--- a/docs/en/Modules/OpenIddict.md
+++ b/docs/en/Modules/OpenIddict.md
@@ -1,102 +1,268 @@
-## ABP OpenIddict Modules
+## ABP OpenIddict Module
-## User Interface
-
-This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
+OpenIddict module provides an integration with the [OpenIddict](https://github.com/openiddict/openiddict-core) which provides advanced authentication features like single sign-on, single log-out, and API access control. This module persists applications, scopes, and other OpenIddict-related objects to the database.
## How to Install
-This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
+This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as a package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
### The Source Code
-The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
+The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed by [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
-## Relations to Other Modules
+## User Interface
-This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
+This module implements the domain logic and database integrations but does not provide any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider purchasing the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
-## The module
+## Relations to Other Modules
-### Demo projects
+This module is based on the [Identity Module](Identity.md) and has an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
-In the module's `app` directory there are six projects(including `angular`)
+## Options
-* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
-* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication
-* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication
-* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`
-* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication
-* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication
+### OpenIddictBuilder
-#### How to run?
+`OpenIddictBuilder` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
-Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
-After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
+Example:
-### Domain module
+```csharp
+public override void PreConfigureServices(ServiceConfigurationContext context)
+{
+ PreConfigure(builder =>
+ {
+ //Set options here...
+ });
+}
+```
-There are four main entities included in this module.
+`OpenIddictBuilder` contains various extension methods to configure the OpenIddict services:
-* OpenIddictApplication: **Represents applications(client)**
-* OpenIddictScope: **Represents scopes**
-* OpenIddictAuthorization: **Represents authorizations, Track of logical chains of tokens and user consent..**
-* OpenIddictToken: **Represents various tokens.**
+- `AddServer()` registers the OpenIddict token server services in the DI container. Contains `OpenIddictServerBuilder` configurations.
+- `AddCore()` registers the OpenIddict core services in the DI container. Contains `OpenIddictCoreBuilder` configurations.
+- `AddValidation()` registers the OpenIddict token validation services in the DI container. Contains `OpenIddictValidationBuilder` configurations.
-Domain also implements four store interfaces in OpenIddict, OpenIddict uses store to manage entities, corresponding to the above four entities, Custom entity repository is used in the store.
+### OpenIddictCoreBuilder
+`OpenIddictCoreBuilder` contains extension methods to configure the OpenIddict core services.
-```cs
-//Manager
-OpenIddictApplicationManager
-OpenIddictScopeManager
-OpenIddictAuthorizationManager
-OpenIddictTokenManager
-
-//Store
-IOpenIddictApplicationStore
-IOpenIddictScopeStore
-IOpenIddictAuthorizationStore
-IOpenIddictTokenStore
-
-//Repository
-IOpenIddictApplicationRepository
-IOpenIddictScopeRepository
-IOpenIddictAuthorizationRepository
-IOpenIddictTokenRepository
+Example:
+
+```csharp
+public override void PreConfigureServices(ServiceConfigurationContext context)
+{
+ PreConfigure(builder =>
+ {
+ //Set options here...
+ });
+}
```
-We enabled most of OpenIddict's features in the `AddOpenIddict` method, You can change OpenIddict's related builder options via `PreConfigure`.
+These services contain:
-```cs
-PreConfigure(builder =>
-{
- //builder
-});
+- Adding `ApplicationStore`, `AuthorizationStore`, `ScopeStore`, `TokenStore`.
+- Replacing `ApplicationManager`, `AuthorizationManager`, `ScopeManager`, `TokenManager`.
+- Replacing `ApplicationStoreResolver`, `AuthorizationStoreResolver`, `ScopeStoreResolver`, `TokenStoreResolver`.
+- Setting `DefaultApplicationEntity`, `DefaultAuthorizationEntity`, `DefaultScopeEntity`, `DefaultTokenEntity`.
+
+### OpenIddictServerBuilder
-PreConfigure(builder =>
+`OpenIddictServerBuilder` contains extension methods to configure OpenIddict server services.
+
+Example:
+
+```csharp
+public override void PreConfigureServices(ServiceConfigurationContext context)
{
- //builder
-});
+ PreConfigure(builder =>
+ {
+ //Set options here...
+ });
+}
+```
+
+These services contain:
+
+- Registering claims, scopes.
+- Setting the `Issuer` URI that is used as the base address for the endpoint URIs returned from the discovery endpoint.
+- Adding development signing keys, encryption/signing keys, credentials, and certificates.
+- Adding/removing event handlers.
+- Enabling/disabling grant types.
+- Setting authentication server endpoint URIs.
+
+### OpenIddictValidationBuilder
-PreConfigure(builder =>
+`OpenIddictValidationBuilder` contains extension methods to configure OpenIddict validation services.
+
+Example:
+
+```csharp
+public override void PreConfigureServices(ServiceConfigurationContext context)
{
- //builder
-});
+ PreConfigure(builder =>
+ {
+ //Set options here...
+ });
+}
```
-#### AbpOpenIddictAspNetCoreOptions
+These services contain:
+
+- `AddAudiances()` for resource servers.
+- `SetIssuer()` URI that is used to determine the actual location of the OAuth 2.0/OpenID Connect configuration document when using provider discovery.
+- `SetConfiguration()` to configure `OpenIdConnectConfiguration`.
+- `UseIntrospection()` to use introspection instead of local/direct validation.
+- Adding encryption key, credentials, and certificates.
+- Adding/removing event handlers.
+- `SetClientId() ` to set the client identifier `client_id ` when communicating with the remote authorization server (e.g for introspection).
+- `SetClientSecret()` to set the identifier `client_secret` when communicating with the remote authorization server (e.g for introspection).
+- `EnableAuthorizationEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and can only be used with an OpenIddict-based authorization server.
+- `EnableTokenEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and it is required when the OpenIddict server is configured to use reference tokens.
+- `UseLocalServer()` to register the OpenIddict validation/server integration services.
+- `UseAspNetCore()` to register the OpenIddict validation services for ASP.NET Core in the DI container.
+
+## Internals
+
+### Domain Layer
+
+#### Aggregates
+
+##### OpenIddictApplication
+
+OpenIddictApplications represent the applications that can request tokens from your OpenIddict Server.
+
+- `OpenIddictApplications` (aggregate root): Represents an OpenIddict application.
+ - `ClientId` (string): The client identifier associated with the current application.
+ - `ClientSecret` (string): The client secret associated with the current application. Maybe hashed or encrypted for security reasons.
+ - `ConsentType` (string): The consent type associated with the current application.
+ - `DisplayName` (string): The display name associated with the current application.
+ - `DisplayNames` (string): The localized display names associated with the current application serialized as a JSON object.
+ - `Permissions` (string): The permissions associated with the current application, serialized as a JSON array.
+ - `PostLogoutRedirectUris` (string): The logout callback URLs associated with the current application, serialized as a JSON array.
+ - `Properties` (string): The additional properties associated with the current application serialized as a JSON object or null.
+ - `RedirectUris` (string): The callback URLs associated with the current application, serialized as a JSON array.
+ - `Requirements` (string): The requirements associated with the current application
+ - `Type` (string): The application type associated with the current application.
+ - `ClientUri` (string): URI to further information about client.
+ - `LogoUri` (string): URI to client logo.
+
+##### OpenIddictAuthorization
+
+OpenIddictAuthorizations are used to keep the allowed scopes, authorization flow types.
+
+- `OpenIddictAuthorization` (aggregate root): Represents an OpenIddict authorization.
+
+ - `ApplicationId` (Guid?): The application associated with the current authorization.
+
+ - `Properties` (string): The additional properties associated with the current authorization serialized as a JSON object or null.
+
+ - `Scopes` (string): The scopes associated with the current authorization, serialized as a JSON array.
+
+ - `Status` (string): The status of the current authorization.
+
+ - `Subject` (string): The subject associated with the current authorization.
+
+ - `Type` (string): The type of the current authorization.
+
+##### OpenIddictScope
+
+OpenIddictScopes are used to keep the scopes of resources.
+
+- `OpenIddictScope` (aggregate root): Represents an OpenIddict scope.
+
+ - `Description` (string): The public description associated with the current scope.
+
+ - `Descriptions` (string): The localized public descriptions associated with the current scope, serialized as a JSON object.
+
+ - `DisplayName` (string): The display name associated with the current scope.
+
+ - `DisplayNames` (string): The localized display names associated with the current scope serialized as a JSON object.
+
+ - `Name` (string): The unique name associated with the current scope.
+ - `Properties` (string): The additional properties associated with the current scope serialized as a JSON object or null.
+ - `Resources` (string): The resources associated with the current scope, serialized as a JSON array.
+
+##### OpenIddictToken
+
+OpenIddictTokens are used to persist the application tokens.
+
+- `OpenIddictToken` (aggregate root): Represents an OpenIddict token.
+
+ - `ApplicationId` (Guid?): The application associated with the current token.
+ - `AuthorizationId` (Guid?): The application associated with the current token.
+ - `CreationDate` (DateTime?): The UTC creation date of the current token.
+ - `ExpirationDate` (DateTime?): The UTC expiration date of the current token.
+ - `Payload` (string): The payload of the current token, if applicable. Only used for reference tokens and may be encrypted for security reasons.
+
+ - `Properties` (string): The additional properties associated with the current token serialized as a JSON object or null.
+ - `RedemptionDate` (DateTime?): The UTC redemption date of the current token.
+ - `Status` (string): The status of the current authorization.
+
+ - `ReferenceId` (string): The reference identifier associated with the current token, if applicable. Only used for reference tokens and may be hashed or encrypted for security reasons.
+
+ - `Status` (string): The status of the current token.
+
+ - `Subject` (string): The subject associated with the current token.
+
+ - `Type` (string): The type of the current token.
-`UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims.
-`AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
+#### Stores
-You can also change this options via `PreConfigure`.
+This module implements OpenIddict stores:
-#### Automatically removing orphaned tokens/authorizations
+- `IAbpOpenIdApplicationStore`
+- `IOpenIddictAuthorizationStore`
+- `IOpenIddictScopeStore`
+- `IOpenIddictTokenStore`
-There is a background task in the `Domain` module (`enabled by default`) that automatically removes orphaned tokens/authorizations, you can configure `TokenCleanupOptions` to manage it.
+##### Repositories
-### ASP NET Core module
+The following custom repositories are defined in this module:
+
+- `IOpenIddictApplicationRepository`
+- `IOpenIddictAuthorizationRepository`
+- `IOpenIddictScopeRepository`
+- `IOpenIddictTokenRepository`
+
+##### Domain Services
+
+This module doesn't contain any domain service but overrides the service below:
+
+- `AbpApplicationManager` used to populate/get `AbpApplicationDescriptor` information that contains `ClientUri` and `LogoUri`.
+
+### Database Providers
+
+#### Common
+
+##### Table/Collection Prefix & Schema
+
+All tables/collections use the `OpenIddict` prefix by default. Set static properties on the `AbpOpenIddictDbProperties` class if you need to change the table prefix or set a schema name (if supported by your database provider).
+
+##### Connection String
+
+This module uses `AbpOpenIddict` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string.
+
+See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details.
+
+#### Entity Framework Core
+
+##### Tables
+
+- **OpenIddictApplications**
+- **OpenIddictAuthorizations**
+- **OpenIddictScopes**
+- **OpenIddictTokens**
+
+#### MongoDB
+
+##### Collections
+
+- **OpenIddictApplications**
+- **OpenIddictAuthorizations**
+- **OpenIddictScopes**
+- **OpenIddictTokens**
+
+## ASP.NET Core Module
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode).
@@ -107,15 +273,57 @@ LogoutController -> connect/logout
UserInfoController -> connect/userinfo
```
-> We will implement the related functions of **device flow** in the PRO module..
+> **Device flow** implementation will be done in the commercial module.
+
+#### AbpOpenIddictAspNetCoreOptions
+
+`AbpOpenIddictAspNetCoreOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
+
+Example:
+
+```csharp
+PreConfigure(options =>
+{
+ //Set options here...
+});
+```
+
+`AbpOpenIddictAspNetCoreOptions` properties:
+
+- `UpdateAbpClaimTypes(default: true)`: Updates `AbpClaimTypes` to be compatible with the Openiddict claims.
+- `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
+
+#### Automatically Removing Orphaned Tokens/Authorizations
+
+The background task that automatically removes orphaned tokens/authorizations. This can be configured by `TokenCleanupOptions` to manage it.
+
+`TokenCleanupOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
+
+Example:
+
+```csharp
+PreConfigure(options =>
+{
+ //Set options here...
+});
+```
+
+`TokenCleanupOptions` properties:
+
+- `IsCleanupEnabled` (default: true): Enable/disable token clean up.
+- `CleanupPeriod` (default: 3,600,000 ms): Setting clean up period.
+- `DisableAuthorizationPruning`: Setting a boolean indicating whether authorizations pruning should be disabled.
+- `DisableTokenPruning`: Setting a boolean indicating whether token pruning should be disabled.
+- `MinimumAuthorizationLifespan` (default: 14 days): Setting the minimum lifespan authorizations must have to be pruned. Cannot be less than 10 minutes.
+- `MinimumTokenLifespan` (default: 14 days): Setting the minimum lifespan tokens must have to be pruned. Cannot be less than 10 minutes.
-#### How to control claims in access_token and id_token
+#### Updating Claims In Access_token and Id_token
-You can use the [Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) to add/remove claims to the `ClaimsPrincipal`.
+[Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`.
-The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`.
+The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email,` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`.
-You can create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims
+Create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims.
```cs
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency
@@ -133,30 +341,11 @@ Configure(options =>
});
```
-For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
-
-### EF Core module
-
-Implements the above four repository interfaces.
-
-### MongoDB module
-
-Implements the above four repository interfaces.
-
-
-## OpenIddict
+For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
-### Documentation
+#### Disable AccessToken Encryption
-For more details about OpenIddict, please refer to its official documentation and Github.
-
-https://documentation.openiddict.com
-
-https://github.com/openiddict/openiddict-core#resources
-
-### Disable AccessToken Encryption
-
-ABP disables the `access token encryption` by default for compatibility, you can manually enable it if needed.
+ABP disables the `access token encryption` by default for compatibility, it can be enabled manually if needed.
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
@@ -170,22 +359,15 @@ public override void PreConfigureServices(ServiceConfigurationContext context)
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
-
-### PKCE
-
-https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
-
-### Request/Response process
-
-I will briefly introduce the principle of OpenIddict so that everyone can quickly understand it.
+### Request/Response Process
The `OpenIddict.Server.AspNetCore` adds an authentication scheme(`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) and implements the `IAuthenticationRequestHandler` interface.
It will be executed first in `AuthenticationMiddleware` and can short-circuit the current request. Otherwise, `DefaultAuthenticateScheme` will be called and continue to execute the pipeline.
-`OpenIddictServerAspNetCoreHandler` will call various built-in handlers(Handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
+`OpenIddictServerAspNetCoreHandler` will call various built-in handlers (handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
-Example a token request:
+Example of a token request:
```
POST /connect/token HTTP/1.1
@@ -199,11 +381,11 @@ Content-Type: application/x-www-form-urlencoded
scope=AbpAPI offline_access
```
-This request will be processed by various handlers. They will confirm the endpoint type of the request, check `http/https`, verify that the request parameters (`client. scope etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
+This request will be processed by various handlers. They will confirm the endpoint type of the request, check `HTTP/HTTPS`, verify that the request parameters (`client. scope, etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
-If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the http request at this time. The rest of our work will be based on this object.
+If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the HTTP request at this time. The rest will be based on this object.
-We may check the `username` and `password` in the request. If it is correct we create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
+Check the `username` and `password` in the request. If it is correct create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
`OpenIddictServerAspNetCoreHandler` do some checks to generate json and replace the http response content.
@@ -214,10 +396,26 @@ If you need to customize OpenIddict, you need to replace/delete/add new handlers
Please refer to:
https://documentation.openiddict.com/guides/index.html#events-model
-## Migrating Guide
+### PKCE
-[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)
+https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
-## Sponsor
+## Demo projects
+
+In the module's `app` directory there are six projects(including `angular`)
+
+* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
+* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication.
+* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication.
+* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`.
+* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication.
+* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication.
-Please consider sponsoring this project: https://github.com/sponsors/kevinchalet
+#### How to run?
+
+Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
+After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
+
+## Migrating Guide
+
+[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)
diff --git a/docs/en/UI/Angular/Current-User.md b/docs/en/UI/Angular/Current-User.md
new file mode 100644
index 0000000000..820a787b5c
--- /dev/null
+++ b/docs/en/UI/Angular/Current-User.md
@@ -0,0 +1,20 @@
+# Angular UI: Current User
+
+The current user information stored in Config State.
+
+### How to Get a Current User Information Configuration
+
+You can use the `getOne` or `getOne$` method of `ConfigStateService` to get a specific configuration property. For that, the property name should be passed to the method as parameter.
+
+```js
+// this.config is an instance of ConfigStateService
+
+const currentUser = this.config.getOne("currentUser");
+
+// or
+this.config.getOne$("currentUser").subscribe(currentUser => {
+ // use currentUser here
+})
+```
+
+> See the [ConfigStateService](./Config-State-Service) for more information.
diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json
index 4b82495a00..b919955c6e 100644
--- a/docs/en/docs-nav.json
+++ b/docs/en/docs-nav.json
@@ -981,6 +981,10 @@
"text": "Authorization",
"path": "UI/Angular/Authorization.md"
},
+ {
+ "text": "Current User",
+ "path": "UI/Angular/Current-User.md"
+ },
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
@@ -1282,10 +1286,6 @@
{
"text": "Deploying to a Clustered Environment",
"path": "Deployment/Clustered-Environment.md"
- },
- {
- "text": "Deploy Abp Webapp to Azure App Service",
- "path": "Deployment/Deploy-Azure-App-Service.md"
}
]
},
diff --git a/docs/en/images/azdevops-1.png b/docs/en/images/azdevops-1.png
deleted file mode 100644
index 78181e4b7c..0000000000
Binary files a/docs/en/images/azdevops-1.png and /dev/null differ
diff --git a/docs/en/images/azdevops-10.png b/docs/en/images/azdevops-10.png
deleted file mode 100644
index 75a273ba52..0000000000
Binary files a/docs/en/images/azdevops-10.png and /dev/null differ
diff --git a/docs/en/images/azdevops-11.png b/docs/en/images/azdevops-11.png
deleted file mode 100644
index 422a0ddbc0..0000000000
Binary files a/docs/en/images/azdevops-11.png and /dev/null differ
diff --git a/docs/en/images/azdevops-12.png b/docs/en/images/azdevops-12.png
deleted file mode 100644
index e1bc1399f9..0000000000
Binary files a/docs/en/images/azdevops-12.png and /dev/null differ
diff --git a/docs/en/images/azdevops-13.png b/docs/en/images/azdevops-13.png
deleted file mode 100644
index e83f972b20..0000000000
Binary files a/docs/en/images/azdevops-13.png and /dev/null differ
diff --git a/docs/en/images/azdevops-14.png b/docs/en/images/azdevops-14.png
deleted file mode 100644
index a4bd3b65d0..0000000000
Binary files a/docs/en/images/azdevops-14.png and /dev/null differ
diff --git a/docs/en/images/azdevops-15.png b/docs/en/images/azdevops-15.png
deleted file mode 100644
index ed4b9dc6a0..0000000000
Binary files a/docs/en/images/azdevops-15.png and /dev/null differ
diff --git a/docs/en/images/azdevops-16.png b/docs/en/images/azdevops-16.png
deleted file mode 100644
index 402f6b7d5e..0000000000
Binary files a/docs/en/images/azdevops-16.png and /dev/null differ
diff --git a/docs/en/images/azdevops-17.png b/docs/en/images/azdevops-17.png
deleted file mode 100644
index 5441a87035..0000000000
Binary files a/docs/en/images/azdevops-17.png and /dev/null differ
diff --git a/docs/en/images/azdevops-18.png b/docs/en/images/azdevops-18.png
deleted file mode 100644
index 8e358172d5..0000000000
Binary files a/docs/en/images/azdevops-18.png and /dev/null differ
diff --git a/docs/en/images/azdevops-19.png b/docs/en/images/azdevops-19.png
deleted file mode 100644
index ff282de4d5..0000000000
Binary files a/docs/en/images/azdevops-19.png and /dev/null differ
diff --git a/docs/en/images/azdevops-2.png b/docs/en/images/azdevops-2.png
deleted file mode 100644
index 9982431409..0000000000
Binary files a/docs/en/images/azdevops-2.png and /dev/null differ
diff --git a/docs/en/images/azdevops-20.png b/docs/en/images/azdevops-20.png
deleted file mode 100644
index f8a00be927..0000000000
Binary files a/docs/en/images/azdevops-20.png and /dev/null differ
diff --git a/docs/en/images/azdevops-21.png b/docs/en/images/azdevops-21.png
deleted file mode 100644
index 7d55d11150..0000000000
Binary files a/docs/en/images/azdevops-21.png and /dev/null differ
diff --git a/docs/en/images/azdevops-22.png b/docs/en/images/azdevops-22.png
deleted file mode 100644
index f103c8f56f..0000000000
Binary files a/docs/en/images/azdevops-22.png and /dev/null differ
diff --git a/docs/en/images/azdevops-23.png b/docs/en/images/azdevops-23.png
deleted file mode 100644
index 7318b82d13..0000000000
Binary files a/docs/en/images/azdevops-23.png and /dev/null differ
diff --git a/docs/en/images/azdevops-3.png b/docs/en/images/azdevops-3.png
deleted file mode 100644
index 0ede96f42a..0000000000
Binary files a/docs/en/images/azdevops-3.png and /dev/null differ
diff --git a/docs/en/images/azdevops-4.png b/docs/en/images/azdevops-4.png
deleted file mode 100644
index 52e025913c..0000000000
Binary files a/docs/en/images/azdevops-4.png and /dev/null differ
diff --git a/docs/en/images/azdevops-5.png b/docs/en/images/azdevops-5.png
deleted file mode 100644
index 31384e5de5..0000000000
Binary files a/docs/en/images/azdevops-5.png and /dev/null differ
diff --git a/docs/en/images/azdevops-6.png b/docs/en/images/azdevops-6.png
deleted file mode 100644
index cc493dfd43..0000000000
Binary files a/docs/en/images/azdevops-6.png and /dev/null differ
diff --git a/docs/en/images/azdevops-7.png b/docs/en/images/azdevops-7.png
deleted file mode 100644
index e5f0cda151..0000000000
Binary files a/docs/en/images/azdevops-7.png and /dev/null differ
diff --git a/docs/en/images/azdevops-8.png b/docs/en/images/azdevops-8.png
deleted file mode 100644
index 5c2f996cf8..0000000000
Binary files a/docs/en/images/azdevops-8.png and /dev/null differ
diff --git a/docs/en/images/azdevops-9.png b/docs/en/images/azdevops-9.png
deleted file mode 100644
index 0c8a7b557e..0000000000
Binary files a/docs/en/images/azdevops-9.png and /dev/null differ
diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index 27f68c5410..771d59dbd0 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -417,6 +417,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Abstractions"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.NewtonsoftJson", "src\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj", "{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Dapr", "src\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj", "{192A829F-D608-4E41-8DE0-058E943E453F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Dapr", "src\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj", "{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Dapr", "src\Volo.Abp.Http.Client.Dapr\Volo.Abp.Http.Client.Dapr.csproj", "{18B796D2-D45D-41AE-9A42-75C9B14B20DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr", "src\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj", "{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr.EventBus", "src\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj", "{B02EF042-C39E-45C4-A92D-BF7554E1889D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking.Dapr", "src\Volo.Abp.DistributedLocking.Dapr\Volo.Abp.DistributedLocking.Dapr.csproj", "{CAE48068-233C-47A9-BEAB-DDF521730E7A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1243,6 +1255,30 @@ Global
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.Build.0 = Release|Any CPU
+ {18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1453,6 +1489,12 @@ Global
{0AD06E14-CBFE-4551-8D18-9E921D8F2A87} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{08531C5D-0436-4721-986D-96446CF54316} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {192A829F-D608-4E41-8DE0-058E943E453F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {DCC41E99-EBC7-4F19-BA0D-A6F770D8E431} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {18B796D2-D45D-41AE-9A42-75C9B14B20DF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {B02EF042-C39E-45C4-A92D-BF7554E1889D} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {CAE48068-233C-47A9-BEAB-DDF521730E7A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj
new file mode 100644
index 0000000000..e1ede9fe69
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs
new file mode 100644
index 0000000000..333a5aae9a
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs
@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Http.Json;
+using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
+using Volo.Abp.EventBus.Dapr;
+using Volo.Abp.Json.SystemTextJson;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+[DependsOn(
+ typeof(AbpAspNetCoreMvcDaprModule),
+ typeof(AbpEventBusDaprModule)
+)]
+public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ // TODO: Add NewtonsoftJson json converter.
+
+ Configure(options =>
+ {
+ options.SerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
+ });
+
+ Configure(options =>
+ {
+ options.JsonSerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
+ });
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusOptions.cs
new file mode 100644
index 0000000000..02ca4c8e22
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusOptions.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+public class AbpAspNetCoreMvcDaprEventBusOptions
+{
+ public List Contributors { get; }
+
+ public AbpAspNetCoreMvcDaprEventBusOptions()
+ {
+ Contributors = new List();
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs
new file mode 100644
index 0000000000..e785f2e737
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs
@@ -0,0 +1,8 @@
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+public class AbpAspNetCoreMvcDaprPubSubConsts
+{
+ public const string DaprSubscribeUrl = "dapr/subscribe";
+
+ public const string DaprEventCallbackUrl = "api/abp/dapr/event";
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProvider.cs
new file mode 100644
index 0000000000..e797bd2fc0
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProvider.cs
@@ -0,0 +1,63 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus;
+using Volo.Abp.EventBus.Dapr;
+using Volo.Abp.EventBus.Distributed;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+public class AbpAspNetCoreMvcDaprPubSubProvider : ITransientDependency
+{
+ protected IServiceProvider ServiceProvider { get; }
+ protected AbpAspNetCoreMvcDaprEventBusOptions AspNetCoreMvcDaprEventBusOptions { get; }
+ protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
+ protected AbpDistributedEventBusOptions DistributedEventBusOptions { get; }
+
+ public AbpAspNetCoreMvcDaprPubSubProvider(
+ IServiceProvider serviceProvider,
+ IOptions aspNetCoreDaprEventBusOptions,
+ IOptions daprEventBusOptions,
+ IOptions distributedEventBusOptions)
+ {
+ ServiceProvider = serviceProvider;
+ AspNetCoreMvcDaprEventBusOptions = aspNetCoreDaprEventBusOptions.Value;
+ DaprEventBusOptions = daprEventBusOptions.Value;
+ DistributedEventBusOptions = distributedEventBusOptions.Value;
+ }
+
+ public virtual async Task> GetSubscriptionsAsync()
+ {
+ var subscriptions = new List();
+ foreach (var handler in DistributedEventBusOptions.Handlers)
+ {
+ foreach (var @interface in handler.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>)))
+ {
+ var eventType = @interface.GetGenericArguments()[0];
+ var eventName = EventNameAttribute.GetNameOrDefault(eventType);
+
+ subscriptions.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinition()
+ {
+ PubSubName = DaprEventBusOptions.PubSubName,
+ Topic = eventName,
+ Route = AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl
+ });
+ }
+ }
+
+ if (AspNetCoreMvcDaprEventBusOptions.Contributors.Any())
+ {
+ using (var scope = ServiceProvider.CreateScope())
+ {
+ var context = new AbpAspNetCoreMvcDaprPubSubProviderContributorContext(scope.ServiceProvider, subscriptions);
+ foreach (var contributor in AspNetCoreMvcDaprEventBusOptions.Contributors)
+ {
+ await contributor.ContributeAsync(context);
+ }
+ }
+ }
+
+ return subscriptions;
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProviderContributorContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProviderContributorContext.cs
new file mode 100644
index 0000000000..564b541dec
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProviderContributorContext.cs
@@ -0,0 +1,16 @@
+using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+public class AbpAspNetCoreMvcDaprPubSubProviderContributorContext
+{
+ public IServiceProvider ServiceProvider { get; }
+
+ public List Subscriptions { get; }
+
+ public AbpAspNetCoreMvcDaprPubSubProviderContributorContext(IServiceProvider serviceProvider, List daprSubscriptionModels)
+ {
+ ServiceProvider = serviceProvider;
+ Subscriptions = daprSubscriptionModels;
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs
new file mode 100644
index 0000000000..c97e793b53
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs
@@ -0,0 +1,36 @@
+using System.Text.Json;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+using Volo.Abp.Dapr;
+using Volo.Abp.EventBus.Dapr;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
+
+[Area("abp")]
+[RemoteService(Name = "abp")]
+public class AbpAspNetCoreMvcDaprPubSubController : AbpController
+{
+ [HttpGet(AbpAspNetCoreMvcDaprPubSubConsts.DaprSubscribeUrl)]
+ public virtual async Task> SubscribeAsync()
+ {
+ return await HttpContext.RequestServices.GetRequiredService().GetSubscriptionsAsync();
+ }
+
+ [HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
+ public virtual async Task EventsAsync()
+ {
+ var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body);
+ var request = JsonSerializer.Deserialize(bodyJsonDocument.RootElement.GetRawText(),
+ HttpContext.RequestServices.GetRequiredService>().Value.JsonSerializerOptions);
+
+ var distributedEventBus = HttpContext.RequestServices.GetRequiredService();
+ var daprSerializer = HttpContext.RequestServices.GetRequiredService();
+
+ var eventData = daprSerializer.Deserialize(bodyJsonDocument.RootElement.GetProperty("data").GetRawText(), distributedEventBus.GetEventType(request.Topic));
+ await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
+
+ return Ok();
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/IAbpAspNetCoreMvcDaprPubSubProviderContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/IAbpAspNetCoreMvcDaprPubSubProviderContributor.cs
new file mode 100644
index 0000000000..87047cd7ec
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/IAbpAspNetCoreMvcDaprPubSubProviderContributor.cs
@@ -0,0 +1,6 @@
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
+
+public interface IAbpAspNetCoreMvcDaprPubSubProviderContributor
+{
+ Task ContributeAsync(AbpAspNetCoreMvcDaprPubSubProviderContributorContext context);
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionDefinition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionDefinition.cs
new file mode 100644
index 0000000000..287e78e01a
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionDefinition.cs
@@ -0,0 +1,10 @@
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+
+public class AbpAspNetCoreMvcDaprSubscriptionDefinition
+{
+ public string PubSubName { get; set; }
+
+ public string Topic { get; set; }
+
+ public string Route { get; set; }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs
new file mode 100644
index 0000000000..46c04b5a44
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs
@@ -0,0 +1,8 @@
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+
+public class AbpAspNetCoreMvcDaprSubscriptionRequest
+{
+ public string PubSubName { get; set; }
+
+ public string Topic { get; set; }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy.cs
new file mode 100644
index 0000000000..0aa6bd4f7c
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy.cs
@@ -0,0 +1,11 @@
+using System.Text.Json;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
+
+public class AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy : JsonNamingPolicy
+{
+ public override string ConvertName(string name)
+ {
+ return name.ToLower();
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs
new file mode 100644
index 0000000000..fa26d35ca6
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs
@@ -0,0 +1,25 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
+
+public class AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter : JsonConverter
+{
+ private JsonSerializerOptions _writeJsonSerializerOptions;
+
+ public override AbpAspNetCoreMvcDaprSubscriptionDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, AbpAspNetCoreMvcDaprSubscriptionDefinition value, JsonSerializerOptions options)
+ {
+ _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(new JsonSerializerOptions(options)
+ {
+ PropertyNamingPolicy = new AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy()
+ }, x => x == this);
+
+ JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions);
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj
new file mode 100644
index 0000000000..0c72d5acc5
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/AbpAspNetCoreMvcDaprModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/AbpAspNetCoreMvcDaprModule.cs
new file mode 100644
index 0000000000..f8ba3fcfc2
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/AbpAspNetCoreMvcDaprModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Dapr;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.AspNetCore.Mvc.Dapr;
+
+[DependsOn(
+ typeof(AbpAspNetCoreMvcModule),
+ typeof(AbpDaprModule)
+)]
+public class AbpAspNetCoreMvcDaprModule : AbpModule
+{
+
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
index 528a04583d..7bcc0b3cd9 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
@@ -171,7 +171,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var policyName in policyNames)
{
- if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null && _permissionDefinitionManager.GetOrNull(policyName) != null)
+ if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null &&
+ await _permissionDefinitionManager.GetOrNullAsync(policyName) != null)
{
abpPolicyNames.Add(policyName);
}
@@ -214,9 +215,9 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{
var dictionary = new Dictionary();
- var localizer = _serviceProvider.GetRequiredService(
+ var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService(
typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType)
- ) as IStringLocalizer;
+ );
foreach (var localizedString in localizer.GetAllStrings())
{
diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
index bb3f104ce4..b6527f10c8 100644
--- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
+++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
@@ -1,4 +1,6 @@
-using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
@@ -24,4 +26,17 @@ public class AbpAuditingModule : AbpModule
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var applicationName = context.Services.GetApplicationName();
+
+ if (!applicationName.IsNullOrEmpty())
+ {
+ Configure(options =>
+ {
+ options.ApplicationName = applicationName;
+ });
+ }
+ }
}
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs
new file mode 100644
index 0000000000..57a224699f
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs
@@ -0,0 +1,14 @@
+using JetBrains.Annotations;
+using Volo.Abp.Localization;
+using Volo.Abp.MultiTenancy;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public interface ICanAddChildPermission
+{
+ PermissionDefinition AddPermission(
+ [NotNull] string name,
+ ILocalizableString displayName = null,
+ MultiTenancySides multiTenancySide = MultiTenancySides.Both,
+ bool isEnabled = true);
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
index 6d1f9a31fd..411b030b6a 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
@@ -37,8 +37,7 @@ public interface IPermissionDefinitionContext
///
PermissionGroupDefinition AddGroup(
[NotNull] string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both);
+ ILocalizableString displayName = null);
///
/// Tries to remove a permission group.
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs
index 553148671c..b4628a15bc 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs
@@ -1,17 +1,18 @@
using System.Collections.Generic;
+using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Authorization.Permissions;
public interface IPermissionDefinitionManager
{
- [NotNull]
- PermissionDefinition Get([NotNull] string name);
+ [ItemNotNull]
+ Task GetAsync([NotNull] string name);
- [CanBeNull]
- PermissionDefinition GetOrNull([NotNull] string name);
+ [ItemCanBeNull]
+ Task GetOrNullAsync([NotNull] string name);
- IReadOnlyList GetPermissions();
+ Task> GetPermissionsAsync();
- IReadOnlyList GetGroups();
+ Task> GetGroupsAsync();
}
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
index 75d463f30f..fb6f07690c 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
@@ -7,7 +7,9 @@ using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
-public class PermissionDefinition : IHasSimpleStateCheckers
+public class PermissionDefinition :
+ IHasSimpleStateCheckers,
+ ICanAddChildPermission
{
///
/// Unique name of the permission.
@@ -110,6 +112,16 @@ public class PermissionDefinition : IHasSimpleStateCheckers
/// Sets a property in the dictionary.
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs
index 7a10fe4b5e..5b5543e868 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs
@@ -20,8 +20,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
public virtual PermissionGroupDefinition AddGroup(
string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
+ ILocalizableString displayName = null)
{
Check.NotNull(name, nameof(name));
@@ -30,7 +29,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
throw new AbpException($"There is already an existing permission group with name: {name}");
}
- return Groups[name] = new PermissionGroupDefinition(name, displayName, multiTenancySide);
+ return Groups[name] = new PermissionGroupDefinition(name, displayName);
}
[NotNull]
diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
index d2efedc0d9..9fd904f54c 100644
--- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
+++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
@@ -6,7 +6,7 @@ using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
-public class PermissionGroupDefinition //TODO: Consider to make possible a group have sub groups
+public class PermissionGroupDefinition : ICanAddChildPermission
{
///
/// Unique name of the group.
@@ -21,12 +21,6 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
}
private ILocalizableString _displayName;
- ///
- /// MultiTenancy side.
- /// Default:
- ///
- public MultiTenancySides MultiTenancySide { get; set; }
-
public IReadOnlyList Permissions => _permissions.ToImmutableList();
private readonly List _permissions;
@@ -45,19 +39,17 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
protected internal PermissionGroupDefinition(
string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
+ ILocalizableString displayName = null)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
- MultiTenancySide = multiTenancySide;
Properties = new Dictionary();
_permissions = new List();
}
public virtual PermissionDefinition AddPermission(
- string name,
+ [NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs
index 01037f3e96..fb37427c48 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs
@@ -31,7 +31,7 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return policy;
}
- var permission = _permissionDefinitionManager.GetOrNull(policyName);
+ var permission = await _permissionDefinitionManager.GetOrNullAsync(policyName);
if (permission != null)
{
//TODO: Optimize & Cache!
@@ -43,16 +43,14 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return null;
}
- public Task> GetPoliciesNamesAsync()
+ public async Task> GetPoliciesNamesAsync()
{
- return Task.FromResult(
- _options.GetPoliciesNames()
- .Union(
- _permissionDefinitionManager
- .GetPermissions()
- .Select(p => p.Name)
- )
- .ToList()
- );
+ return _options.GetPoliciesNames()
+ .Union(
+ (await _permissionDefinitionManager
+ .GetPermissionsAsync())
+ .Select(p => p.Name)
+ )
+ .ToList();
}
}
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AuthenticatedSimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AuthenticatedSimpleStateCheckerSerializerContributor.cs
new file mode 100644
index 0000000000..2a23b5bfcd
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AuthenticatedSimpleStateCheckerSerializerContributor.cs
@@ -0,0 +1,38 @@
+using System.Text.Json.Nodes;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.SimpleStateChecking;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public class AuthenticatedSimpleStateCheckerSerializerContributor :
+ ISimpleStateCheckerSerializerContributor,
+ ISingletonDependency
+{
+ public const string CheckerShortName = "A";
+
+ public string SerializeToJson(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (checker is not RequireAuthenticatedSimpleStateChecker)
+ {
+ return null;
+ }
+
+ var jsonObject = new JsonObject {
+ ["T"] = CheckerShortName
+ };
+
+ return jsonObject.ToJsonString();
+ }
+
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (jsonObject["T"]?.ToString() != CheckerShortName)
+ {
+ return null;
+ }
+
+ return new RequireAuthenticatedSimpleStateChecker();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs
new file mode 100644
index 0000000000..ad74dae46b
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public interface IDynamicPermissionDefinitionStore
+{
+ Task GetOrNullAsync(string name);
+
+ Task> GetPermissionsAsync();
+
+ Task> GetGroupsAsync();
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs
new file mode 100644
index 0000000000..719412e4db
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public interface IStaticPermissionDefinitionStore
+{
+ Task GetOrNullAsync(string name);
+
+ Task> GetPermissionsAsync();
+
+ Task> GetGroupsAsync();
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs
new file mode 100644
index 0000000000..61e70b057c
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ISingletonDependency
+{
+ private readonly static Task CachedPermissionResult = Task.FromResult((PermissionDefinition)null);
+
+ private readonly static Task> CachedPermissionsResult =
+ Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList());
+
+ private readonly static Task> CachedGroupsResult =
+ Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList());
+
+ public Task GetOrNullAsync(string name)
+ {
+ return CachedPermissionResult;
+ }
+
+ public Task> GetPermissionsAsync()
+ {
+ return CachedPermissionsResult;
+ }
+
+ public Task> GetGroupsAsync()
+ {
+ return CachedGroupsResult;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
index fec8771afa..168eb3064b 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
@@ -43,8 +43,12 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(name, nameof(name));
- var permission = PermissionDefinitionManager.Get(name);
-
+ var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
+ if (permission == null)
+ {
+ return false;
+ }
+
if (!permission.IsEnabled)
{
return false;
@@ -97,18 +101,24 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(names, nameof(names));
- var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ?? CurrentTenant.GetMultiTenancySide();
-
var result = new MultiplePermissionGrantResult();
if (!names.Any())
{
return result;
}
+ var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ??
+ CurrentTenant.GetMultiTenancySide();
+
var permissionDefinitions = new List();
foreach (var name in names)
{
- var permission = PermissionDefinitionManager.Get(name);
+ var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
+ if (permission == null)
+ {
+ result.Result.Add(name, PermissionGrantResult.Prohibited);
+ continue;
+ }
result.Result.Add(name, PermissionGrantResult.Undefined);
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
index 7f4de3c767..de24ff59c5 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
@@ -1,47 +1,27 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
+using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
-public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency
+public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransientDependency
{
- protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
- private readonly Lazy> _lazyPermissionGroupDefinitions;
-
- protected IDictionary PermissionDefinitions => _lazyPermissionDefinitions.Value;
- private readonly Lazy> _lazyPermissionDefinitions;
-
- protected AbpPermissionOptions Options { get; }
-
- private readonly IServiceProvider _serviceProvider;
+ private readonly IStaticPermissionDefinitionStore _staticStore;
+ private readonly IDynamicPermissionDefinitionStore _dynamicStore;
public PermissionDefinitionManager(
- IOptions options,
- IServiceProvider serviceProvider)
+ IStaticPermissionDefinitionStore staticStore,
+ IDynamicPermissionDefinitionStore dynamicStore)
{
- _serviceProvider = serviceProvider;
- Options = options.Value;
-
- _lazyPermissionDefinitions = new Lazy>(
- CreatePermissionDefinitions,
- isThreadSafe: true
- );
-
- _lazyPermissionGroupDefinitions = new Lazy>(
- CreatePermissionGroupDefinitions,
- isThreadSafe: true
- );
+ _staticStore = staticStore;
+ _dynamicStore = dynamicStore;
}
- public virtual PermissionDefinition Get(string name)
+ public virtual async Task GetAsync(string name)
{
- var permission = GetOrNull(name);
-
+ var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined permission: " + name);
@@ -50,82 +30,41 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingle
return permission;
}
- public virtual PermissionDefinition GetOrNull(string name)
+ public virtual async Task GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
- return PermissionDefinitions.GetOrDefault(name);
- }
-
- public virtual IReadOnlyList GetPermissions()
- {
- return PermissionDefinitions.Values.ToImmutableList();
- }
-
- public IReadOnlyList GetGroups()
- {
- return PermissionGroupDefinitions.Values.ToImmutableList();
- }
-
- protected virtual Dictionary CreatePermissionDefinitions()
- {
- var permissions = new Dictionary();
-
- foreach (var groupDefinition in PermissionGroupDefinitions.Values)
- {
- foreach (var permission in groupDefinition.Permissions)
- {
- AddPermissionToDictionaryRecursively(permissions, permission);
- }
- }
-
- return permissions;
+ return await _staticStore.GetOrNullAsync(name) ??
+ await _dynamicStore.GetOrNullAsync(name);
}
- protected virtual void AddPermissionToDictionaryRecursively(
- Dictionary permissions,
- PermissionDefinition permission)
+ public virtual async Task> GetPermissionsAsync()
{
- if (permissions.ContainsKey(permission.Name))
- {
- throw new AbpException("Duplicate permission name: " + permission.Name);
- }
-
- permissions[permission.Name] = permission;
-
- foreach (var child in permission.Children)
- {
- AddPermissionToDictionaryRecursively(permissions, child);
- }
+ var staticPermissions = await _staticStore.GetPermissionsAsync();
+ var staticPermissionNames = staticPermissions
+ .Select(p => p.Name)
+ .ToImmutableHashSet();
+
+ var dynamicPermissions = await _dynamicStore.GetPermissionsAsync();
+
+ /* We prefer static permissions over dynamics */
+ return staticPermissions.Concat(
+ dynamicPermissions.Where(d => !staticPermissionNames.Contains(d.Name))
+ ).ToImmutableList();
}
- protected virtual Dictionary CreatePermissionGroupDefinitions()
+ public async Task> GetGroupsAsync()
{
- using (var scope = _serviceProvider.CreateScope())
- {
- var context = new PermissionDefinitionContext(scope.ServiceProvider);
-
- var providers = Options
- .DefinitionProviders
- .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
- .ToList();
-
- foreach (var provider in providers)
- {
- provider.PreDefine(context);
- }
-
- foreach (var provider in providers)
- {
- provider.Define(context);
- }
-
- foreach (var provider in providers)
- {
- provider.PostDefine(context);
- }
-
- return context.Groups;
- }
+ var staticGroups = await _staticStore.GetGroupsAsync();
+ var staticGroupNames = staticGroups
+ .Select(p => p.Name)
+ .ToImmutableHashSet();
+
+ var dynamicGroups = await _dynamicStore.GetGroupsAsync();
+
+ /* We prefer static groups over dynamics */
+ return staticGroups.Concat(
+ dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
+ ).ToImmutableList();
}
-}
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionsSimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionsSimpleStateCheckerSerializerContributor.cs
new file mode 100644
index 0000000000..3b890797f6
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionsSimpleStateCheckerSerializerContributor.cs
@@ -0,0 +1,62 @@
+using System.Linq;
+using System.Text.Json.Nodes;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.SimpleStateChecking;
+
+namespace Volo.Abp.GlobalFeatures;
+
+public class PermissionsSimpleStateCheckerSerializerContributor :
+ ISimpleStateCheckerSerializerContributor,
+ ISingletonDependency
+{
+ public const string CheckerShortName = "P";
+
+ public string SerializeToJson(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (checker is not RequirePermissionsSimpleStateChecker permissionsSimpleStateChecker)
+ {
+ return null;
+ }
+
+ var jsonObject = new JsonObject {
+ ["T"] = CheckerShortName,
+ ["A"] = permissionsSimpleStateChecker.RequiresAll
+ };
+
+ var nameArray = new JsonArray();
+ foreach (var permissionName in permissionsSimpleStateChecker.PermissionNames)
+ {
+ nameArray.Add(permissionName);
+ }
+
+ jsonObject["N"] = nameArray;
+ return jsonObject.ToJsonString();
+ }
+
+ public ISimpleStateChecker Deserialize(
+ JsonObject jsonObject,
+ TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (jsonObject["T"]?.ToString() != CheckerShortName)
+ {
+ return null;
+ }
+
+ var nameArray = jsonObject["N"] as JsonArray;
+ if (nameArray == null)
+ {
+ throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
+ }
+
+ return new RequirePermissionsSimpleStateChecker(
+ new RequirePermissionsSimpleBatchStateCheckerModel(
+ state,
+ nameArray.Select(x => x.ToString()).ToArray(),
+ (bool?)jsonObject["A"] ?? false
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs
index 3ae1809b55..fd8eef3791 100644
--- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs
@@ -8,6 +8,10 @@ namespace Volo.Abp.Authorization.Permissions;
public class RequirePermissionsSimpleStateChecker : ISimpleStateChecker
where TState : IHasSimpleStateCheckers
{
+ public bool RequiresAll => _model.RequiresAll;
+
+ public string[] PermissionNames => _model.Permissions;
+
private readonly RequirePermissionsSimpleBatchStateCheckerModel _model;
public RequirePermissionsSimpleStateChecker(RequirePermissionsSimpleBatchStateCheckerModel model)
diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs
new file mode 100644
index 0000000000..ee4803d61e
--- /dev/null
+++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Authorization.Permissions;
+
+public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency
+{
+ protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
+ private readonly Lazy> _lazyPermissionGroupDefinitions;
+
+ protected IDictionary PermissionDefinitions => _lazyPermissionDefinitions.Value;
+ private readonly Lazy> _lazyPermissionDefinitions;
+
+ protected AbpPermissionOptions Options { get; }
+
+ private readonly IServiceProvider _serviceProvider;
+
+ public StaticPermissionDefinitionStore(
+ IServiceProvider serviceProvider,
+ IOptions options)
+ {
+ _serviceProvider = serviceProvider;
+ Options = options.Value;
+
+ _lazyPermissionDefinitions = new Lazy>(
+ CreatePermissionDefinitions,
+ isThreadSafe: true
+ );
+
+ _lazyPermissionGroupDefinitions = new Lazy>(
+ CreatePermissionGroupDefinitions,
+ isThreadSafe: true
+ );
+ }
+
+ protected virtual Dictionary CreatePermissionDefinitions()
+ {
+ var permissions = new Dictionary();
+
+ foreach (var groupDefinition in PermissionGroupDefinitions.Values)
+ {
+ foreach (var permission in groupDefinition.Permissions)
+ {
+ AddPermissionToDictionaryRecursively(permissions, permission);
+ }
+ }
+
+ return permissions;
+ }
+
+ protected virtual void AddPermissionToDictionaryRecursively(
+ Dictionary permissions,
+ PermissionDefinition permission)
+ {
+ if (permissions.ContainsKey(permission.Name))
+ {
+ throw new AbpException("Duplicate permission name: " + permission.Name);
+ }
+
+ permissions[permission.Name] = permission;
+
+ foreach (var child in permission.Children)
+ {
+ AddPermissionToDictionaryRecursively(permissions, child);
+ }
+ }
+
+ protected virtual Dictionary CreatePermissionGroupDefinitions()
+ {
+ using (var scope = _serviceProvider.CreateScope())
+ {
+ var context = new PermissionDefinitionContext(scope.ServiceProvider);
+
+ var providers = Options
+ .DefinitionProviders
+ .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
+ .ToList();
+
+ foreach (var provider in providers)
+ {
+ provider.PreDefine(context);
+ }
+
+ foreach (var provider in providers)
+ {
+ provider.Define(context);
+ }
+
+ foreach (var provider in providers)
+ {
+ provider.PostDefine(context);
+ }
+
+ return context.Groups;
+ }
+ }
+
+ public Task GetOrNullAsync(string name)
+ {
+ return Task.FromResult(PermissionDefinitions.GetOrDefault(name));
+ }
+
+ public virtual Task> GetPermissionsAsync()
+ {
+ return Task.FromResult>(
+ PermissionDefinitions.Values.ToImmutableList()
+ );
+ }
+
+ public Task> GetGroupsAsync()
+ {
+ return Task.FromResult>(
+ PermissionGroupDefinitions.Values.ToImmutableList()
+ );
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
index 492e938874..65212fe69f 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
@@ -113,8 +113,11 @@ public class BundlingService : IBundlingService, ITransientDependency
var contributor = CreateContributorInstance(bundleDefinition.BundleContributorType);
contributor.AddScripts(scriptContext);
}
+
+ scriptContext.BundleDefinitions.AddIfNotContains(
+ x => x.Source == "_framework/blazor.webassembly.js",
+ () => new BundleDefinition { Source = "_framework/blazor.webassembly.js" });
- scriptContext.Add("_framework/blazor.webassembly.js");
return scriptContext;
}
@@ -206,7 +209,7 @@ public class BundlingService : IBundlingService, ITransientDependency
builder.Append($" ");
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs
index 0f249ceae7..abf1e515df 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs
@@ -41,10 +41,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
break;
}
- if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
- {
- context.Symbols.Add("dbms:PostgreSQL");
- }
+ context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
switch (context.BuildArgs.UiFramework)
{
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
index 397a35222a..515bb98a56 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
@@ -109,10 +109,7 @@ public abstract class AppTemplateBase : TemplateInfo
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests"));
}
- if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
- {
- context.Symbols.Add("dbms:PostgreSQL");
- }
+ context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
}
protected void DeleteUnrelatedProjects(ProjectBuildContext context, List steps)
diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
index ce4af2befa..72ce661ed5 100644
--- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
+++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
@@ -39,4 +39,10 @@ public static class ServiceCollectionApplicationExtensions
{
return await AbpApplicationFactory.CreateAsync(startupModuleType, services, optionsAction);
}
+
+ [CanBeNull]
+ public static string GetApplicationName(this IServiceCollection services)
+ {
+ return services.GetSingletonInstance().ApplicationName;
+ }
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
index 741f6f0586..87ddf14c1b 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
@@ -23,6 +23,8 @@ public abstract class AbpApplicationBase : IAbpApplication
public IServiceCollection Services { get; }
public IReadOnlyList Modules { get; }
+
+ public string ApplicationName { get; }
private bool _configuredServices;
@@ -41,8 +43,11 @@ public abstract class AbpApplicationBase : IAbpApplication
var options = new AbpApplicationCreationOptions(services);
optionsAction?.Invoke(options);
+
+ ApplicationName = options.ApplicationName;
services.AddSingleton(this);
+ services.AddSingleton(this);
services.AddSingleton(this);
services.AddCoreServices();
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
index d18e43275b..24a1bf8ee6 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
@@ -20,6 +20,9 @@ public class AbpApplicationCreationOptions
public AbpConfigurationBuilderOptions Configuration { get; }
public bool SkipConfigureServices { get; set; }
+
+ [CanBeNull]
+ public string ApplicationName { get; set; }
public AbpApplicationCreationOptions([NotNull] IServiceCollection services)
{
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs
index 245ce6fb06..5f09157fcc 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs
@@ -60,6 +60,6 @@ internal class AbpApplicationWithInternalServiceProvider : AbpApplicationBase, I
public override void Dispose()
{
base.Dispose();
- ServiceScope.Dispose();
+ ServiceScope?.Dispose();
}
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IRootServiceProviderAccessor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IRootServiceProviderAccessor.cs
new file mode 100644
index 0000000000..8a3c1d09a5
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IRootServiceProviderAccessor.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Volo.Abp.DependencyInjection;
+
+///
+/// The root service provider of the application.
+/// Be careful to use the root service provider since there is no way
+/// to release/dispose objects resolved from the root service provider.
+/// So, always create a new scope if you need to resolve any service.
+///
+public interface IRootServiceProvider : IServiceProvider
+{
+
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/RootServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/RootServiceProvider.cs
new file mode 100644
index 0000000000..cbb6fb8984
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/RootServiceProvider.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Volo.Abp.DependencyInjection;
+
+[ExposeServices(typeof(IRootServiceProvider))]
+public class RootServiceProvider : IRootServiceProvider, ISingletonDependency
+{
+ protected IServiceProvider ServiceProvider { get; }
+
+ public RootServiceProvider(IObjectAccessor objectAccessor)
+ {
+ ServiceProvider = objectAccessor.Value;
+ }
+
+ public virtual object GetService(Type serviceType)
+ {
+ return ServiceProvider.GetService(serviceType);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs
index 2fb66b735f..35542ee91f 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs
@@ -5,7 +5,10 @@ using Volo.Abp.Modularity;
namespace Volo.Abp;
-public interface IAbpApplication : IModuleContainer, IDisposable
+public interface IAbpApplication :
+ IModuleContainer,
+ IApplicationNameAccessor,
+ IDisposable
{
///
/// Type of the startup (entrance) module of the application.
@@ -23,7 +26,7 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// This can not be used before initialize the application.
///
IServiceProvider ServiceProvider { get; }
-
+
///
/// Calls the Pre/Post/ConfigureServicesAsync methods of the modules.
/// If you use this method, you must have set the
@@ -40,4 +43,4 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// Used to gracefully shutdown the application and all modules.
///
void Shutdown();
-}
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IApplicationNameAccessor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IApplicationNameAccessor.cs
new file mode 100644
index 0000000000..0047fcbcdf
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/IApplicationNameAccessor.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp;
+
+public interface IApplicationNameAccessor
+{
+ ///
+ /// Name of the application.
+ /// This is useful for systems with multiple applications, to distinguish
+ /// resources of the applications located together.
+ ///
+ string ApplicationName { get; }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs
index f1a2e40425..e919b981a6 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs
@@ -6,4 +6,4 @@ public interface ISimpleStateChecker
where TState : IHasSimpleStateCheckers
{
Task IsEnabledAsync(SimpleStateCheckerContext context);
-}
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs
new file mode 100644
index 0000000000..ffbed53cf7
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Nodes;
+
+namespace Volo.Abp.SimpleStateChecking;
+
+public interface ISimpleStateCheckerSerializer
+{
+ public string Serialize(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers;
+
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers;
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs
new file mode 100644
index 0000000000..d65c90eee6
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Nodes;
+using JetBrains.Annotations;
+
+namespace Volo.Abp.SimpleStateChecking;
+
+public interface ISimpleStateCheckerSerializerContributor
+{
+ [CanBeNull]
+ public string SerializeToJson(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers;
+
+ [CanBeNull]
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers;
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs
new file mode 100644
index 0000000000..d5d931ffae
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Text.Json.Nodes;
+using JetBrains.Annotations;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.SimpleStateChecking;
+
+public class SimpleStateCheckerSerializer :
+ ISimpleStateCheckerSerializer,
+ ISingletonDependency
+{
+ private readonly IEnumerable _contributors;
+
+ public SimpleStateCheckerSerializer(IEnumerable contributors)
+ {
+ _contributors = contributors;
+ }
+
+ [CanBeNull]
+ public string Serialize(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers
+ {
+ foreach (var contributor in _contributors)
+ {
+ var result = contributor.SerializeToJson(checker);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ [CanBeNull]
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ foreach (var contributor in _contributors)
+ {
+ var result = contributor.Deserialize(jsonObject, state);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs
new file mode 100644
index 0000000000..3594261741
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json.Nodes;
+
+namespace Volo.Abp.SimpleStateChecking;
+
+public static class SimpleStateCheckerSerializerExtensions
+{
+ public static string Serialize(
+ this ISimpleStateCheckerSerializer serializer,
+ IList> stateCheckers)
+ where TState : IHasSimpleStateCheckers
+ {
+ switch (stateCheckers.Count)
+ {
+ case 0:
+ return null;
+ case 1:
+ var serializedChecker = serializer.Serialize(stateCheckers.Single());
+ return serializedChecker != null
+ ? $"[{serializedChecker}]"
+ : null;
+ default:
+ var serializedCheckers = new List(stateCheckers.Count);
+
+ foreach (var stateChecker in stateCheckers)
+ {
+ var serialized = serializer.Serialize(stateChecker);
+ if (serialized != null)
+ {
+ serializedCheckers.Add(serialized);
+ }
+ }
+
+ return serializedCheckers.Any()
+ ? $"[{serializedCheckers.JoinAsString(",")}]"
+ : null;
+ }
+ }
+
+ public static ISimpleStateChecker[] DeserializeArray(
+ this ISimpleStateCheckerSerializer serializer,
+ string value,
+ TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (value.IsNullOrWhiteSpace())
+ {
+ return Array.Empty>();
+ }
+
+ var array = JsonNode.Parse(value) as JsonArray;
+ if (array == null || array.Count == 0)
+ {
+ return Array.Empty>();
+ }
+
+ if (array.Count == 1)
+ {
+ var jsonObject = array[0] as JsonObject;
+ if (jsonObject == null)
+ {
+ throw new AbpException("JSON value is not an array of objects: " + value);
+ }
+
+ var checker = serializer.Deserialize(jsonObject, state);
+ if (checker == null)
+ {
+ return Array.Empty>();
+ }
+
+ return new[] { checker };
+ }
+
+ var checkers = new List>();
+
+ for (var i = 0; i < array.Count; i++)
+ {
+ if (array[i] is not JsonObject jsonObject)
+ {
+ throw new AbpException("JSON value is not an array of objects: " + value);
+ }
+
+ checkers.Add(serializer.Deserialize(jsonObject, state));
+ }
+
+ return checkers.Where(x => x != null).ToArray();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Dapr/FodyWeavers.xml b/framework/src/Volo.Abp.Dapr/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Dapr/FodyWeavers.xsd b/framework/src/Volo.Abp.Dapr/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Dapr/Volo.Abp.Dapr.csproj b/framework/src/Volo.Abp.Dapr/Volo.Abp.Dapr.csproj
new file mode 100644
index 0000000000..ab079747e9
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo.Abp.Dapr.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs
new file mode 100644
index 0000000000..faaf450ddc
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs
@@ -0,0 +1,48 @@
+using System.Collections.Concurrent;
+using System.Text.Json;
+using Dapr.Client;
+using Microsoft.Extensions.Options;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Json.SystemTextJson;
+
+namespace Volo.Abp.Dapr;
+
+public class AbpDaprClientFactory : ITransientDependency
+{
+ protected AbpDaprOptions Options { get; }
+ protected AbpSystemTextJsonSerializerOptions SystemTextJsonSerializerOptions { get; }
+
+ public AbpDaprClientFactory(
+ IOptions options,
+ IOptions systemTextJsonSerializerOptions)
+ {
+ Options = options.Value;
+ SystemTextJsonSerializerOptions = systemTextJsonSerializerOptions.Value;
+ }
+
+ public virtual async Task CreateAsync()
+ {
+ var builder = new DaprClientBuilder()
+ .UseJsonSerializationOptions(await CreateJsonSerializerOptions());
+
+ if (!Options.HttpEndpoint.IsNullOrWhiteSpace())
+ {
+ builder.UseHttpEndpoint(Options.HttpEndpoint);
+ }
+
+ if (!Options.GrpcEndpoint.IsNullOrWhiteSpace())
+ {
+ builder.UseGrpcEndpoint(Options.GrpcEndpoint);
+ }
+
+ return builder.Build();
+ }
+
+ private readonly static ConcurrentDictionary JsonSerializerOptionsCache = new ConcurrentDictionary();
+
+ protected virtual Task CreateJsonSerializerOptions()
+ {
+ return Task.FromResult(JsonSerializerOptionsCache.GetOrAdd(nameof(AbpDaprClientFactory),
+ _ => new JsonSerializerOptions(SystemTextJsonSerializerOptions.JsonSerializerOptions)));
+ }
+}
diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs
new file mode 100644
index 0000000000..43d6d11033
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Json;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Dapr;
+
+[DependsOn(typeof(AbpJsonModule))]
+public class AbpDaprModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Services.GetConfiguration();
+ Configure(configuration.GetSection("Dapr"));
+ }
+}
diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs
new file mode 100644
index 0000000000..9d743263c5
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs
@@ -0,0 +1,10 @@
+namespace Volo.Abp.Dapr;
+
+public class AbpDaprOptions
+{
+ public string AppId { get; set; }
+
+ public string HttpEndpoint { get; set; }
+
+ public string GrpcEndpoint { get; set; }
+}
diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprSerializer.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprSerializer.cs
new file mode 100644
index 0000000000..7eec2c5c1c
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprSerializer.cs
@@ -0,0 +1,16 @@
+namespace Volo.Abp.Dapr;
+
+public interface IDaprSerializer
+{
+ byte[] Serialize(object obj);
+
+ object Deserialize(byte[] value, Type type);
+
+ T Deserialize(byte[] value);
+
+ string SerializeToString(object obj);
+
+ object Deserialize(string value, Type type);
+
+ T Deserialize(string value);
+}
diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/Utf8JsonDaprSerializer.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/Utf8JsonDaprSerializer.cs
new file mode 100644
index 0000000000..ce9b4a8523
--- /dev/null
+++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/Utf8JsonDaprSerializer.cs
@@ -0,0 +1,45 @@
+using System.Text;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Json;
+
+namespace Volo.Abp.Dapr;
+
+public class Utf8JsonDaprSerializer : IDaprSerializer, ITransientDependency
+{
+ private readonly IJsonSerializer _jsonSerializer;
+
+ public Utf8JsonDaprSerializer(IJsonSerializer jsonSerializer)
+ {
+ _jsonSerializer = jsonSerializer;
+ }
+
+ public byte[] Serialize(object obj)
+ {
+ return Encoding.UTF8.GetBytes(_jsonSerializer.Serialize(obj));
+ }
+
+ public object Deserialize(byte[] value, Type type)
+ {
+ return _jsonSerializer.Deserialize(type, Encoding.UTF8.GetString(value));
+ }
+
+ public T Deserialize(byte[] value)
+ {
+ return _jsonSerializer.Deserialize(Encoding.UTF8.GetString(value));
+ }
+
+ public string SerializeToString(object obj)
+ {
+ return _jsonSerializer.Serialize(obj);
+ }
+
+ public object Deserialize(string value, Type type)
+ {
+ return _jsonSerializer.Deserialize(type, value);
+ }
+
+ public T Deserialize(string value)
+ {
+ return _jsonSerializer.Deserialize(value);
+ }
+}
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xml b/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xsd b/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo.Abp.DistributedLocking.Dapr.csproj b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo.Abp.DistributedLocking.Dapr.csproj
new file mode 100644
index 0000000000..4adc11cef7
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo.Abp.DistributedLocking.Dapr.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs
new file mode 100644
index 0000000000..43feba3c3b
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs
@@ -0,0 +1,13 @@
+namespace Volo.Abp.DistributedLocking.Dapr;
+
+public class AbpDistributedLockDaprOptions
+{
+ public string StoreName { get; set; }
+
+ public TimeSpan DefaultTimeout { get; set; }
+
+ public AbpDistributedLockDaprOptions()
+ {
+ DefaultTimeout = TimeSpan.FromSeconds(30);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockingDaprModule.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockingDaprModule.cs
new file mode 100644
index 0000000000..c915cfe2f7
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockingDaprModule.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Dapr;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.DistributedLocking.Dapr;
+
+[DependsOn(
+ typeof(AbpDistributedLockingAbstractionsModule),
+ typeof(AbpDaprModule))]
+public class AbpDistributedLockingDaprModule : AbpModule
+{
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs
new file mode 100644
index 0000000000..7f95a56542
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs
@@ -0,0 +1,50 @@
+using Microsoft.Extensions.Options;
+using Volo.Abp.Dapr;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.DistributedLocking.Dapr;
+
+[Dependency(ReplaceServices = true)]
+public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency
+{
+ protected AbpDaprClientFactory DaprClientFactory { get; }
+ protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; }
+ protected AbpDaprOptions DaprOptions { get; }
+
+ public DaprAbpDistributedLock(
+ AbpDaprClientFactory daprClientFactory,
+ IOptions distributedLockDaprOptions,
+ IOptions daprOptions)
+ {
+ DaprClientFactory = daprClientFactory;
+ DaprOptions = daprOptions.Value;
+ DistributedLockDaprOptions = distributedLockDaprOptions.Value;
+ }
+
+ public async Task TryAcquireAsync(
+ string name,
+ TimeSpan timeout = default,
+ CancellationToken cancellationToken = default)
+ {
+ if (timeout == default)
+ {
+ timeout = DistributedLockDaprOptions.DefaultTimeout;
+ }
+
+ var daprClient = await DaprClientFactory.CreateAsync();
+
+ var lockResponse = await daprClient.Lock(
+ DistributedLockDaprOptions.StoreName,
+ name,
+ DaprOptions.AppId,
+ (int)timeout.TotalSeconds,
+ cancellationToken);
+
+ if (lockResponse == null || !lockResponse.Success)
+ {
+ return null;
+ }
+
+ return new DaprAbpDistributedLockHandle(lockResponse);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLockHandle.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLockHandle.cs
new file mode 100644
index 0000000000..b10f0f3672
--- /dev/null
+++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLockHandle.cs
@@ -0,0 +1,18 @@
+using Dapr.Client;
+
+namespace Volo.Abp.DistributedLocking.Dapr;
+
+public class DaprAbpDistributedLockHandle : IAbpDistributedLockHandle
+{
+ protected TryLockResponse LockResponse { get; }
+
+ public DaprAbpDistributedLockHandle(TryLockResponse lockResponse)
+ {
+ LockResponse = lockResponse;
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await LockResponse.DisposeAsync();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj b/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj
index fd9b992133..0abb5e699c 100644
--- a/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj
+++ b/framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj
@@ -16,6 +16,7 @@
+
diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs
index 4c4abd3f32..eef5b396a2 100644
--- a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs
+++ b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs
@@ -1,11 +1,12 @@
using Volo.Abp.Modularity;
+using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
[DependsOn(
- typeof(AbpDistributedLockingAbstractionsModule)
+ typeof(AbpDistributedLockingAbstractionsModule),
+ typeof(AbpThreadingModule)
)]
public class AbpDistributedLockingModule : AbpModule
{
-
}
diff --git a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs
index e78f1934c1..769303a348 100644
--- a/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs
+++ b/framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs
@@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using Medallion.Threading;
using Volo.Abp.DependencyInjection;
+using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
@@ -10,10 +11,14 @@ namespace Volo.Abp.DistributedLocking;
public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDependency
{
protected IDistributedLockProvider DistributedLockProvider { get; }
+ protected ICancellationTokenProvider CancellationTokenProvider { get; }
- public MedallionAbpDistributedLock(IDistributedLockProvider distributedLockProvider)
+ public MedallionAbpDistributedLock(
+ IDistributedLockProvider distributedLockProvider,
+ ICancellationTokenProvider cancellationTokenProvider)
{
DistributedLockProvider = distributedLockProvider;
+ CancellationTokenProvider = cancellationTokenProvider;
}
public async Task TryAcquireAsync(
@@ -23,7 +28,14 @@ public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDepend
{
Check.NotNullOrWhiteSpace(name, nameof(name));
- var handle = await DistributedLockProvider.TryAcquireLockAsync(name, timeout, cancellationToken);
+ CancellationTokenProvider.FallbackToProvider(cancellationToken);
+
+ var handle = await DistributedLockProvider.TryAcquireLockAsync(
+ name,
+ timeout,
+ CancellationTokenProvider.FallbackToProvider(cancellationToken)
+ );
+
if (handle == null)
{
return null;
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xml b/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xsd b/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo.Abp.EventBus.Dapr.csproj b/framework/src/Volo.Abp.EventBus.Dapr/Volo.Abp.EventBus.Dapr.csproj
new file mode 100644
index 0000000000..95cd73dbfd
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo.Abp.EventBus.Dapr.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpDaprEventBusOptions.cs b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpDaprEventBusOptions.cs
new file mode 100644
index 0000000000..d3ced52ba2
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpDaprEventBusOptions.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp.EventBus.Dapr;
+
+public class AbpDaprEventBusOptions
+{
+ public string PubSubName { get; set; }
+
+ public AbpDaprEventBusOptions()
+ {
+ PubSubName = "pubsub";
+ }
+}
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpEventBusDaprModule.cs b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpEventBusDaprModule.cs
new file mode 100644
index 0000000000..5f6329a4bc
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/AbpEventBusDaprModule.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Dapr;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.EventBus.Dapr;
+
+[DependsOn(
+ typeof(AbpEventBusModule),
+ typeof(AbpDaprModule)
+ )]
+public class AbpEventBusDaprModule : AbpModule
+{
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ context
+ .ServiceProvider
+ .GetRequiredService()
+ .Initialize();
+ }
+}
diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs
new file mode 100644
index 0000000000..1f241b9c6d
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs
@@ -0,0 +1,221 @@
+using System.Collections.Concurrent;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Dapr;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.Guids;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Threading;
+using Volo.Abp.Timing;
+using Volo.Abp.Uow;
+
+namespace Volo.Abp.EventBus.Dapr;
+
+[Dependency(ReplaceServices = true)]
+[ExposeServices(typeof(IDistributedEventBus), typeof(DaprDistributedEventBus))]
+public class DaprDistributedEventBus : DistributedEventBusBase, ISingletonDependency
+{
+ protected IDaprSerializer Serializer { get; }
+ protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
+ protected AbpDaprClientFactory DaprClientFactory { get; }
+
+ protected ConcurrentDictionary> HandlerFactories { get; }
+ protected ConcurrentDictionary EventTypes { get; }
+
+ public DaprDistributedEventBus(
+ IServiceScopeFactory serviceScopeFactory,
+ ICurrentTenant currentTenant,
+ IUnitOfWorkManager unitOfWorkManager,
+ IOptions abpDistributedEventBusOptions,
+ IGuidGenerator guidGenerator,
+ IClock clock,
+ IEventHandlerInvoker eventHandlerInvoker,
+ IDaprSerializer serializer,
+ IOptions daprEventBusOptions,
+ AbpDaprClientFactory daprClientFactory)
+ : base(serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, clock, eventHandlerInvoker)
+ {
+ Serializer = serializer;
+ DaprEventBusOptions = daprEventBusOptions.Value;
+ DaprClientFactory = daprClientFactory;
+
+ HandlerFactories = new ConcurrentDictionary>();
+ EventTypes = new ConcurrentDictionary();
+ }
+
+ public void Initialize()
+ {
+ SubscribeHandlers(AbpDistributedEventBusOptions.Handlers);
+ }
+
+ public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
+ {
+ var handlerFactories = GetOrCreateHandlerFactories(eventType);
+
+ if (factory.IsInFactories(handlerFactories))
+ {
+ return NullDisposable.Instance;
+ }
+
+ handlerFactories.Add(factory);
+
+ return new EventHandlerFactoryUnregistrar(this, eventType, factory);
+ }
+
+ public override void Unsubscribe(Func action)
+ {
+ Check.NotNull(action, nameof(action));
+
+ GetOrCreateHandlerFactories(typeof(TEvent))
+ .Locking(factories =>
+ {
+ factories.RemoveAll(
+ factory =>
+ {
+ var singleInstanceFactory = factory as SingleInstanceHandlerFactory;
+ if (singleInstanceFactory == null)
+ {
+ return false;
+ }
+
+ var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler;
+ if (actionHandler == null)
+ {
+ return false;
+ }
+
+ return actionHandler.Action == action;
+ });
+ });
+ }
+
+ public override void Unsubscribe(Type eventType, IEventHandler handler)
+ {
+ GetOrCreateHandlerFactories(eventType)
+ .Locking(factories =>
+ {
+ factories.RemoveAll(
+ factory =>
+ factory is SingleInstanceHandlerFactory &&
+ (factory as SingleInstanceHandlerFactory).HandlerInstance == handler
+ );
+ });
+ }
+
+ public override void Unsubscribe(Type eventType, IEventHandlerFactory factory)
+ {
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
+ }
+
+ public override void UnsubscribeAll(Type eventType)
+ {
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
+ }
+
+ protected async override Task PublishToEventBusAsync(Type eventType, object eventData)
+ {
+ await PublishToDaprAsync(eventType, eventData);
+ }
+
+ protected override void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord)
+ {
+ unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
+ }
+
+ protected override IEnumerable GetHandlerFactories(Type eventType)
+ {
+ var handlerFactoryList = new List();
+
+ foreach (var handlerFactory in HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
+ {
+ handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
+ }
+
+ return handlerFactoryList.ToArray();
+ }
+
+ public async override Task PublishFromOutboxAsync(OutgoingEventInfo outgoingEvent, OutboxConfig outboxConfig)
+ {
+ await PublishToDaprAsync(outgoingEvent.EventName, Serializer.Deserialize(outgoingEvent.EventData, GetEventType(outgoingEvent.EventName)));
+ }
+
+ public async override Task PublishManyFromOutboxAsync(IEnumerable outgoingEvents, OutboxConfig outboxConfig)
+ {
+ var outgoingEventArray = outgoingEvents.ToArray();
+
+ foreach (var outgoingEvent in outgoingEventArray)
+ {
+ await PublishToDaprAsync(outgoingEvent.EventName, Serializer.Deserialize(outgoingEvent.EventData, GetEventType(outgoingEvent.EventName)));
+ }
+ }
+
+ public async override Task ProcessFromInboxAsync(IncomingEventInfo incomingEvent, InboxConfig inboxConfig)
+ {
+ var eventType = EventTypes.GetOrDefault(incomingEvent.EventName);
+ if (eventType == null)
+ {
+ return;
+ }
+
+ var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
+ var exceptions = new List();
+ await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
+ if (exceptions.Any())
+ {
+ ThrowOriginalExceptions(eventType, exceptions);
+ }
+ }
+
+ protected override byte[] Serialize(object eventData)
+ {
+ return Serializer.Serialize(eventData);
+ }
+
+ private List GetOrCreateHandlerFactories(Type eventType)
+ {
+ return HandlerFactories.GetOrAdd(
+ eventType,
+ type =>
+ {
+ var eventName = EventNameAttribute.GetNameOrDefault(type);
+ EventTypes[eventName] = type;
+ return new List();
+ }
+ );
+ }
+
+ public Type GetEventType(string eventName)
+ {
+ return EventTypes.GetOrDefault(eventName);
+ }
+
+ protected virtual async Task PublishToDaprAsync(Type eventType, object eventData)
+ {
+ await PublishToDaprAsync(EventNameAttribute.GetNameOrDefault(eventType), eventData);
+ }
+
+ protected virtual async Task PublishToDaprAsync(string eventName, object eventData)
+ {
+ var client = await DaprClientFactory.CreateAsync();
+ await client.PublishEventAsync(pubsubName: DaprEventBusOptions.PubSubName, topicName: eventName, data: eventData);
+ }
+
+ private static bool ShouldTriggerEventForHandler(Type targetEventType, Type handlerEventType)
+ {
+ //Should trigger same type
+ if (handlerEventType == targetEventType)
+ {
+ return true;
+ }
+
+ //TODO: Support inheritance? But it does not support on subscription to RabbitMq!
+ //Should trigger for inherited types
+ if (handlerEventType.IsAssignableFrom(targetEventType))
+ {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor.cs
new file mode 100644
index 0000000000..58e2817139
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor.cs
@@ -0,0 +1,56 @@
+using System.Linq;
+using System.Text.Json.Nodes;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.SimpleStateChecking;
+
+namespace Volo.Abp.Features;
+
+public class FeaturesSimpleStateCheckerSerializerContributor :
+ ISimpleStateCheckerSerializerContributor,
+ ISingletonDependency
+{
+ public const string CheckerShortName = "F";
+
+ public string SerializeToJson(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (checker is not RequireFeaturesSimpleStateChecker featuresSimpleStateChecker)
+ {
+ return null;
+ }
+
+ var jsonObject = new JsonObject {
+ ["T"] = CheckerShortName,
+ ["A"] = featuresSimpleStateChecker.RequiresAll
+ };
+
+ var nameArray = new JsonArray();
+ foreach (var featureName in featuresSimpleStateChecker.FeatureNames)
+ {
+ nameArray.Add(featureName);
+ }
+
+ jsonObject["N"] = nameArray;
+ return jsonObject.ToJsonString();
+ }
+
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (jsonObject["T"]?.ToString() != CheckerShortName)
+ {
+ return null;
+ }
+
+ var nameArray = jsonObject["N"] as JsonArray;
+ if (nameArray == null)
+ {
+ throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
+ }
+
+ return new RequireFeaturesSimpleStateChecker(
+ (bool?)jsonObject["A"] ?? false,
+ nameArray.Select(x => x.ToString()).ToArray()
+ );
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs
index 1b437e623a..625d097441 100644
--- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs
@@ -7,8 +7,8 @@ namespace Volo.Abp.Features;
public class RequireFeaturesSimpleStateChecker : ISimpleStateChecker
where TState : IHasSimpleStateCheckers
{
- private readonly string[] _featureNames;
- private readonly bool _requiresAll;
+ public string[] FeatureNames { get; }
+ public bool RequiresAll { get; }
public RequireFeaturesSimpleStateChecker(params string[] featureNames)
: this(true, featureNames)
@@ -19,13 +19,13 @@ public class RequireFeaturesSimpleStateChecker : ISimpleStateChecker IsEnabledAsync(SimpleStateCheckerContext context)
{
var featureChecker = context.ServiceProvider.GetRequiredService();
- return await featureChecker.IsEnabledAsync(_requiresAll, _featureNames);
+ return await featureChecker.IsEnabledAsync(RequiresAll, FeatureNames);
}
}
diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs
new file mode 100644
index 0000000000..d4abc25403
--- /dev/null
+++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs
@@ -0,0 +1,56 @@
+using System.Linq;
+using System.Text.Json.Nodes;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.SimpleStateChecking;
+
+namespace Volo.Abp.GlobalFeatures;
+
+public class GlobalFeaturesSimpleStateCheckerSerializerContributor :
+ ISimpleStateCheckerSerializerContributor,
+ ISingletonDependency
+{
+ public const string CheckerShortName = "G";
+
+ public string SerializeToJson(ISimpleStateChecker checker)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (checker is not RequireGlobalFeaturesSimpleStateChecker globalFeaturesSimpleStateChecker)
+ {
+ return null;
+ }
+
+ var jsonObject = new JsonObject {
+ ["T"] = CheckerShortName,
+ ["A"] = globalFeaturesSimpleStateChecker.RequiresAll
+ };
+
+ var nameArray = new JsonArray();
+ foreach (var globalFeatureName in globalFeaturesSimpleStateChecker.GlobalFeatureNames)
+ {
+ nameArray.Add(globalFeatureName);
+ }
+
+ jsonObject["N"] = nameArray;
+ return jsonObject.ToJsonString();
+ }
+
+ public ISimpleStateChecker Deserialize(JsonObject jsonObject, TState state)
+ where TState : IHasSimpleStateCheckers
+ {
+ if (jsonObject["T"]?.ToString() != CheckerShortName)
+ {
+ return null;
+ }
+
+ var nameArray = jsonObject["N"] as JsonArray;
+ if (nameArray == null)
+ {
+ throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
+ }
+
+ return new RequireGlobalFeaturesSimpleStateChecker(
+ (bool?)jsonObject["A"] ?? false,
+ nameArray.Select(x => x.ToString()).ToArray()
+ );
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs
index 77fdaa0e1e..d37aba8397 100644
--- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs
+++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs
@@ -8,8 +8,8 @@ namespace Volo.Abp.GlobalFeatures;
public class RequireGlobalFeaturesSimpleStateChecker : ISimpleStateChecker
where TState : IHasSimpleStateCheckers
{
- private readonly string[] _globalFeatureNames;
- private readonly bool _requiresAll;
+ public string[] GlobalFeatureNames { get; }
+ public bool RequiresAll { get; }
public RequireGlobalFeaturesSimpleStateChecker(params string[] globalFeatureNames)
: this(true, globalFeatureNames)
@@ -20,23 +20,23 @@ public class RequireGlobalFeaturesSimpleStateChecker : ISimpleStateCheck
{
Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames));
- _requiresAll = requiresAll;
- _globalFeatureNames = globalFeatureNames;
+ RequiresAll = requiresAll;
+ GlobalFeatureNames = globalFeatureNames;
}
public RequireGlobalFeaturesSimpleStateChecker(bool requiresAll, params Type[] globalFeatureNames)
{
Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames));
- _requiresAll = requiresAll;
- _globalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray();
+ RequiresAll = requiresAll;
+ GlobalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray();
}
public Task IsEnabledAsync(SimpleStateCheckerContext context)
{
- var isEnabled = _requiresAll
- ? _globalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x))
- : _globalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x));
+ var isEnabled = RequiresAll
+ ? GlobalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x))
+ : GlobalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x));
return Task.FromResult(isEnabled);
}
diff --git a/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xml b/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xml
new file mode 100644
index 0000000000..be0de3a908
--- /dev/null
+++ b/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xsd b/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.Http.Client.Dapr/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Http.Client.Dapr/Volo.Abp.Http.Client.Dapr.csproj b/framework/src/Volo.Abp.Http.Client.Dapr/Volo.Abp.Http.Client.Dapr.csproj
new file mode 100644
index 0000000000..610203dae5
--- /dev/null
+++ b/framework/src/Volo.Abp.Http.Client.Dapr/Volo.Abp.Http.Client.Dapr.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpHttpClientDaprModule.cs b/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpHttpClientDaprModule.cs
new file mode 100644
index 0000000000..18a509d3ff
--- /dev/null
+++ b/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpHttpClientDaprModule.cs
@@ -0,0 +1,23 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Dapr;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Http.Client.Dapr;
+
+[DependsOn(
+ typeof(AbpHttpClientModule),
+ typeof(AbpDaprModule)
+)]
+public class AbpHttpClientDaprModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(options =>
+ {
+ options.ProxyClientBuildActions.Add((_, clientBuilder) =>
+ {
+ clientBuilder.AddHttpMessageHandler();
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpInvocationHandler.cs b/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpInvocationHandler.cs
new file mode 100644
index 0000000000..a3397b55e4
--- /dev/null
+++ b/framework/src/Volo.Abp.Http.Client.Dapr/Volo/Abp/Http/Client/Dapr/AbpInvocationHandler.cs
@@ -0,0 +1,14 @@
+using Dapr.Client;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Dapr;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Http.Client.Dapr;
+
+public class AbpInvocationHandler : InvocationHandler, ITransientDependency
+{
+ public AbpInvocationHandler(IOptions daprOptions)
+ {
+ DaprEndpoint = daprOptions.Value.HttpEndpoint;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs
index 4ccac104cd..7bc239813d 100644
--- a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs
+++ b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionHttpClientProxyExtensions.cs
@@ -212,7 +212,7 @@ public static class ServiceCollectionHttpClientProxyExtensions
}
///
- /// Checks wether the type is suitable to use with the proxying.
+ /// Checks whether the type is suitable to use with the proxying.
/// Currently the type is checked statically against some fixed conditions.
///
/// Type to check
diff --git a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
index 61494d609e..88eb8a399d 100644
--- a/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
+++ b/framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
@@ -12,7 +12,7 @@ public class LocalizableString : ILocalizableString
[NotNull]
public string Name { get; }
- public LocalizableString(Type resourceType, [NotNull] string name)
+ public LocalizableString([CanBeNull] Type resourceType, [NotNull] string name)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
ResourceType = resourceType;
@@ -20,7 +20,28 @@ public class LocalizableString : ILocalizableString
public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory)
{
- return stringLocalizerFactory.Create(ResourceType)[Name];
+ var localizer = ResourceType != null
+ ? stringLocalizerFactory.Create(ResourceType)
+ : stringLocalizerFactory.CreateDefaultOrNull();
+
+ if (localizer == null)
+ {
+ throw new AbpException($"Set {nameof(ResourceType)} or configure the default localization resource type (in the AbpLocalizationOptions)!");
+ }
+
+ var result = localizer[Name];
+
+ if (result.ResourceNotFound && ResourceType != null)
+ {
+ /* Search in the default resource if not found in the provided resource */
+ localizer = stringLocalizerFactory.CreateDefaultOrNull();
+ if (localizer != null)
+ {
+ result = localizer[Name];
+ }
+ }
+
+ return result;
}
public static LocalizableString Create([NotNull] string name)
diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizableStringSerializer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizableStringSerializer.cs
new file mode 100644
index 0000000000..6f7a487b6c
--- /dev/null
+++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizableStringSerializer.cs
@@ -0,0 +1,8 @@
+namespace Volo.Abp.Localization;
+
+public interface ILocalizableStringSerializer
+{
+ string Serialize(ILocalizableString localizableString);
+
+ ILocalizableString Deserialize(string value);
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs
new file mode 100644
index 0000000000..4cfd6e2f32
--- /dev/null
+++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs
@@ -0,0 +1,69 @@
+using System;
+using Microsoft.Extensions.Options;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Localization;
+
+public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransientDependency
+{
+ protected AbpLocalizationOptions LocalizationOptions { get; }
+
+ public LocalizableStringSerializer(IOptions localizationOptions)
+ {
+ LocalizationOptions = localizationOptions.Value;
+ }
+
+ public virtual string Serialize(ILocalizableString localizableString)
+ {
+ if (localizableString is LocalizableString realLocalizableString)
+ {
+ return $"L:{LocalizationResourceNameAttribute.GetName(realLocalizableString.ResourceType)},{realLocalizableString.Name}";
+ }
+
+ if (localizableString is FixedLocalizableString fixedLocalizableString)
+ {
+ return $"F:{fixedLocalizableString.Value}";
+ }
+
+ throw new AbpException($"Unknown {nameof(ILocalizableString)} type: {localizableString.GetType().FullName}");
+ }
+
+ public virtual ILocalizableString Deserialize(string value)
+ {
+ if (value.IsNullOrEmpty() ||
+ value.Length < 3 ||
+ value[1] != ':')
+ {
+ return new FixedLocalizableString(value);
+ }
+
+ var type = value[0];
+ switch (type)
+ {
+ case 'F':
+ return new FixedLocalizableString(value.Substring(2));
+ case 'L':
+ var commaPosition = value.IndexOf(',', 2);
+ if (commaPosition == -1)
+ {
+ throw new AbpException("Invalid LocalizableString value: " + value);
+ }
+
+ var resourceName = value.Substring(2, commaPosition - 2);
+ var name = value.Substring(commaPosition + 1);
+ if (name.IsNullOrWhiteSpace())
+ {
+ throw new AbpException("Invalid LocalizableString value: " + value);
+ }
+
+ var resourceType = LocalizationOptions.Resources.GetOrNull(resourceName)?.ResourceType;
+
+ return new LocalizableString(
+ resourceType,
+ name
+ );
+ default:
+ return new FixedLocalizableString(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
index e394e01202..d672a2f17a 100644
--- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
+++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
@@ -6,6 +6,8 @@ namespace Volo.Abp.Localization;
public class LocalizationResourceDictionary : Dictionary
{
+ private readonly Dictionary _resourcesByNames = new();
+
public LocalizationResource Add([CanBeNull] string defaultCultureName = null)
{
return Add(typeof(TResouce), defaultCultureName);
@@ -18,7 +20,12 @@ public class LocalizationResourceDictionary : Dictionary()
@@ -33,4 +40,20 @@ public class LocalizationResourceDictionary : Dictionary
[Flags]
-public enum MultiTenancySides
+public enum MultiTenancySides : byte
{
///
/// Tenant side.
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs
index b97b0056e9..d5de504afc 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using JetBrains.Annotations;
namespace Volo.Abp.Data;
@@ -26,4 +27,28 @@ public static class ExtraPropertyDictionaryExtensions
extraPropertyDictionary[key] = Enum.Parse(enumType, extraPropertyDictionary[key].ToString(), ignoreCase: true);
return extraPropertyDictionary[key];
}
-}
+
+ public static bool HasSameItems(
+ [NotNull] this ExtraPropertyDictionary dictionary,
+ [NotNull] ExtraPropertyDictionary otherDictionary)
+ {
+ Check.NotNull(dictionary, nameof(dictionary));
+ Check.NotNull(otherDictionary, nameof(otherDictionary));
+
+ if (dictionary.Count != otherDictionary.Count)
+ {
+ return false;
+ }
+
+ foreach (var key in dictionary.Keys)
+ {
+ if (!otherDictionary.ContainsKey(key) ||
+ dictionary[key]?.ToString() != otherDictionary[key]?.ToString())
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
index 0bd64d55f1..db543bb67f 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
+using JetBrains.Annotations;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Reflection;
@@ -121,4 +122,14 @@ public static class HasExtraPropertiesExtensions
source.RemoveProperty(property.Name);
}
}
+
+ public static bool HasSameExtraProperties(
+ [NotNull] this IHasExtraProperties source,
+ [NotNull] IHasExtraProperties other)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(other, nameof(other));
+
+ return source.ExtraProperties.HasSameItems(other.ExtraProperties);
+ }
}
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs
index 8f8113ba0d..2da06632cd 100644
--- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs
@@ -4,7 +4,7 @@ namespace Volo.Abp.Threading;
public class NullCancellationTokenProvider : CancellationTokenProviderBase
{
- public static NullCancellationTokenProvider Instance { get; } = new NullCancellationTokenProvider();
+ public static NullCancellationTokenProvider Instance { get; } = new();
public override CancellationToken Token => OverrideValue?.CancellationToken ?? CancellationToken.None;
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingOptions_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingOptions_Tests.cs
new file mode 100644
index 0000000000..6853046241
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingOptions_Tests.cs
@@ -0,0 +1,23 @@
+using Microsoft.Extensions.Options;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.Auditing;
+
+public class AbpAuditingOptions_Tests : AbpAuditingTestBase
+{
+ private const string ApplicationName = "TEST_APP_NAME";
+
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ base.SetAbpApplicationCreationOptions(options);
+ options.ApplicationName = ApplicationName;
+ }
+
+ [Fact]
+ public void Should_Set_Application_Name_From_Global_Application_Name_By_Default()
+ {
+ var options = GetRequiredService>().Value;
+ options.ApplicationName.ShouldBe(ApplicationName);
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthenticatedSimpleStateCheckerSerializerContributor_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthenticatedSimpleStateCheckerSerializerContributor_Tests.cs
new file mode 100644
index 0000000000..0d84d167de
--- /dev/null
+++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthenticatedSimpleStateCheckerSerializerContributor_Tests.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.Text.Json.Nodes;
+using Shouldly;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.SimpleStateChecking;
+using Xunit;
+
+namespace Volo.Abp.Authorization;
+
+public class AuthenticatedSimpleStateCheckerSerializerContributor_Tests
+{
+ [Fact]
+ public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new AuthenticatedSimpleStateCheckerSerializerContributor();
+
+ var result = serializer.SerializeToJson(
+ new RequireAuthenticatedSimpleStateChecker()
+ );
+
+ result.ShouldBe("{\"T\":\"A\"}");
+ }
+
+ [Fact]
+ public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new AuthenticatedSimpleStateCheckerSerializerContributor();
+
+ var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"A\"}");
+ var checker = serializer.Deserialize(jsonObject, new MyState());
+
+ checker.ShouldBeOfType>();
+ var globalFeaturesSimpleStateChecker = checker as RequireAuthenticatedSimpleStateChecker;
+ globalFeaturesSimpleStateChecker.ShouldNotBeNull();
+ }
+
+ private class MyState : IHasSimpleStateCheckers
+ {
+ public List> StateCheckers { get; } = new();
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs
index 42f6d3ce4a..a32939fa53 100644
--- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs
+++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs
@@ -67,9 +67,9 @@ public class Authorization_Tests : AuthorizationTestBase
}
[Fact]
- public void Should_Permission_Definition_GetGroup()
+ public async Task Should_Permission_Definition_GetGroup()
{
- _permissionDefinitionManager.GetGroups().Count.ShouldBe(1);
+ (await _permissionDefinitionManager.GetGroupsAsync()).Count.ShouldBe(1);
}
[Fact]
diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs
index b0be00fe01..50805d4b58 100644
--- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs
+++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs
@@ -29,7 +29,8 @@ public class SpecifyPermissionStateProvider : PermissionStateProvider_Tests
[Fact]
public async Task PermissionState_Test()
{
- var myPermission1 = PermissionDefinitionManager.Get("MyPermission1");
+ var myPermission1 = await PermissionDefinitionManager.GetOrNullAsync("MyPermission1");
+ myPermission1.ShouldNotBeNull();
myPermission1.StateCheckers.ShouldContain(x => x.GetType() == typeof(TestRequireEditionPermissionSimpleStateChecker));
(await StateCheckerManager.IsEnabledAsync(myPermission1)).ShouldBeFalse();
@@ -54,7 +55,8 @@ public class GlobalPermissionStateProvider : PermissionStateProvider_Tests
[Fact]
public async Task Global_PermissionState_Test()
{
- var myPermission2 = PermissionDefinitionManager.Get("MyPermission2");
+ var myPermission2 = await PermissionDefinitionManager.GetOrNullAsync("MyPermission2");
+ myPermission2.ShouldNotBeNull();
(await StateCheckerManager.IsEnabledAsync(myPermission2)).ShouldBeFalse();
diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
index f69d94ce61..9eff208198 100644
--- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
+++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
+using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Modularity.PlugIns;
using Xunit;
@@ -142,4 +143,40 @@ public class AbpApplication_Initialize_Tests
plugInModule.OnApplicationShutdownIsCalled.ShouldBeTrue();
}
}
+
+ [Fact]
+ public void Should_Set_And_Get_ApplicationName()
+ {
+ const string applicationName = "MyApplication";
+
+ using (var application = AbpApplicationFactory.Create(options =>
+ {
+ options.ApplicationName = applicationName;
+ }))
+ {
+ application.ApplicationName.ShouldBe(applicationName);
+ application.Services.GetApplicationName().ShouldBe(applicationName);
+
+ application.Initialize();
+
+ application.ServiceProvider
+ .GetRequiredService()
+ .ApplicationName
+ .ShouldBe(applicationName);
+ }
+ }
+
+ [Fact]
+ public async Task Should_Resolve_Root_Service_Provider()
+ {
+ using (var application = await AbpApplicationFactory.CreateAsync())
+ {
+ await application.InitializeAsync();
+
+ application
+ .ServiceProvider
+ .GetRequiredService()
+ .ShouldNotBeNull();
+ }
+ }
}
diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor_Tests.cs
new file mode 100644
index 0000000000..9791ac44bc
--- /dev/null
+++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor_Tests.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using System.Text.Json.Nodes;
+using Shouldly;
+using Volo.Abp.SimpleStateChecking;
+using Xunit;
+
+namespace Volo.Abp.Features;
+
+public class FeaturesSimpleStateCheckerSerializerContributor_Tests
+{
+ [Fact]
+ public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new FeaturesSimpleStateCheckerSerializerContributor();
+
+ var result = serializer.SerializeToJson(
+ new RequireFeaturesSimpleStateChecker(
+ "FeatureA",
+ "FeatureB"
+ )
+ );
+
+ result.ShouldBe("{\"T\":\"F\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
+ }
+
+ [Fact]
+ public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new FeaturesSimpleStateCheckerSerializerContributor();
+
+ var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"F\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
+ var checker = serializer.Deserialize(jsonObject, new MyState());
+
+ checker.ShouldBeOfType>();
+ var globalFeaturesSimpleStateChecker = checker as RequireFeaturesSimpleStateChecker;
+ globalFeaturesSimpleStateChecker.ShouldNotBeNull();
+ globalFeaturesSimpleStateChecker.RequiresAll.ShouldBeTrue();
+ globalFeaturesSimpleStateChecker.FeatureNames[0].ShouldBe("FeatureA");
+ globalFeaturesSimpleStateChecker.FeatureNames[1].ShouldBe("FeatureB");
+ }
+
+ private class MyState : IHasSimpleStateCheckers
+ {
+ public List> StateCheckers { get; } = new();
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs
new file mode 100644
index 0000000000..e0425775dc
--- /dev/null
+++ b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using System.Text.Json.Nodes;
+using Shouldly;
+using Volo.Abp.SimpleStateChecking;
+using Xunit;
+
+namespace Volo.Abp.GlobalFeatures;
+
+public class GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests
+{
+ [Fact]
+ public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor();
+
+ var result = serializer.SerializeToJson(
+ new RequireGlobalFeaturesSimpleStateChecker(
+ "FeatureA",
+ "FeatureB"
+ )
+ );
+
+ result.ShouldBe("{\"T\":\"G\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
+ }
+
+ [Fact]
+ public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
+ {
+ var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor();
+
+ var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"G\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
+ var checker = serializer.Deserialize