diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index 5b1a1fbeb2..985c1f7a64 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -9,7 +9,20 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. -## 2.2.6 (2026-04-08) Latest +## 2.2.7 (2026-04-20) Latest + +* Improved Blazor WebApp template setup for easier tiered application development +* Added application version tracking in analytics events +* Fixed issues in Basic Theme public website templates +* Improved PostgreSQL vector database support in templates +* Enhanced Blazor CRUD support with built-in Book management example +* Modernized React Native template components +* Updated to ABP 10.3 and Blazorise 2.0.4 +* Improved run profile and PowerShell execution reliability +* Added AI Management and Rate Limiting modules to available module options + + +## 2.2.6 (2026-04-08) - Disable Scriban 7.0 cumulative output limit for template rendering diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index a5a1e366f1..2dcdda868f 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -11,6 +11,7 @@ This document provides a general overview of the relationship between various ve | **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| +| 2.2.7 | 10.3.0 | | 2.2.5 - 2.2.6 | 10.2.0 | | 2.2.2 - 2.2.4 | 10.1.1 | | 2.2.1 | 10.1.0 | diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index dd2a9cfde7..553f90f859 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -197,29 +197,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, protected virtual EfCoreDatabaseProvider? GetDatabaseProviderOrNull(ModelBuilder modelBuilder) { - switch (Database.ProviderName) - { - case "Microsoft.EntityFrameworkCore.SqlServer": - return EfCoreDatabaseProvider.SqlServer; - case "Npgsql.EntityFrameworkCore.PostgreSQL": - return EfCoreDatabaseProvider.PostgreSql; - case "Pomelo.EntityFrameworkCore.MySql": - case "MySql.Data.MySqlClient": - return EfCoreDatabaseProvider.MySql; - case "Oracle.EntityFrameworkCore": - case "Devart.Data.Oracle.Entity.EFCore": - return EfCoreDatabaseProvider.Oracle; - case "Microsoft.EntityFrameworkCore.Sqlite": - return EfCoreDatabaseProvider.Sqlite; - case "Microsoft.EntityFrameworkCore.InMemory": - return EfCoreDatabaseProvider.InMemory; - case "FirebirdSql.EntityFrameworkCore.Firebird": - return EfCoreDatabaseProvider.Firebird; - case "Microsoft.EntityFrameworkCore.Cosmos": - return EfCoreDatabaseProvider.Cosmos; - default: - return null; - } + return EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(Database.ProviderName); } public async override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper.cs new file mode 100644 index 0000000000..6b3c228c62 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper.cs @@ -0,0 +1,52 @@ +using System; + +namespace Volo.Abp.EntityFrameworkCore; + +public static class EfCoreDatabaseProviderHelper +{ + public static EfCoreDatabaseProvider? GetDatabaseProviderOrNull(string? providerName) + { + if (providerName.IsNullOrWhiteSpace()) + { + return null; + } + + if (providerName.Contains("SqlServer", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.SqlServer; + } + if (providerName.Contains("Npgsql", StringComparison.OrdinalIgnoreCase) || + providerName.Contains("PostgreSQL", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.PostgreSql; + } + if (providerName.Contains("MySql", StringComparison.OrdinalIgnoreCase) || + providerName.Contains("Pomelo", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.MySql; + } + if (providerName.Contains("Oracle", StringComparison.OrdinalIgnoreCase) || + providerName.Contains("Devart", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.Oracle; + } + if (providerName.Contains("Sqlite", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.Sqlite; + } + if (providerName.Contains("InMemory", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.InMemory; + } + if (providerName.Contains("Firebird", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.Firebird; + } + if (providerName.Contains("Cosmos", StringComparison.OrdinalIgnoreCase)) + { + return EfCoreDatabaseProvider.Cosmos; + } + + return null; + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj index acbe19a353..77b1448ded 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj @@ -17,6 +17,12 @@ + + + + + + diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper_Tests.cs new file mode 100644 index 0000000000..f4faf5aacb --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper_Tests.cs @@ -0,0 +1,111 @@ +using Microsoft.EntityFrameworkCore; +using Shouldly; +using Xunit; + +namespace Volo.Abp.EntityFrameworkCore; + +public class EfCoreDatabaseProviderHelper_Tests +{ + [Fact] + public void Should_Detect_SqlServer_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.SqlServerDbContextOptionsExtensions.UseSqlServer(builder, "Server=localhost;Database=test"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.SqlServer); + } + + [Fact] + public void Should_Detect_PostgreSql_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.NpgsqlDbContextOptionsBuilderExtensions.UseNpgsql(builder, "Host=localhost;Database=test"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.PostgreSql); + } + + [Fact] + public void Should_Detect_MySql_Pomelo_From_Assembly_Name() + { + var providerName = typeof(Microsoft.EntityFrameworkCore.MySqlDbContextOptionsBuilderExtensions).Assembly.GetName().Name; + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(providerName) + .ShouldBe(EfCoreDatabaseProvider.MySql); + } + + [Fact] + public void Should_Detect_MySql_Oracle_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.MySQLDbContextOptionsExtensions.UseMySQL(builder, "Server=localhost;Database=test"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.MySql); + } + + [Fact] + public void Should_Detect_Oracle_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.OracleDbContextOptionsExtensions.UseOracle(builder, "Data Source=localhost/XE"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.Oracle); + } + + [Fact] + public void Should_Detect_Sqlite_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.SqliteDbContextOptionsBuilderExtensions.UseSqlite(builder, "Data Source=:memory:"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.Sqlite); + } + + [Fact] + public void Should_Detect_InMemory_From_Real_Assembly() + { + var builder = new DbContextOptionsBuilder(); + Microsoft.EntityFrameworkCore.InMemoryDbContextOptionsExtensions.UseInMemoryDatabase(builder, "test"); + using var context = new EmptyDbContext(builder.Options); + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(context.Database.ProviderName) + .ShouldBe(EfCoreDatabaseProvider.InMemory); + } + + [Theory] + [InlineData("Devart.Data.Oracle.Entity.EFCore", EfCoreDatabaseProvider.Oracle)] + [InlineData("FirebirdSql.EntityFrameworkCore.Firebird", EfCoreDatabaseProvider.Firebird)] + [InlineData("Microsoft.EntityFrameworkCore.Cosmos", EfCoreDatabaseProvider.Cosmos)] + public void Should_Detect_Providers_Without_Package_Reference(string providerName, EfCoreDatabaseProvider expected) + { + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(providerName).ShouldBe(expected); + } + + [Theory] + [InlineData("microsoft.entityframeworkcore.sqlserver", EfCoreDatabaseProvider.SqlServer)] + [InlineData("POMELO.ENTITYFRAMEWORKCORE.MYSQL", EfCoreDatabaseProvider.MySql)] + [InlineData("npgsql.entityframeworkcore.postgresql", EfCoreDatabaseProvider.PostgreSql)] + public void Should_Detect_Providers_Case_Insensitively(string providerName, EfCoreDatabaseProvider expected) + { + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(providerName).ShouldBe(expected); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("Some.Unknown.Provider")] + public void Should_Return_Null_For_Unknown_Or_Empty(string? providerName) + { + EfCoreDatabaseProviderHelper.GetDatabaseProviderOrNull(providerName).ShouldBeNull(); + } + + private class EmptyDbContext : DbContext + { + public EmptyDbContext(DbContextOptions options) : base(options) + { + } + } +}