Browse Source

Merge branch 'rel-10.0' into Blazorise-1.8.8

pull/24352/head
maliming 10 months ago
parent
commit
651ebd3b4e
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      Directory.Packages.props
  2. 11
      docs/en/framework/data/entity-framework-core/index.md
  3. BIN
      docs/en/images/elsa-studio-wasm.png
  4. 97
      docs/en/modules/ai-management/index.md
  5. 78
      docs/en/modules/elsa-pro.md
  6. 4
      docs/en/modules/identity-pro.md
  7. 0
      docs/en/modules/identity/ldap.md
  8. 26
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs
  9. 2
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj
  10. 6
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs
  11. 39
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs
  12. 22
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs
  13. 21
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  14. 59
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  15. 30
      framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs
  16. 18
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs
  17. 30
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs
  18. 1
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs
  19. 5
      modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs
  20. 5
      modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs
  21. 5
      modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestModule.cs
  22. 5
      modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs
  23. 1
      modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/CmsKitEntityFrameworkCoreTestModule.cs
  24. 5
      modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs
  25. 5
      modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs
  26. 5
      modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs
  27. 5
      modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo/Abp/OpenIddict/EntityFrameworkCore/OpenIddictEntityFrameworkCoreTestModule.cs
  28. 2
      modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj
  29. 40
      modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs
  30. 1
      modules/setting-management/src/Volo.Abp.SettingManagement.Installer/AngularInstallationInfo.json
  31. 5
      modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs
  32. 5
      modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs
  33. BIN
      source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip
  34. BIN
      source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip

2
Directory.Packages.props

@ -137,7 +137,7 @@
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="7.2.0" /> <PackageVersion Include="OpenIddict.Server.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="7.2.0" /> <PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="7.2.0" /> <PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="7.2.0" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.90" /> <PackageVersion Include="Oracle.EntityFrameworkCore" Version="10.23.26000" />
<PackageVersion Include="Polly" Version="8.6.3" /> <PackageVersion Include="Polly" Version="8.6.3" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" /> <PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" /> <PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />

11
docs/en/framework/data/entity-framework-core/index.md

@ -146,7 +146,7 @@ Configure<AbpDbContextOptions>(options =>
}); });
```` ````
Add actions for the `ConfigureConventions` and `OnModelCreating` methods of the `DbContext` as shown below: Add actions for the `ConfigureConventions`, `OnModelCreating` and `OnConfiguring` methods of the `DbContext` as shown below:
````csharp ````csharp
Configure<AbpDbContextOptions>(options => Configure<AbpDbContextOptions>(options =>
@ -170,6 +170,15 @@ Configure<AbpDbContextOptions>(options =>
{ {
// This action is called for OnModelCreating method of specific DbContext. // This action is called for OnModelCreating method of specific DbContext.
}); });
options.ConfigureDefaultOnConfiguring((dbContext, optionsBuilder) =>
{
// This action is called for OnConfiguring method of all DbContexts.
});
options.ConfigureOnConfiguring<YourDbContext>((dbContext, optionsBuilder) =>
{
// This action is called for OnConfiguring method of specific DbContext.
});
}); });
```` ````

BIN
docs/en/images/elsa-studio-wasm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

97
docs/en/modules/ai-management/index.md

