From 7819e602ff8edfd8fc01d1fff1ce94c5d1662c3b Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 20 Apr 2026 17:51:45 +0800 Subject: [PATCH] Detect EF Core database provider by keyword match - Extract provider name mapping into EfCoreDatabaseProviderHelper - Match provider names by Contains instead of exact switch, so newer assembly names (e.g. MySql.EntityFrameworkCore) are recognized without code changes - Add unit tests covering real provider assemblies and string fallbacks --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 24 +--- .../EfCoreDatabaseProviderHelper.cs | 52 ++++++++ .../Volo.Abp.EntityFrameworkCore.Tests.csproj | 6 + .../EfCoreDatabaseProviderHelper_Tests.cs | 111 ++++++++++++++++++ 4 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper.cs create mode 100644 framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreDatabaseProviderHelper_Tests.cs 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) + { + } + } +}