From e5df65b81ab9bf4c6f91ebaf61cccbd1f2a60043 Mon Sep 17 00:00:00 2001 From: Artem Dudarev Date: Thu, 12 Sep 2024 17:49:32 +0200 Subject: [PATCH] VCST-1752: Fix performance degradation on SQL Server (#2834) --- Directory.Build.props | 1 + .../DbContextOptionsBuilderExtensions.cs | 15 --------- .../DbContextOptionsBuilderExtensions.cs | 27 ++++++++++++++++ .../MySqlDataAssemblyMarker.cs | 5 +++ .../MySqlDbContextFactory.cs | 9 ++---- .../DbContextOptionsBuilderExtensions.cs | 14 -------- .../DbContextOptionsBuilderExtensions.cs | 26 +++++++++++++++ .../PostgreSqlDataAssemblyMarker.cs | 5 +++ .../PostgreSqlDbContextFactory.cs | 9 ++---- .../DbContextOptionsBuilderExtensions.cs | 16 ---------- .../DbContextOptionsBuilderExtensions.cs | 32 +++++++++++++++++++ .../SqlServerDataAssemblyMarker.cs | 5 +++ .../SqlServerDbContextFactory.cs | 4 +-- src/VirtoCommerce.Platform.Web/Startup.cs | 16 ++++++---- .../appsettings.json | 5 +++ 15 files changed, 121 insertions(+), 68 deletions(-) delete mode 100644 src/VirtoCommerce.Platform.Data.MySql/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.MySql/Extensions/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.MySql/MySqlDataAssemblyMarker.cs delete mode 100644 src/VirtoCommerce.Platform.Data.PostgreSql/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.PostgreSql/Extensions/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDataAssemblyMarker.cs delete mode 100644 src/VirtoCommerce.Platform.Data.SqlServer/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.SqlServer/Extensions/DbContextOptionsBuilderExtensions.cs create mode 100644 src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDataAssemblyMarker.cs diff --git a/Directory.Build.props b/Directory.Build.props index 94c0d8eadbf..a4c128fcdfd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,6 +17,7 @@ true + NU5048 diff --git a/src/VirtoCommerce.Platform.Data.MySql/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.MySql/DbContextOptionsBuilderExtensions.cs deleted file mode 100644 index aa12fe48282..00000000000 --- a/src/VirtoCommerce.Platform.Data.MySql/DbContextOptionsBuilderExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using VirtoCommerce.Platform.Data.Repositories; - -namespace VirtoCommerce.Platform.Data.MySql -{ - public static class DbContextOptionsBuilderExtensions - { - /// - /// Configures the context to use PostgreSql. - /// - public static DbContextOptionsBuilder UseMySqlDatabase(this DbContextOptionsBuilder builder, string connectionString) => - builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), db => db - .MigrationsAssembly(typeof(MySqlDbContextFactory).Assembly.GetName().Name)); - } -} diff --git a/src/VirtoCommerce.Platform.Data.MySql/Extensions/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.MySql/Extensions/DbContextOptionsBuilderExtensions.cs new file mode 100644 index 00000000000..98a12d67b20 --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.MySql/Extensions/DbContextOptionsBuilderExtensions.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; + +namespace VirtoCommerce.Platform.Data.MySql.Extensions; + +public static class DbContextOptionsBuilderExtensions +{ + /// + /// Configures the context to use MySql. + /// + public static DbContextOptionsBuilder UseMySqlDatabase( + this DbContextOptionsBuilder optionsBuilder, + string? connectionString, + Type migrationsAssemblyMarkerType, + IConfiguration configuration, + Action? mySqlOptionsAction = null) + { + return optionsBuilder.UseMySql(connectionString, + ServerVersion.AutoDetect(connectionString), + mySqlDbContextOptionsBuilder => + { + mySqlDbContextOptionsBuilder.MigrationsAssembly(migrationsAssemblyMarkerType.Assembly.GetName().Name); + mySqlOptionsAction?.Invoke(mySqlDbContextOptionsBuilder, configuration); + }); + } +} diff --git a/src/VirtoCommerce.Platform.Data.MySql/MySqlDataAssemblyMarker.cs b/src/VirtoCommerce.Platform.Data.MySql/MySqlDataAssemblyMarker.cs new file mode 100644 index 00000000000..7162cf76a8b --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.MySql/MySqlDataAssemblyMarker.cs @@ -0,0 +1,5 @@ +namespace VirtoCommerce.Platform.Data.MySql; + +public class MySqlDataAssemblyMarker +{ +} diff --git a/src/VirtoCommerce.Platform.Data.MySql/MySqlDbContextFactory.cs b/src/VirtoCommerce.Platform.Data.MySql/MySqlDbContextFactory.cs index a00ba8d7c0a..a5bfed2ad7f 100644 --- a/src/VirtoCommerce.Platform.Data.MySql/MySqlDbContextFactory.cs +++ b/src/VirtoCommerce.Platform.Data.MySql/MySqlDbContextFactory.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using VirtoCommerce.Platform.Data.Repositories; @@ -23,7 +18,7 @@ PlatformDbContext IDesignTimeDbContextFactory.CreateDbContext connectionString, ResolveServerVersion(serverVersion, connectionString), db => db - .MigrationsAssembly(typeof(MySqlDbContextFactory).Assembly.GetName().Name)); + .MigrationsAssembly(typeof(MySqlDataAssemblyMarker).Assembly.GetName().Name)); return new PlatformDbContext(builder.Options); } @@ -38,7 +33,7 @@ SecurityDbContext IDesignTimeDbContextFactory.CreateDbContext connectionString, ResolveServerVersion(serverVersion, connectionString), db => db - .MigrationsAssembly(typeof(MySqlDbContextFactory).Assembly.GetName().Name)); + .MigrationsAssembly(typeof(MySqlDataAssemblyMarker).Assembly.GetName().Name)); builder.UseOpenIddict(); diff --git a/src/VirtoCommerce.Platform.Data.PostgreSql/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.PostgreSql/DbContextOptionsBuilderExtensions.cs deleted file mode 100644 index bd04a5ed286..00000000000 --- a/src/VirtoCommerce.Platform.Data.PostgreSql/DbContextOptionsBuilderExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace VirtoCommerce.Platform.Data.PostgreSql -{ - public static class DbContextOptionsBuilderExtensions - { - /// - /// Configures the context to use PostgreSql. - /// - public static DbContextOptionsBuilder UsePostgreSqlDatabase(this DbContextOptionsBuilder builder, string connectionString) => - builder.UseNpgsql(connectionString, db => db - .MigrationsAssembly(typeof(PostgreSqlDbContextFactory).Assembly.GetName().Name)); - } -} diff --git a/src/VirtoCommerce.Platform.Data.PostgreSql/Extensions/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.PostgreSql/Extensions/DbContextOptionsBuilderExtensions.cs new file mode 100644 index 00000000000..ee8c4f3cc46 --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.PostgreSql/Extensions/DbContextOptionsBuilderExtensions.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; + +namespace VirtoCommerce.Platform.Data.PostgreSql.Extensions; + +public static class DbContextOptionsBuilderExtensions +{ + /// + /// Configures the context to use PostgreSql. + /// + public static DbContextOptionsBuilder UsePostgreSqlDatabase( + this DbContextOptionsBuilder optionsBuilder, + string? connectionString, + Type migrationsAssemblyMarkerType, + IConfiguration configuration, + Action? npgsqlOptionsAction = null) + { + return optionsBuilder.UseNpgsql(connectionString, + npgsqlDbContextOptionsBuilder => + { + npgsqlDbContextOptionsBuilder.MigrationsAssembly(migrationsAssemblyMarkerType.Assembly.GetName().Name); + npgsqlOptionsAction?.Invoke(npgsqlDbContextOptionsBuilder, configuration); + }); + } +} diff --git a/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDataAssemblyMarker.cs b/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDataAssemblyMarker.cs new file mode 100644 index 00000000000..e09ff829724 --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDataAssemblyMarker.cs @@ -0,0 +1,5 @@ +namespace VirtoCommerce.Platform.Data.PostgreSql; + +public class PostgreSqlDataAssemblyMarker +{ +} diff --git a/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDbContextFactory.cs b/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDbContextFactory.cs index 09b34cd67f7..678452c3787 100644 --- a/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDbContextFactory.cs +++ b/src/VirtoCommerce.Platform.Data.PostgreSql/PostgreSqlDbContextFactory.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using VirtoCommerce.Platform.Data.Repositories; @@ -20,7 +15,7 @@ PlatformDbContext IDesignTimeDbContextFactory.CreateDbContext builder.UseNpgsql( connectionString, - db => db.MigrationsAssembly(typeof(PostgreSqlDbContextFactory).Assembly.GetName().Name)); + db => db.MigrationsAssembly(typeof(PostgreSqlDataAssemblyMarker).Assembly.GetName().Name)); return new PlatformDbContext(builder.Options); } @@ -32,7 +27,7 @@ SecurityDbContext IDesignTimeDbContextFactory.CreateDbContext builder.UseNpgsql( connectionString, - db => db.MigrationsAssembly(typeof(PostgreSqlDbContextFactory).Assembly.GetName().Name)); + db => db.MigrationsAssembly(typeof(PostgreSqlDataAssemblyMarker).Assembly.GetName().Name)); builder.UseOpenIddict(); diff --git a/src/VirtoCommerce.Platform.Data.SqlServer/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.SqlServer/DbContextOptionsBuilderExtensions.cs deleted file mode 100644 index 7bcc7ccea9c..00000000000 --- a/src/VirtoCommerce.Platform.Data.SqlServer/DbContextOptionsBuilderExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace VirtoCommerce.Platform.Data.SqlServer -{ - public static class DbContextOptionsBuilderExtensions - { - /// - /// Configures the context to use SqlServer. - /// - public static DbContextOptionsBuilder UseSqlServerDatabase(this DbContextOptionsBuilder builder, string connectionString) - { - return builder.UseSqlServer(connectionString, db => db - .MigrationsAssembly(typeof(SqlServerDbContextFactory).Assembly.GetName().Name)); - } - } -} diff --git a/src/VirtoCommerce.Platform.Data.SqlServer/Extensions/DbContextOptionsBuilderExtensions.cs b/src/VirtoCommerce.Platform.Data.SqlServer/Extensions/DbContextOptionsBuilderExtensions.cs new file mode 100644 index 00000000000..7d59c52cc92 --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.SqlServer/Extensions/DbContextOptionsBuilderExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; + +namespace VirtoCommerce.Platform.Data.SqlServer.Extensions; + +public static class DbContextOptionsBuilderExtensions +{ + /// + /// Configures the context to use SqlServer. + /// + public static DbContextOptionsBuilder UseSqlServerDatabase( + this DbContextOptionsBuilder optionsBuilder, + string? connectionString, + Type migrationsAssemblyMarkerType, + IConfiguration configuration, + Action? sqlServerOptionsAction = null) + { + return optionsBuilder.UseSqlServer(connectionString, + sqlServerOptionsBuilder => + { + var compatibilityLevel = configuration.GetValue("SqlServer:CompatibilityLevel", null); + if (compatibilityLevel != null) + { + sqlServerOptionsBuilder.UseCompatibilityLevel(compatibilityLevel.Value); + } + + sqlServerOptionsBuilder.MigrationsAssembly(migrationsAssemblyMarkerType.Assembly.GetName().Name); + sqlServerOptionsAction?.Invoke(sqlServerOptionsBuilder, configuration); + }); + } +} diff --git a/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDataAssemblyMarker.cs b/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDataAssemblyMarker.cs new file mode 100644 index 00000000000..8580eb6a8fc --- /dev/null +++ b/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDataAssemblyMarker.cs @@ -0,0 +1,5 @@ +namespace VirtoCommerce.Platform.Data.SqlServer; + +public class SqlServerDataAssemblyMarker +{ +} diff --git a/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDbContextFactory.cs b/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDbContextFactory.cs index 9759fe13769..47f7e4abf06 100644 --- a/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDbContextFactory.cs +++ b/src/VirtoCommerce.Platform.Data.SqlServer/SqlServerDbContextFactory.cs @@ -15,7 +15,7 @@ PlatformDbContext IDesignTimeDbContextFactory.CreateDbContext builder.UseSqlServer( connectionString, - db => db.MigrationsAssembly(typeof(SqlServerDbContextFactory).Assembly.GetName().Name)); + db => db.MigrationsAssembly(typeof(SqlServerDataAssemblyMarker).Assembly.GetName().Name)); return new PlatformDbContext(builder.Options); } @@ -27,7 +27,7 @@ SecurityDbContext IDesignTimeDbContextFactory.CreateDbContext builder.UseSqlServer( connectionString, - db => db.MigrationsAssembly(typeof(SqlServerDbContextFactory).Assembly.GetName().Name)); + db => db.MigrationsAssembly(typeof(SqlServerDataAssemblyMarker).Assembly.GetName().Name)); builder.UseOpenIddict(); diff --git a/src/VirtoCommerce.Platform.Web/Startup.cs b/src/VirtoCommerce.Platform.Web/Startup.cs index fa843f76b35..c0ebf2a50c3 100644 --- a/src/VirtoCommerce.Platform.Web/Startup.cs +++ b/src/VirtoCommerce.Platform.Web/Startup.cs @@ -46,11 +46,14 @@ using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.Platform.Data.Extensions; using VirtoCommerce.Platform.Data.MySql; +using VirtoCommerce.Platform.Data.MySql.Extensions; using VirtoCommerce.Platform.Data.MySql.HealthCheck; using VirtoCommerce.Platform.Data.PostgreSql; +using VirtoCommerce.Platform.Data.PostgreSql.Extensions; using VirtoCommerce.Platform.Data.PostgreSql.HealthCheck; using VirtoCommerce.Platform.Data.Repositories; using VirtoCommerce.Platform.Data.SqlServer; +using VirtoCommerce.Platform.Data.SqlServer.Extensions; using VirtoCommerce.Platform.Data.SqlServer.HealthCheck; using VirtoCommerce.Platform.DistributedLock; using VirtoCommerce.Platform.Hangfire.Extensions; @@ -133,19 +136,18 @@ public void ConfigureServices(IServiceCollection services) services.AddDbContext((provider, options) => { - var databaseProvider = Configuration.GetValue("DatabaseProvider", "SqlServer"); var connectionString = Configuration.GetConnectionString("VirtoCommerce"); switch (databaseProvider) { case "MySql": - options.UseMySqlDatabase(connectionString); + options.UseMySqlDatabase(connectionString, typeof(MySqlDataAssemblyMarker), Configuration); break; case "PostgreSql": - options.UsePostgreSqlDatabase(connectionString); + options.UsePostgreSqlDatabase(connectionString, typeof(PostgreSqlDataAssemblyMarker), Configuration); break; default: - options.UseSqlServerDatabase(connectionString); + options.UseSqlServerDatabase(connectionString, typeof(SqlServerDataAssemblyMarker), Configuration); break; } }); @@ -203,13 +205,13 @@ public void ConfigureServices(IServiceCollection services) switch (databaseProvider) { case "MySql": - options.UseMySqlDatabase(connectionString); + options.UseMySqlDatabase(connectionString, typeof(MySqlDataAssemblyMarker), Configuration); break; case "PostgreSql": - options.UsePostgreSqlDatabase(connectionString); + options.UsePostgreSqlDatabase(connectionString, typeof(PostgreSqlDataAssemblyMarker), Configuration); break; default: - options.UseSqlServerDatabase(connectionString); + options.UseSqlServerDatabase(connectionString, typeof(SqlServerDataAssemblyMarker), Configuration); break; } diff --git a/src/VirtoCommerce.Platform.Web/appsettings.json b/src/VirtoCommerce.Platform.Web/appsettings.json index d43d1abdc76..bdb19b6529f 100644 --- a/src/VirtoCommerce.Platform.Web/appsettings.json +++ b/src/VirtoCommerce.Platform.Web/appsettings.json @@ -5,6 +5,11 @@ "VirtoCommerce": "Data Source=(local);Initial Catalog=VirtoCommerce3.net8;Persist Security Info=True;User ID=virto;Password=virto;Connect Timeout=30;TrustServerCertificate=True;" //"RedisConnectionString": "127.0.0.1:6379,ssl=False" }, + "SqlServer": { + // Set compatibility level to 120 (SQL Server 2014) to prevent the use of OPENJSON, which has poor performance. + // https://github.com/dotnet/efcore/issues/32394 + // "CompatibilityLevel": 120 + }, "Serilog": { "Using": [ "Serilog.Sinks.Console",