@ -3,8 +3,7 @@
> You must have an ABP Team or a higher license to use this module. > You must have an ABP Team or a higher license to use this module.
> **⚠️ Important Notice** > **⚠️ Important Notice**
> The **AI Management Module** is currently in **preview** and not yet production-ready. The documentation and implementation are subject to change. > The **AI Management Module** is currently in **preview**. The documentation and implementation are subject to change.
> We recommend using this module for **evaluation and experimentation** only, not in production environments for now.
This module implements AI (Artificial Intelligence) management capabilities on top of the [Artificial Intelligence Workspaces](../../framework/infrastructure/artificial-intelligence/index.md) feature of the ABP Framework and allows to manage workspaces dynamically from the application including UI components and API endpoints. This module implements AI (Artificial Intelligence) management capabilities on top of the [Artificial Intelligence Workspaces](../../framework/infrastructure/artificial-intelligence/index.md) feature of the ABP Framework and allows to manage workspaces dynamically from the application including UI components and API endpoints.
@ -130,7 +129,7 @@ PreConfigure<AbpAIWorkspaceOptions>(options =>
#### Dynamic Workspaces #### Dynamic Workspaces
* **Created through the UI** or programmatically via `IWorkspaceRepository` * **Created through the UI** or programmatically via `ApplicationWorkspaceManager` and `IWorkspaceRepository`
* **Fully manageable** - can be created, updated, activated/deactivated, and deleted * **Fully manageable** - can be created, updated, activated/deactivated, and deleted
* **Stored in database** with all configuration * **Stored in database** with all configuration
* **Ideal for** user-customizable AI features * **Ideal for** user-customizable AI features
@ -138,15 +137,30 @@ PreConfigure<AbpAIWorkspaceOptions>(options =>
Example (data seeding): Example (data seeding):
```csharp ```csharp
var workspace = new Workspace( public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDependency
name: "CustomerSupportWorkspace", {
provider: "OpenAI", private readonly IWorkspaceRepository _workspaceRepository;
modelName: "gpt-4", private readonly ApplicationWorkspaceManager _applicationWorkspaceManager;
apiKey: "your-api-key" public WorkspaceDataSeederContributor(
); IWorkspaceRepository workspaceRepository,
workspace.ApplicationName = ApplicationInfoAccessor.ApplicationName; ApplicationWorkspaceManager applicationWorkspaceManager)
workspace.SystemPrompt = "You are a helpful customer support assistant."; {
await _workspaceRepository.InsertAsync(workspace); _workspaceRepository = workspaceRepository;
_applicationWorkspaceManager = applicationWorkspaceManager;
}
public async Task SeedAsync(DataSeedContext context)
{
var workspace = await _applicationWorkspaceManager.CreateAsync(
name: "CustomerSupportWorkspace",
provider: "OpenAI",
modelName: "gpt-4");
workspace.ApiKey = "your-api-key";
workspace.SystemPrompt = "You are a helpful customer support assistant.";
await _workspaceRepository.InsertAsync(workspace);
}
``` ```
### Workspace Naming Rules ### Workspace Naming Rules
@ -172,12 +186,13 @@ The AI Management module defines the following permissions:
In addition to module-level permissions, you can restrict access to individual workspaces by setting the `RequiredPermissionName` property: In addition to module-level permissions, you can restrict access to individual workspaces by setting the `RequiredPermissionName` property:
```csharp ```csharp
var workspace = new Workspace( var workspace = await _applicationWorkspaceManager.CreateAsync(
name: "PremiumWorkspace", name: "PremiumWorkspace",
provider: "OpenAI", provider: "OpenAI",
modelName: "gpt-4", modelName: "gpt-4"
requiredPermissionName: "MyApp.PremiumFeatures"
); );
// Set a specific permission for the workspace
workspace.RequiredPermissionName = MyAppPermissions.AccessPremiumWorkspaces;
``` ```
When a workspace has a required permission: When a workspace has a required permission:
@ -430,6 +445,37 @@ Your application acts as a proxy, forwarding these requests to the AI Management
| **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally | | **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally |
| **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services | | **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services |
## Using Dynamic Workspace Configurations for custom requirements
The AI Management module allows you to access only configuration of a workspace without resolving pre-constructed chat client. This is useful when you want to use a workspace for your own purposes and you don't need to use the chat client.
The `IWorkspaceConfigurationStore` service is used to access the configuration of a workspace. It has multiple implementaations according to the usage scenario.
```csharp
public class MyService
{
private readonly IWorkspaceConfigurationStore _workspaceConfigurationStore;
public MyService(IWorkspaceConfigurationStore workspaceConfigurationStore)
{
_workspaceConfigurationStore = workspaceConfigurationStore;
}
public async Task DoSomethingAsync()
{
// Get the configuration of the workspace that can be managed dynamically.
var configuration = await _workspaceConfigurationStore.GetAsync("MyWorkspace");
// Do something with the configuration
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatClient(
config.ModelName!,
new Uri(config.ApiBaseUrl),
config.ApiKey
)
.Build();
}
}
```
## Implementing Custom AI Provider Factories ## Implementing Custom AI Provider Factories
While the AI Management module provides built-in support for OpenAI through the `Volo.AIManagement.OpenAI` package, you can easily add support for other AI providers by implementing a custom `IChatClientFactory`. While the AI Management module provides built-in support for OpenAI through the `Volo.AIManagement.OpenAI` package, you can easily add support for other AI providers by implementing a custom `IChatClientFactory`.
@ -558,14 +604,14 @@ After implementing and registering your factory:
2. **Through Code** (data seeding): 2. **Through Code** (data seeding):
```csharp ```csharp
await _workspaceRepository.InsertAsync(new Workspace( var workspace = await _applicationWorkspaceManager.CreateAsync(
GuidGenerator.Create(), name: "MyOllamaWorkspace",
"MyOllamaWorkspace", provider: "Ollama",
provider: "Ollama", modelName: "mistral"
modelName: "mistral", );
apiBaseUrl: "http://localhost:11434", workspace.ApiBaseUrl = "http://localhost:11434";
description: "Local Ollama workspace" workspace.Description = "Local Ollama workspace";
)); await _workspaceRepository.InsertAsync(workspace);
``` ```
> **Tip**: The provider name you use in `AddFactory<TFactory>("ProviderName")` must match the provider name stored in the workspace configuration in the database. > **Tip**: The provider name you use in `AddFactory<TFactory>("ProviderName")` must match the provider name stored in the workspace configuration in the database.
@ -589,7 +635,7 @@ The following custom repositories are defined:
#### Domain Services #### Domain Services
- `ApplicationWorkspaceManager`: Manages workspace operations and validations. - `ApplicationWorkspaceManager`: Manages workspace operations and validations.
- `WorkspaceConfigurationStore`: Retrieves workspace configuration with caching. - `WorkspaceConfigurationStore`: Retrieves workspace configuration with caching. Implements `IWorkspaceConfigurationStore` interface.
- `ChatClientResolver`: Resolves the appropriate `IChatClient` implementation for a workspace. - `ChatClientResolver`: Resolves the appropriate `IChatClient` implementation for a workspace.
#### Integration Services #### Integration Services
@ -618,6 +664,9 @@ Workspace configurations are cached for performance. The cache key format:
WorkspaceConfiguration:{ApplicationName}:{WorkspaceName} WorkspaceConfiguration:{ApplicationName}:{WorkspaceName}
``` ```
### HttpApi Client Layer
- `IntegrationWorkspaceConfigurationStore`: Integration service for remote workspace configuration retrieval. Implements `IWorkspaceConfigurationStore` interface.
The cache is automatically invalidated when workspaces are created, updated, or deleted. The cache is automatically invalidated when workspaces are created, updated, or deleted.
## See Also ## See Also

78
docs/en/modules/elsa-pro.md

@ -4,7 +4,7 @@
This module integrates [Elsa Workflows](https://docs.elsaworkflows.io/) into ABP Framework applications and is designed to make it easy for developers to use Elsa's capabilities within their ABP-based projects. For creating, managing, and customizing workflows themselves, please refer to [the official Elsa documentation](https://docs.elsaworkflows.io/). This module integrates [Elsa Workflows](https://docs.elsaworkflows.io/) into ABP Framework applications and is designed to make it easy for developers to use Elsa's capabilities within their ABP-based projects. For creating, managing, and customizing workflows themselves, please refer to [the official Elsa documentation](https://docs.elsaworkflows.io/).
## How to install ## How to Install
The Elsa module is not installed in [the startup templates](../solution-templates/layered-web-application) by default and must be installed manually. There are two ways of installing a module into your application and each one of these approaches is explained in the next sections. The Elsa module is not installed in [the startup templates](../solution-templates/layered-web-application) by default and must be installed manually. There are two ways of installing a module into your application and each one of these approaches is explained in the next sections.
@ -37,6 +37,23 @@ After adding the package references, open the module class of the project (e.g.:
> If you are using Blazor Web App, you need to add the `Volo.Elsa.Admin.Blazor.WebAssembly` package to the **{ProjectName}.Blazor.Client.csproj** project and add the `Volo.Elsa.Admin.Blazor.Server` package to the **{ProjectName}.Blazor.csproj** project. > If you are using Blazor Web App, you need to add the `Volo.Elsa.Admin.Blazor.WebAssembly` package to the **{ProjectName}.Blazor.Client.csproj** project and add the `Volo.Elsa.Admin.Blazor.Server` package to the **{ProjectName}.Blazor.csproj** project.
### `AbpElsaAspNetCoreModule` and `AbpElsaIdentityModule`
These two modules generally will be added to your authentication project. Please add `Volo.Elsa.Abp.AspNetCore` and `Volo.Elsa.Abp.Identity` packages to your project and add the `AbpElsaAspNetCoreModule` and `AbpElsaIdentityModule` to the `DependsOn` attribute of your module class based on your project structure:
```xml
<PackageReference Include="Volo.Abp.Elsa.AspNetCore" Version="x.x.x" />
<PackageReference Include="Volo.Abp.Elsa.Identity" Version="x.x.x" />
```
```csharp
[DependsOn(
//...
typeof(AbpElsaAspNetCoreModule),
typeof(AbpElsaIdentityModule)
)]
```
## The Elsa Module ## The Elsa Module
The Elsa Workflows has its own database provider, and also has a Tenant/Role/User system. They are under active development, so the ABP Elsa module is not yet fully integrated. Below is the current status of each module in the ABP's Elsa Module: The Elsa Workflows has its own database provider, and also has a Tenant/Role/User system. They are under active development, so the ABP Elsa module is not yet fully integrated. Below is the current status of each module in the ABP's Elsa Module:
@ -56,6 +73,49 @@ The rest of the projects/modules are basically empty and will be implemented in
- `AbpElsaBlazorWebAssemblyModule(Volo.Elsa.Abp.Blazor.WebAssembly)` - `AbpElsaBlazorWebAssemblyModule(Volo.Elsa.Abp.Blazor.WebAssembly)`
- `AbpElsaWebModule(Volo.Elsa.Abp.Web)` - `AbpElsaWebModule(Volo.Elsa.Abp.Web)`
## Configure the Elsa Server
You need to configure Elsa in your ABP application to use its features. You can do that in the `ConfigureServices` method of your `YourElsaAppModule` class as shown below:
> For more information about configuring Elsa, please refer to [the official Elsa documentation](https://docs.elsaworkflows.io/).
```cs
private void ConfigureElsa(ServiceConfigurationContext context, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("Default")!;
context.Services
.AddElsa(elsa => elsa
.UseAbpIdentity(identity => // Use UseAbpIdentity instead of UseIdentity to integrate with ABP Identity module
{
identity.TokenOptions = options => options.SigningKey = "large-signing-key-for-signing-JWT-tokens";
})
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlServer(connectionString)))
.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlServer(connectionString)))
.UseScheduling()
.UseJavaScript()
.UseLiquid()
.UseCSharp()
.UseHttp(http => http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options))
.UseWorkflowsApi()
.AddActivitiesFrom<YourElsaAppModule>()
.AddWorkflowsFrom<YourElsaAppModule>()
);
}
```
## Elsa Database Migration
Elsa module uses its own database context and migration system, ABP Elsa module doesn't contain any `aggregate root/entity` at the moment. So, **you don't need to create any initial migration for Elsa module**. You just need to configure the Elsa Services as follows:
```cs
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlServer(connectionString)))
.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef => ef.UseSqlServer(connectionString)))
```
When you run your application, Elsa will create its own database tables if they do not exist.
> See [how to configure Elsa Workflows to use different database providers for persistence, including SQL Server, PostgreSQL, and MongoDB](https://docs.elsaworkflows.io/getting-started/database-configuration) for more information.
### Elsa Module Permissions ### Elsa Module Permissions
The Elsa Workflow API endpoints check permissions. Also, it has a `*` wildcard permission to allow all permissions. The Elsa Workflow API endpoints check permissions. Also, it has a `*` wildcard permission to allow all permissions.
@ -72,14 +132,24 @@ You can also grant parts of the permissions to a role or user. It will add the `
### Elsa Studio ### Elsa Studio
Elsa Studio is an **independent** web application that allows you to design, manage, and execute workflows. It is built using **Blazor Server/WebAssembly**. [Elsa Studio](https://docs.elsaworkflows.io/application-types/elsa-studio) is a **standalone** web application that allows you to design, manage, and execute workflows. It is built using **Blazor Server/WebAssembly**.
`ElsaDemoApp.Studio.WASM` is a sample Blazor WebAssembly project that demonstrates how to use Elsa Studio with ELSA Server with ABP Framework.
> Elsa Studio has its own layout and theme, and you can't integrate it into an ABP Blazor project for now.
![Elsa Studio](../images/elsa-studio-wasm.png)
Please check the [Elsa Workflows - Sample Workflow Demo](../samples/elsa-workflows-demo.md) document to download its source code for review.
#### Elsa Studio Authentication
Elsa Studio requires authentication and there are two ways to authenticate Elsa Studio: Elsa Studio requires authentication and there are two ways to authenticate Elsa Studio:
* Password Flow Authentication * Password Flow Authentication
* Code Flow Authentication * Code Flow Authentication
#### Elsa Studio - Password Flow Authentication ##### Elsa Studio - Password Flow Authentication
The `AbpElsaIdentityModule(Volo.Elsa.Abp.Identity)` module is used to integrate with [ABP Identity module](./identity-pro.md) to check Elsa Studio *username* and *password* against ABP Identity. The `AbpElsaIdentityModule(Volo.Elsa.Abp.Identity)` module is used to integrate with [ABP Identity module](./identity-pro.md) to check Elsa Studio *username* and *password* against ABP Identity.
@ -109,7 +179,7 @@ Once, you logged in to the application, you can start defining workflows, manage
![elsa-main](../images/elsa-main-page.png) ![elsa-main](../images/elsa-main-page.png)
#### Elsa Studio - Code Flow Authentication ##### Elsa Studio - Code Flow Authentication
ABP applications use [OpenIddict](./openiddict-pro.md) for authentication. So, you can use the [Authorization Code Flow](https://oauth.net/2/grant-types/authorization-code/) to authenticate Elsa Studio. ABP applications use [OpenIddict](./openiddict-pro.md) for authentication. So, you can use the [Authorization Code Flow](https://oauth.net/2/grant-types/authorization-code/) to authenticate Elsa Studio.

4
docs/en/modules/identity-pro.md

@ -434,8 +434,8 @@ This module doesn't define any additional distributed event. See the [standard d
## See Also ## See Also
* [Import External Users](./identity/import-external-users.md) * [Import External Users](./identity/import-external-users.md)
* [LDAP Login](./identity/idap.md) * [LDAP Login](./identity/ldap.md)
* [OAuth Login](./identity/oauth-login.md) * [OAuth Login](./identity/oauth-login.md)
* [Periodic Password Change (Password Aging)](./identity/periodic-password-change.md) * [Periodic Password Change (Password Aging)](./identity/periodic-password-change.md)
* [Two Factor Authentication](./identity/two-factor-authentication.md) * [Two Factor Authentication](./identity/two-factor-authentication.md)
* [Session Management](./identity/session-management.md) * [Session Management](./identity/session-management.md)

0
docs/en/modules/identity/idap.md → docs/en/modules/identity/ldap.md

26
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs

@ -67,7 +67,7 @@ public class TelemetryService : ITelemetryService, IScopedDependency
private Task AddActivityAsync(ActivityContext context) private Task AddActivityAsync(ActivityContext context)
{ {
_ = Task.Run(async () => var telemetryTask = Task.Run(async () =>
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
@ -81,6 +81,30 @@ public class TelemetryService : ITelemetryService, IScopedDependency
telemetryActivitySender); telemetryActivitySender);
}); });
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{
try
{
telemetryTask.Wait(TimeSpan.FromSeconds(10));
}
catch
{
// ignored
}
};
Console.CancelKeyPress += (_, _) =>
{
try
{
telemetryTask.Wait(TimeSpan.FromSeconds(10));
}
catch
{
// ignored
}
};
return Task.CompletedTask; return Task.CompletedTask;
} }

2
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj

@ -22,8 +22,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Oracle.EntityFrameworkCore" /> <PackageReference Include="Oracle.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore" VersionOverride="10.0.0-rc.2.25502.107" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" VersionOverride="10.0.0-rc.2.25502.107" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.EntityFrameworkCore;
public class AbpSqliteOptions
{
public int? BusyTimeout { get; set; }
}

39
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs

@ -0,0 +1,39 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace Volo.Abp.EntityFrameworkCore.Interceptors;
/// <summary>
/// https://github.com/dotnet/efcore/issues/29514
/// </summary>
public class SqliteBusyTimeoutSaveChangesInterceptor : SaveChangesInterceptor
{
private readonly string _pragmaCommand;
public SqliteBusyTimeoutSaveChangesInterceptor(int timeoutMilliseconds)
{
_pragmaCommand = $"PRAGMA busy_timeout={timeoutMilliseconds};";
}
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
if (eventData.Context != null)
{
eventData.Context.Database.ExecuteSqlRaw(_pragmaCommand);
}
return result;
}
public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
if (eventData.Context != null)
{
await eventData.Context.Database.ExecuteSqlRawAsync(_pragmaCommand, cancellationToken: cancellationToken);
}
return await base.SavingChangesAsync(eventData, result, cancellationToken);
}
}

22
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.EntityFrameworkCore.GlobalFilters;
using Volo.Abp.EntityFrameworkCore.Interceptors;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.Sqlite; namespace Volo.Abp.EntityFrameworkCore.Sqlite;
@ -8,11 +10,31 @@ namespace Volo.Abp.EntityFrameworkCore.Sqlite;
)] )]
public class AbpEntityFrameworkCoreSqliteModule : AbpModule public class AbpEntityFrameworkCoreSqliteModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(options =>
{
options.BusyTimeout = 5000;
});
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<AbpEfCoreGlobalFilterOptions>(options => Configure<AbpEfCoreGlobalFilterOptions>(options =>
{ {
options.UseDbFunction = true; options.UseDbFunction = true;
}); });
var sqliteOptions = context.Services.ExecutePreConfiguredActions<AbpSqliteOptions>();
if (sqliteOptions.BusyTimeout.HasValue)
{
Configure<AbpDbContextOptions>(options =>
{
options.ConfigureDefaultOnConfiguring((dbContext, dbContextOptionsBuilder) =>
{
dbContextOptionsBuilder.AddInterceptors(new SqliteBusyTimeoutSaveChangesInterceptor(sqliteOptions.BusyTimeout.Value));
}, overrideExisting: false);
});
}
} }
} }

21
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -117,6 +117,17 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{ {
optionsBuilder.ConfigureWarnings(c => c.Ignore(RelationalEventId.PendingModelChangesWarning)); optionsBuilder.ConfigureWarnings(c => c.Ignore(RelationalEventId.PendingModelChangesWarning));
base.OnConfiguring(optionsBuilder); base.OnConfiguring(optionsBuilder);
if (LazyServiceProvider == null || Options == null)
{
return;
}
Options.Value.DefaultOnConfiguringAction?.Invoke(this, optionsBuilder);
foreach (var onConfiguringAction in Options.Value.OnConfiguringActions.GetOrDefault(typeof(TDbContext)) ?? [])
{
onConfiguringAction.As<Action<DbContext, DbContextOptionsBuilder>>().Invoke(this, optionsBuilder);
}
} }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
@ -804,7 +815,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder, modelBuilder,
mutableEntityType mutableEntityType
); );
entityTypeBuilder.ConfigureByConvention(); entityTypeBuilder.ConfigureByConvention();
ConfigureGlobalFilters<TEntity>(modelBuilder, mutableEntityType, entityTypeBuilder); ConfigureGlobalFilters<TEntity>(modelBuilder, mutableEntityType, entityTypeBuilder);
@ -821,7 +832,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
protected virtual void ConfigureGlobalFilters<TEntity>( protected virtual void ConfigureGlobalFilters<TEntity>(
ModelBuilder modelBuilder, ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType, IMutableEntityType mutableEntityType,
EntityTypeBuilder<TEntity> entityTypeBuilder) EntityTypeBuilder<TEntity> entityTypeBuilder)
where TEntity : class where TEntity : class
{ {
@ -852,7 +863,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{ {
return; return;
} }
foreach (var property in mutableEntityType.GetProperties(). foreach (var property in mutableEntityType.GetProperties().
Where(property => property.PropertyInfo != null && Where(property => property.PropertyInfo != null &&
@ -864,7 +875,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder, modelBuilder,
mutableEntityType mutableEntityType
); );
entityTypeBuilder entityTypeBuilder
.Property(property.Name) .Property(property.Name)
.HasConversion(property.ClrType == typeof(DateTime) .HasConversion(property.ClrType == typeof(DateTime)
@ -874,7 +885,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
} }
protected virtual void ConfigureValueGenerated<TEntity>( protected virtual void ConfigureValueGenerated<TEntity>(
ModelBuilder modelBuilder, ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType) IMutableEntityType mutableEntityType)
where TEntity : class where TEntity : class
{ {

59
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

@ -27,8 +27,12 @@ public class AbpDbContextOptions
internal Action<DbContext, ModelBuilder>? DefaultOnModelCreatingAction { get; set; } internal Action<DbContext, ModelBuilder>? DefaultOnModelCreatingAction { get; set; }
internal Action<DbContext, DbContextOptionsBuilder>? DefaultOnConfiguringAction { get; set; }
internal Dictionary<Type, List<object>> OnModelCreatingActions { get; } internal Dictionary<Type, List<object>> OnModelCreatingActions { get; }
internal Dictionary<Type, List<object>> OnConfiguringActions { get; }
public AbpDbContextOptions() public AbpDbContextOptions()
{ {
DefaultPreConfigureActions = new List<Action<AbpDbContextConfigurationContext>>(); DefaultPreConfigureActions = new List<Action<AbpDbContextConfigurationContext>>();
@ -37,6 +41,7 @@ public class AbpDbContextOptions
DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>(); DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>();
ConventionActions = new Dictionary<Type, List<object>>(); ConventionActions = new Dictionary<Type, List<object>>();
OnModelCreatingActions = new Dictionary<Type, List<object>>(); OnModelCreatingActions = new Dictionary<Type, List<object>>();
OnConfiguringActions = new Dictionary<Type, List<object>>();
} }
public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action) public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action)
@ -53,11 +58,18 @@ public class AbpDbContextOptions
DefaultConfigureAction = action; DefaultConfigureAction = action;
} }
public void ConfigureDefaultConvention([NotNull] Action<DbContext, ModelConfigurationBuilder> action) public void ConfigureDefaultConvention([NotNull] Action<DbContext, ModelConfigurationBuilder> action, bool overrideExisting = false)
{ {
Check.NotNull(action, nameof(action)); Check.NotNull(action, nameof(action));
DefaultConventionAction = action; if (overrideExisting)
{
DefaultConventionAction = action;
}
else
{
DefaultConventionAction += action;
}
} }
public void ConfigureConventions<TDbContext>([NotNull] Action<TDbContext, ModelConfigurationBuilder> action) public void ConfigureConventions<TDbContext>([NotNull] Action<TDbContext, ModelConfigurationBuilder> action)
@ -78,11 +90,32 @@ public class AbpDbContextOptions
actions.Add(action); actions.Add(action);
} }
public void ConfigureDefaultOnModelCreating([NotNull] Action<DbContext, ModelBuilder> action) public void ConfigureDefaultOnModelCreating([NotNull] Action<DbContext, ModelBuilder> action, bool overrideExisting = false)
{ {
Check.NotNull(action, nameof(action)); Check.NotNull(action, nameof(action));
DefaultOnModelCreatingAction = action; if (overrideExisting)
{
DefaultOnModelCreatingAction = action;
}
else
{
DefaultOnModelCreatingAction += action;
}
}
public void ConfigureDefaultOnConfiguring([NotNull] Action<DbContext, DbContextOptionsBuilder> action, bool overrideExisting = false)
{
Check.NotNull(action, nameof(action));
if (overrideExisting)
{
DefaultOnConfiguringAction = action;
}
else
{
DefaultOnConfiguringAction += action;
}
} }
public void ConfigureOnModelCreating<TDbContext>([NotNull] Action<TDbContext, ModelBuilder> action) public void ConfigureOnModelCreating<TDbContext>([NotNull] Action<TDbContext, ModelBuilder> action)
@ -103,6 +136,24 @@ public class AbpDbContextOptions
actions.Add(action); actions.Add(action);
} }
public void ConfigureOnConfiguring<TDbContext>([NotNull] Action<TDbContext, DbContextOptionsBuilder> action)
where TDbContext : AbpDbContext<TDbContext>
{
Check.NotNull(action, nameof(action));
var actions = OnConfiguringActions.GetOrDefault(typeof(TDbContext));
if (actions == null)
{
OnConfiguringActions[typeof(TDbContext)] = new List<object>
{
new Action<DbContext, DbContextOptionsBuilder>((dbContext, builder) => action((TDbContext)dbContext, builder))
};
return;
}
actions.Add(action);
}
public bool IsConfiguredDefault() public bool IsConfiguredDefault()
{ {
return DefaultConfigureAction != null; return DefaultConfigureAction != null;

30
framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs

@ -168,7 +168,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear()); GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
} }
protected async override Task PublishToEventBusAsync(Type eventType, object eventData) protected override async Task PublishToEventBusAsync(Type eventType, object eventData)
{ {
var headers = new Headers var headers = new Headers
{ {
@ -193,7 +193,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
unitOfWork.AddOrReplaceDistributedEvent(eventRecord); unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
} }
public async override Task PublishFromOutboxAsync( public override async Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent, OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig) OutboxConfig outboxConfig)
{ {
@ -206,13 +206,18 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!)); headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!));
} }
await PublishAsync( var result = await PublishAsync(
AbpKafkaEventBusOptions.TopicName, AbpKafkaEventBusOptions.TopicName,
outgoingEvent.EventName, outgoingEvent.EventName,
outgoingEvent.EventData, outgoingEvent.EventData,
headers headers
); );
if (result.Status != PersistenceStatus.Persisted)
{
throw new AbpException($"Failed to publish event '{outgoingEvent.EventName}' to topic '{AbpKafkaEventBusOptions.TopicName}'. Status: {result.Status}");
}
using (CorrelationIdProvider.Change(outgoingEvent.GetCorrelationId())) using (CorrelationIdProvider.Change(outgoingEvent.GetCorrelationId()))
{ {
await TriggerDistributedEventSentAsync(new DistributedEventSent() await TriggerDistributedEventSentAsync(new DistributedEventSent()
@ -224,7 +229,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
} }
} }
public async override Task PublishManyFromOutboxAsync(IEnumerable<OutgoingEventInfo> outgoingEvents, OutboxConfig outboxConfig) public override async Task PublishManyFromOutboxAsync(IEnumerable<OutgoingEventInfo> outgoingEvents, OutboxConfig outboxConfig)
{ {
var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName); var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName);
var outgoingEventArray = outgoingEvents.ToArray(); var outgoingEventArray = outgoingEvents.ToArray();
@ -242,7 +247,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!)); headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!));
} }
producer.Produce( var result = await producer.ProduceAsync(
AbpKafkaEventBusOptions.TopicName, AbpKafkaEventBusOptions.TopicName,
new Message<string, byte[]> new Message<string, byte[]>
{ {
@ -251,6 +256,11 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
Headers = headers Headers = headers
}); });
if (result.Status != PersistenceStatus.Persisted)
{
throw new AbpException($"Failed to publish event '{outgoingEvent.EventName}' to topic '{AbpKafkaEventBusOptions.TopicName}'. Status: {result.Status}");
}
using (CorrelationIdProvider.Change(outgoingEvent.GetCorrelationId())) using (CorrelationIdProvider.Change(outgoingEvent.GetCorrelationId()))
{ {
await TriggerDistributedEventSentAsync(new DistributedEventSent() await TriggerDistributedEventSentAsync(new DistributedEventSent()
@ -263,7 +273,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
} }
} }
public async override Task ProcessFromInboxAsync( public override async Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent, IncomingEventInfo incomingEvent,
InboxConfig inboxConfig) InboxConfig inboxConfig)
{ {
@ -290,12 +300,16 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
return Serializer.Serialize(eventData); return Serializer.Serialize(eventData);
} }
private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers) private async Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers)
{ {
var eventName = EventNameAttribute.GetNameOrDefault(eventType); var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData); var body = Serializer.Serialize(eventData);
return PublishAsync(topicName, eventName, body, headers); var result = await PublishAsync(topicName, eventName, body, headers);
if (result.Status != PersistenceStatus.Persisted)
{
throw new AbpException($"Failed to publish event '{eventName}' to topic '{topicName}'. Status: {result.Status}");
}
} }
private Task<DeliveryResult<string, byte[]>> PublishAsync( private Task<DeliveryResult<string, byte[]>> PublishAsync(

18
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs

@ -17,7 +17,7 @@ public class ProducerPool : IProducerPool, ISingletonDependency
protected ConcurrentDictionary<string, Lazy<IProducer<string, byte[]>>> Producers { get; } protected ConcurrentDictionary<string, Lazy<IProducer<string, byte[]>>> Producers { get; }
protected TimeSpan TotalDisposeWaitDuration { get; set; } = TimeSpan.FromSeconds(10); protected TimeSpan TotalDisposeWaitDuration { get; set; } = TimeSpan.FromSeconds(10);
protected TimeSpan DefaultTransactionsWaitDuration { get; set; } = TimeSpan.FromSeconds(30); protected TimeSpan DefaultTransactionsWaitDuration { get; set; } = TimeSpan.FromSeconds(30);
public ILogger<ProducerPool> Logger { get; set; } public ILogger<ProducerPool> Logger { get; set; }
@ -41,8 +41,10 @@ public class ProducerPool : IProducerPool, ISingletonDependency
{ {
var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection).ToDictionary(k => k.Key, v => v.Value)); var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection).ToDictionary(k => k.Key, v => v.Value));
Options.ConfigureProducer?.Invoke(producerConfig); Options.ConfigureProducer?.Invoke(producerConfig);
producerConfig.Acks ??= Acks.All;
producerConfig.EnableIdempotence ??= true;
return new ProducerBuilder<string, byte[]>(producerConfig).Build(); return new ProducerBuilder<string, byte[]>(producerConfig).Build();
})).Value; })).Value;
} }
@ -70,7 +72,7 @@ public class ProducerPool : IProducerPool, ISingletonDependency
foreach (var producer in Producers.Values) foreach (var producer in Producers.Values)
{ {
var poolItemDisposeStopwatch = Stopwatch.StartNew(); var poolItemDisposeStopwatch = Stopwatch.StartNew();
try try
{ {
producer.Value.Dispose(); producer.Value.Dispose();
@ -78,19 +80,19 @@ public class ProducerPool : IProducerPool, ISingletonDependency
catch catch
{ {
} }
poolItemDisposeStopwatch.Stop(); poolItemDisposeStopwatch.Stop();
remainingWaitDuration = remainingWaitDuration > poolItemDisposeStopwatch.Elapsed remainingWaitDuration = remainingWaitDuration > poolItemDisposeStopwatch.Elapsed
? remainingWaitDuration.Subtract(poolItemDisposeStopwatch.Elapsed) ? remainingWaitDuration.Subtract(poolItemDisposeStopwatch.Elapsed)
: TimeSpan.Zero; : TimeSpan.Zero;
} }
poolDisposeStopwatch.Stop(); poolDisposeStopwatch.Stop();
Logger.LogInformation( Logger.LogInformation(
$"Disposed Kafka Producer Pool ({Producers.Count} producers in {poolDisposeStopwatch.Elapsed.TotalMilliseconds:0.00} ms)."); $"Disposed Kafka Producer Pool ({Producers.Count} producers in {poolDisposeStopwatch.Elapsed.TotalMilliseconds:0.00} ms).");
if (poolDisposeStopwatch.Elapsed.TotalSeconds > 5.0) if (poolDisposeStopwatch.Elapsed.TotalSeconds > 5.0)
{ {
Logger.LogWarning( Logger.LogWarning(

30
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -8,25 +10,31 @@ namespace Volo.Abp.MultiTenancy;
public class TenantResolver : ITenantResolver, ITransientDependency public class TenantResolver : ITenantResolver, ITransientDependency
{ {
private readonly IServiceProvider _serviceProvider; public ILogger<TenantResolver> Logger { get; set; }
private readonly AbpTenantResolveOptions _options;
protected IServiceProvider ServiceProvider { get; }
protected AbpTenantResolveOptions Options { get; }
public TenantResolver(IOptions<AbpTenantResolveOptions> options, IServiceProvider serviceProvider) public TenantResolver(IOptions<AbpTenantResolveOptions> options, IServiceProvider serviceProvider)
{ {
_serviceProvider = serviceProvider; Logger = NullLogger<TenantResolver>.Instance;
_options = options.Value;
ServiceProvider = serviceProvider;
Options = options.Value;
} }
public virtual async Task<TenantResolveResult> ResolveTenantIdOrNameAsync() public virtual async Task<TenantResolveResult> ResolveTenantIdOrNameAsync()
{ {
var result = new TenantResolveResult(); var result = new TenantResolveResult();
using (var serviceScope = _serviceProvider.CreateScope()) Logger.LogDebug("Starting resolving tenant...");
using (var serviceScope = ServiceProvider.CreateScope())
{ {
var context = new TenantResolveContext(serviceScope.ServiceProvider); var context = new TenantResolveContext(serviceScope.ServiceProvider);
foreach (var tenantResolver in _options.TenantResolvers) foreach (var tenantResolver in Options.TenantResolvers)
{ {
Logger.LogDebug("Trying to resolve tenant through '{TenantResolverName}'...", tenantResolver.Name);
await tenantResolver.ResolveAsync(context); await tenantResolver.ResolveAsync(context);
result.AppliedResolvers.Add(tenantResolver.Name); result.AppliedResolvers.Add(tenantResolver.Name);
@ -34,15 +42,21 @@ public class TenantResolver : ITenantResolver, ITransientDependency
if (context.HasResolvedTenantOrHost()) if (context.HasResolvedTenantOrHost())
{ {
result.TenantIdOrName = context.TenantIdOrName; result.TenantIdOrName = context.TenantIdOrName;
Logger.LogDebug("Tenant resolved by '{TenantResolverName}' as '{TenantIdOrName}'.", tenantResolver.Name, result.TenantIdOrName ?? "Host");
break; break;
} }
} }
} }
if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(_options.FallbackTenant)) if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(Options.FallbackTenant))
{ {
result.TenantIdOrName = _options.FallbackTenant; result.TenantIdOrName = Options.FallbackTenant;
result.AppliedResolvers.Add(TenantResolverNames.FallbackTenant); result.AppliedResolvers.Add(TenantResolverNames.FallbackTenant);
Logger.LogDebug("No tenant resolved. Using fallback tenant as '{FallbackTenant}'.", result.TenantIdOrName);
}
else if (result.TenantIdOrName.IsNullOrEmpty())
{
Logger.LogDebug("No tenant resolved.");
} }
return result; return result;

1
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs

@ -30,6 +30,7 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
TestEntityExtensionConfigurator.Configure(); TestEntityExtensionConfigurator.Configure();
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
} }
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)

5
modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs

@ -16,6 +16,11 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore;
)] )]
public class AbpAuditLoggingEntityFrameworkCoreTestModule : AbpModule public class AbpAuditLoggingEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs

@ -16,6 +16,11 @@ namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore;
)] )]
public class AbpBackgroundJobsEntityFrameworkCoreTestModule : AbpModule public class AbpBackgroundJobsEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestModule.cs

@ -15,6 +15,11 @@ namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
)] )]
public class BlobStoringDatabaseEntityFrameworkCoreTestModule : AbpModule public class BlobStoringDatabaseEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs

@ -18,6 +18,11 @@ namespace Volo.Blogging.EntityFrameworkCore
{ {
private SqliteConnection _sqliteConnection; private SqliteConnection _sqliteConnection;
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
_sqliteConnection = CreateDatabaseAndGetConnection(); _sqliteConnection = CreateDatabaseAndGetConnection();

1
modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/CmsKitEntityFrameworkCoreTestModule.cs

@ -20,6 +20,7 @@ public class CmsKitEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
context.Services.AddDataMigrationEnvironment(); context.Services.AddDataMigrationEnvironment();
} }

5
modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs

@ -15,6 +15,11 @@ namespace Volo.Docs.EntityFrameworkCore
)] )]
public class DocsEntityFrameworkCoreTestModule : AbpModule public class DocsEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs

@ -19,6 +19,11 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
)] )]
public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs

@ -18,6 +18,11 @@ namespace Volo.Abp.Identity.EntityFrameworkCore;
)] )]
public class AbpIdentityEntityFrameworkCoreTestModule : AbpModule public class AbpIdentityEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo/Abp/OpenIddict/EntityFrameworkCore/OpenIddictEntityFrameworkCoreTestModule.cs

@ -20,6 +20,11 @@ namespace Volo.Abp.OpenIddict.EntityFrameworkCore;
)] )]
public class OpenIddictEntityFrameworkCoreTestModule : AbpModule public class OpenIddictEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

2
modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj

@ -15,10 +15,10 @@
<ProjectReference Include="..\..\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" /> <ProjectReference Include="..\..\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.Domain.Tests\Volo.Abp.PermissionManagement.Domain.Tests.csproj" /> <ProjectReference Include="..\Volo.Abp.PermissionManagement.Domain.Tests\Volo.Abp.PermissionManagement.Domain.Tests.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj" /> <ProjectReference Include="..\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup> </ItemGroup>

40
modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs

@ -1,36 +1,62 @@
using System; using System.Threading.Tasks;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using Microsoft.Data.Sqlite;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore; namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
[DependsOn( [DependsOn(
typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementTestBaseModule))] typeof(AbpPermissionManagementTestBaseModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class AbpPermissionManagementEntityFrameworkCoreTestModule : AbpModule public class AbpPermissionManagementEntityFrameworkCoreTestModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.AddEntityFrameworkInMemoryDatabase(); PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
var databaseName = Guid.NewGuid().ToString(); public override void ConfigureServices(ServiceConfigurationContext context)
{
var sqliteConnection = CreateDatabaseAndGetConnection();
Configure<AbpDbContextOptions>(options => Configure<AbpDbContextOptions>(options =>
{ {
options.Configure(abpDbContextConfigurationContext => options.Configure(abpDbContextConfigurationContext =>
{ {
abpDbContextConfigurationContext.DbContextOptions.UseInMemoryDatabase(databaseName); abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
}); });
}); });
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions
});
context.Services.AddAlwaysDisableUnitOfWorkTransaction(); context.Services.AddAlwaysDisableUnitOfWorkTransaction();
} }
private static SqliteConnection CreateDatabaseAndGetConnection()
{
var connection = new AbpUnitTestSqliteConnection("Data Source=:memory:");
connection.Open();
new PermissionManagementDbContext(
new DbContextOptionsBuilder<PermissionManagementDbContext>().UseSqlite(connection).Options
).GetService<IRelationalDatabaseCreator>().CreateTables();
return connection;
}
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var task = context.ServiceProvider.GetRequiredService<AbpPermissionManagementDomainModule>().GetInitializeDynamicPermissionsTask(); var task = context.ServiceProvider.GetRequiredService<AbpPermissionManagementDomainModule>().GetInitializeDynamicPermissionsTask();

1
modules/setting-management/src/Volo.Abp.SettingManagement.Installer/AngularInstallationInfo.json

@ -2,6 +2,7 @@
"packages":[ "packages":[
{ {
"name": "@abp/ng.setting-management", "name": "@abp/ng.setting-management",
"keepPackageInPackageJson": true,
"appRoutingModuleConfiguration":{ "appRoutingModuleConfiguration":{
"routes":[ "routes":[
"{ path: 'setting-management', loadChildren: () => import('@abp/ng.setting-management').then(c => c.createRoutes()),}" "{ path: 'setting-management', loadChildren: () => import('@abp/ng.setting-management').then(c => c.createRoutes()),}"

5
modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs

@ -17,6 +17,11 @@ namespace Volo.Abp.SettingManagement.EntityFrameworkCore;
)] )]
public class AbpSettingManagementEntityFrameworkCoreTestModule : AbpModule public class AbpSettingManagementEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

5
modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs

@ -17,6 +17,11 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore;
)] )]
public class AbpTenantManagementEntityFrameworkCoreTestModule : AbpModule public class AbpTenantManagementEntityFrameworkCoreTestModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var sqliteConnection = CreateDatabaseAndGetConnection(); var sqliteConnection = CreateDatabaseAndGetConnection();

BIN
source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip

Binary file not shown.

BIN
source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip

Binary file not shown.
Loading…
Cancel
Save