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.Validation.AspNetCore" 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.Extensions.Http" Version="3.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
Configure<AbpDbContextOptions>(options =>
@ -170,6 +170,15 @@ Configure<AbpDbContextOptions>(options =>
{
// 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.
> **⚠️ Important Notice**
> The **AI Management Module** is currently in **preview** and not yet production-ready. The documentation and implementation are subject to change.
> We recommend using this module for **evaluation and experimentation** only, not in production environments for now.
> The **AI Management Module** is currently in **preview**. The documentation and implementation are subject to change.
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
* **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
* **Stored in database** with all configuration
* **Ideal for** user-customizable AI features
@ -138,15 +137,30 @@ PreConfigure<AbpAIWorkspaceOptions>(options =>
Example (data seeding):
```csharp
var workspace = new Workspace(
name: "CustomerSupportWorkspace",
provider: "OpenAI",
modelName: "gpt-4",
apiKey: "your-api-key"
);
workspace.ApplicationName = ApplicationInfoAccessor.ApplicationName;
workspace.SystemPrompt = "You are a helpful customer support assistant.";
await _workspaceRepository.InsertAsync(workspace);
public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDependency
{
private readonly IWorkspaceRepository _workspaceRepository;
private readonly ApplicationWorkspaceManager _applicationWorkspaceManager;
public WorkspaceDataSeederContributor(
IWorkspaceRepository workspaceRepository,
ApplicationWorkspaceManager applicationWorkspaceManager)
{
_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
@ -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:
```csharp
var workspace = new Workspace(
var workspace = await _applicationWorkspaceManager.CreateAsync(
name: "PremiumWorkspace",
provider: "OpenAI",
modelName: "gpt-4",
requiredPermissionName: "MyApp.PremiumFeatures"
modelName: "gpt-4"
);
// Set a specific permission for the workspace
workspace.RequiredPermissionName = MyAppPermissions.AccessPremiumWorkspaces;
```
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 |
| **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
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):
```csharp
await _workspaceRepository.InsertAsync(new Workspace(
GuidGenerator.Create(),
"MyOllamaWorkspace",
provider: "Ollama",
modelName: "mistral",
apiBaseUrl: "http://localhost:11434",
description: "Local Ollama workspace"
));
var workspace = await _applicationWorkspaceManager.CreateAsync(
name: "MyOllamaWorkspace",
provider: "Ollama",
modelName: "mistral"
);
workspace.ApiBaseUrl = "http://localhost:11434";
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.
@ -589,7 +635,7 @@ The following custom repositories are defined:
#### Domain Services
- `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.
#### Integration Services
@ -618,6 +664,9 @@ Workspace configurations are cached for performance. The cache key format:
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.
## 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/).
## 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.
@ -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.
### `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 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)`
- `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
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 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:
* Password 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.
@ -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 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.

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
* [Import External Users](./identity/import-external-users.md)
* [LDAP Login](./identity/idap.md)
* [LDAP Login](./identity/ldap.md)
* [OAuth Login](./identity/oauth-login.md)
* [Periodic Password Change (Password Aging)](./identity/periodic-password-change.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)
{
_ = Task.Run(async () =>
var telemetryTask = Task.Run(async () =>
{
using var scope = _serviceScopeFactory.CreateScope();
@ -81,6 +81,30 @@ public class TelemetryService : ITelemetryService, IScopedDependency
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;
}

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

@ -22,8 +22,6 @@
<ItemGroup>
<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>
</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.Interceptors;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.Sqlite;
@ -8,11 +10,31 @@ namespace Volo.Abp.EntityFrameworkCore.Sqlite;
)]
public class AbpEntityFrameworkCoreSqliteModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(options =>
{
options.BusyTimeout = 5000;
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpEfCoreGlobalFilterOptions>(options =>
{
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));
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)
@ -804,7 +815,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder,
mutableEntityType
);
entityTypeBuilder.ConfigureByConvention();
ConfigureGlobalFilters<TEntity>(modelBuilder, mutableEntityType, entityTypeBuilder);
@ -821,7 +832,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
protected virtual void ConfigureGlobalFilters<TEntity>(
ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType,
IMutableEntityType mutableEntityType,
EntityTypeBuilder<TEntity> entityTypeBuilder)
where TEntity : class
{
@ -852,7 +863,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{
return;
}
foreach (var property in mutableEntityType.GetProperties().
Where(property => property.PropertyInfo != null &&
@ -864,7 +875,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder,
mutableEntityType
);
entityTypeBuilder
.Property(property.Name)
.HasConversion(property.ClrType == typeof(DateTime)
@ -874,7 +885,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
protected virtual void ConfigureValueGenerated<TEntity>(
ModelBuilder modelBuilder,
ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType)
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, DbContextOptionsBuilder>? DefaultOnConfiguringAction { get; set; }
internal Dictionary<Type, List<object>> OnModelCreatingActions { get; }
internal Dictionary<Type, List<object>> OnConfiguringActions { get; }
public AbpDbContextOptions()
{
DefaultPreConfigureActions = new List<Action<AbpDbContextConfigurationContext>>();
@ -37,6 +41,7 @@ public class AbpDbContextOptions
DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>();
ConventionActions = new Dictionary<Type, List<object>>();
OnModelCreatingActions = new Dictionary<Type, List<object>>();
OnConfiguringActions = new Dictionary<Type, List<object>>();
}
public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action)
@ -53,11 +58,18 @@ public class AbpDbContextOptions
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));
DefaultConventionAction = action;
if (overrideExisting)
{
DefaultConventionAction = action;
}
else
{
DefaultConventionAction += action;
}
}
public void ConfigureConventions<TDbContext>([NotNull] Action<TDbContext, ModelConfigurationBuilder> action)
@ -78,11 +90,32 @@ public class AbpDbContextOptions
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));
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)
@ -103,6 +136,24 @@ public class AbpDbContextOptions
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()
{
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());
}
protected async override Task PublishToEventBusAsync(Type eventType, object eventData)
protected override async Task PublishToEventBusAsync(Type eventType, object eventData)
{
var headers = new Headers
{
@ -193,7 +193,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
}
public async override Task PublishFromOutboxAsync(
public override async Task PublishFromOutboxAsync(
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig)
{
@ -206,13 +206,18 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!));
}
await PublishAsync(
var result = await PublishAsync(
AbpKafkaEventBusOptions.TopicName,
outgoingEvent.EventName,
outgoingEvent.EventData,
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()))
{
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 outgoingEventArray = outgoingEvents.ToArray();
@ -242,7 +247,7 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
headers.Add(EventBusConsts.CorrelationIdHeaderName, System.Text.Encoding.UTF8.GetBytes(outgoingEvent.GetCorrelationId()!));
}
producer.Produce(
var result = await producer.ProduceAsync(
AbpKafkaEventBusOptions.TopicName,
new Message<string, byte[]>
{
@ -251,6 +256,11 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
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()))
{
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,
InboxConfig inboxConfig)
{
@ -290,12 +300,16 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
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 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(

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

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

@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@ -8,25 +10,31 @@ namespace Volo.Abp.MultiTenancy;
public class TenantResolver : ITenantResolver, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
private readonly AbpTenantResolveOptions _options;
public ILogger<TenantResolver> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
protected AbpTenantResolveOptions Options { get; }
public TenantResolver(IOptions<AbpTenantResolveOptions> options, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_options = options.Value;
Logger = NullLogger<TenantResolver>.Instance;
ServiceProvider = serviceProvider;
Options = options.Value;
}
public virtual async Task<TenantResolveResult> ResolveTenantIdOrNameAsync()
{
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);
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);
result.AppliedResolvers.Add(tenantResolver.Name);
@ -34,15 +42,21 @@ public class TenantResolver : ITenantResolver, ITransientDependency
if (context.HasResolvedTenantOrHost())
{
result.TenantIdOrName = context.TenantIdOrName;
Logger.LogDebug("Tenant resolved by '{TenantResolverName}' as '{TenantIdOrName}'.", tenantResolver.Name, result.TenantIdOrName ?? "Host");
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);
Logger.LogDebug("No tenant resolved. Using fallback tenant as '{FallbackTenant}'.", result.TenantIdOrName);
}
else if (result.TenantIdOrName.IsNullOrEmpty())
{
Logger.LogDebug("No tenant resolved.");
}
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)
{
TestEntityExtensionConfigurator.Configure();
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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;
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
_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)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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="..\Volo.Abp.PermissionManagement.Domain.Tests\Volo.Abp.PermissionManagement.Domain.Tests.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>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</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.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
using Microsoft.Data.Sqlite;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
[DependsOn(
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementTestBaseModule))]
typeof(AbpPermissionManagementTestBaseModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
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 =>
{
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();
}
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)
{
var task = context.ServiceProvider.GetRequiredService<AbpPermissionManagementDomainModule>().GetInitializeDynamicPermissionsTask();

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

@ -2,6 +2,7 @@
"packages":[
{
"name": "@abp/ng.setting-management",
"keepPackageInPackageJson": true,
"appRoutingModuleConfiguration":{
"routes":[
"{ 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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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 override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpSqliteOptions>(x => x.BusyTimeout = null);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
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