From f203825ab5ca9f7ac0334a848d0c488b136a6930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SAL=C4=B0H=20=C3=96ZKARA?= <58659931+salihozkara@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:49:29 +0300 Subject: [PATCH 1/4] Add extensibility for DbContext OnConfiguring actions Introduced DefaultOnConfiguringAction and OnConfiguringActions in AbpDbContextOptions to allow configuration of actions to be executed during DbContext.OnConfiguring. Updated AbpDbContext to invoke these actions, enabling more flexible and modular configuration of DbContext options. --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 6 ++++ .../AbpDbContextOptions.cs | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) 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 4b79bf017c..c773f527d6 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -117,6 +117,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { optionsBuilder.ConfigureWarnings(c => c.Ignore(RelationalEventId.PendingModelChangesWarning)); base.OnConfiguring(optionsBuilder); + + Options.Value.DefaultOnConfiguringAction?.Invoke(this, optionsBuilder); + foreach (var onConfiguringAction in Options.Value.OnConfiguringActions.GetOrDefault(typeof(TDbContext)) ?? []) + { + onConfiguringAction.As>().Invoke(this, optionsBuilder); + } } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs index acde97340b..a179bb3ee8 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs @@ -26,8 +26,12 @@ public class AbpDbContextOptions internal Dictionary> ConventionActions { get; } internal Action? DefaultOnModelCreatingAction { get; set; } + + internal Action? DefaultOnConfiguringAction { get; set; } internal Dictionary> OnModelCreatingActions { get; } + + internal Dictionary> OnConfiguringActions { get; } public AbpDbContextOptions() { @@ -37,6 +41,7 @@ public class AbpDbContextOptions DbContextReplacements = new Dictionary(); ConventionActions = new Dictionary>(); OnModelCreatingActions = new Dictionary>(); + OnConfiguringActions = new Dictionary>(); } public void PreConfigure([NotNull] Action action) @@ -84,6 +89,13 @@ public class AbpDbContextOptions DefaultOnModelCreatingAction = action; } + + public void ConfigureDefaultOnConfiguring([NotNull] Action action) + { + Check.NotNull(action, nameof(action)); + + DefaultOnConfiguringAction = action; + } public void ConfigureOnModelCreating([NotNull] Action action) where TDbContext : AbpDbContext @@ -102,6 +114,24 @@ public class AbpDbContextOptions actions.Add(action); } + + public void ConfigureOnConfiguring([NotNull] Action action) + where TDbContext : AbpDbContext + { + Check.NotNull(action, nameof(action)); + + var actions = OnConfiguringActions.GetOrDefault(typeof(TDbContext)); + if (actions == null) + { + OnConfiguringActions[typeof(TDbContext)] = new List + { + new Action((dbContext, builder) => action((TDbContext)dbContext, builder)) + }; + return; + } + + actions.Add(action); + } public bool IsConfiguredDefault() { From 8b4e12efa241d0a9c123066e4a011cc1c551a6f8 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 27 Oct 2025 21:11:59 +0800 Subject: [PATCH 2/4] Document OnConfiguring options in EF Core setup --- docs/en/framework/data/entity-framework-core/index.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/en/framework/data/entity-framework-core/index.md b/docs/en/framework/data/entity-framework-core/index.md index 208baeefda..418c040565 100644 --- a/docs/en/framework/data/entity-framework-core/index.md +++ b/docs/en/framework/data/entity-framework-core/index.md @@ -146,7 +146,7 @@ Configure(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(options => @@ -170,6 +170,15 @@ Configure(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((dbContext, optionsBuilder) => + { + // This action is called for OnConfiguring method of specific DbContext. + }); }); ```` From 6bb63e170a440b3c2ce75c44f7d50e26f9b2651f Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 28 Oct 2025 14:10:56 +0800 Subject: [PATCH 3/4] Add null checks in OnConfiguring of AbpDbContext --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 c773f527d6..9243d4680e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -117,7 +117,12 @@ public abstract class AbpDbContext : 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)) ?? []) { @@ -810,7 +815,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, modelBuilder, mutableEntityType ); - + entityTypeBuilder.ConfigureByConvention(); ConfigureGlobalFilters(modelBuilder, mutableEntityType, entityTypeBuilder); @@ -827,7 +832,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, protected virtual void ConfigureGlobalFilters( ModelBuilder modelBuilder, - IMutableEntityType mutableEntityType, + IMutableEntityType mutableEntityType, EntityTypeBuilder entityTypeBuilder) where TEntity : class { @@ -858,7 +863,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { return; } - + foreach (var property in mutableEntityType.GetProperties(). Where(property => property.PropertyInfo != null && @@ -870,7 +875,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, modelBuilder, mutableEntityType ); - + entityTypeBuilder .Property(property.Name) .HasConversion(property.ClrType == typeof(DateTime) @@ -880,7 +885,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, } protected virtual void ConfigureValueGenerated( - ModelBuilder modelBuilder, + ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) where TEntity : class { From 5caaa2b6f515c49f7f9aea5f19c3af06c9019a8d Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 5 Dec 2025 09:59:35 +0800 Subject: [PATCH 4/4] Add SQLite busy timeout support and interceptor --- .../EntityFrameworkCore/AbpSqliteOptions.cs | 6 +++ ...SqliteBusyTimeoutSaveChangesInterceptor.cs | 39 ++++++++++++++++++ .../AbpEntityFrameworkCoreSqliteModule.cs | 22 ++++++++++ .../AbpDbContextOptions.cs | 41 ++++++++++++++----- 4 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs new file mode 100644 index 0000000000..2d94b2864a --- /dev/null +++ b/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; } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs new file mode 100644 index 0000000000..32b4859bbb --- /dev/null +++ b/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; + +/// +/// https://github.com/dotnet/efcore/issues/29514 +/// +public class SqliteBusyTimeoutSaveChangesInterceptor : SaveChangesInterceptor +{ + private readonly string _pragmaCommand; + + public SqliteBusyTimeoutSaveChangesInterceptor(int timeoutMilliseconds) + { + _pragmaCommand = $"PRAGMA busy_timeout={timeoutMilliseconds};"; + } + + public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result) + { + if (eventData.Context != null) + { + eventData.Context.Database.ExecuteSqlRaw(_pragmaCommand); + } + + return result; + } + + public override async ValueTask> SavingChangesAsync(DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) + { + if (eventData.Context != null) + { + await eventData.Context.Database.ExecuteSqlRawAsync(_pragmaCommand, cancellationToken: cancellationToken); + } + + return await base.SavingChangesAsync(eventData, result, cancellationToken); + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs index 52415d73b1..6b767ceee4 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs +++ b/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(options => + { + options.BusyTimeout = 5000; + }); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options.UseDbFunction = true; }); + + var sqliteOptions = context.Services.ExecutePreConfiguredActions(); + if (sqliteOptions.BusyTimeout.HasValue) + { + Configure(options => + { + options.ConfigureDefaultOnConfiguring((dbContext, dbContextOptionsBuilder) => + { + dbContextOptionsBuilder.AddInterceptors(new SqliteBusyTimeoutSaveChangesInterceptor(sqliteOptions.BusyTimeout.Value)); + }, overrideExisting: false); + }); + } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs index a179bb3ee8..75cf409608 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs @@ -26,11 +26,11 @@ public class AbpDbContextOptions internal Dictionary> ConventionActions { get; } internal Action? DefaultOnModelCreatingAction { get; set; } - + internal Action? DefaultOnConfiguringAction { get; set; } internal Dictionary> OnModelCreatingActions { get; } - + internal Dictionary> OnConfiguringActions { get; } public AbpDbContextOptions() @@ -58,11 +58,18 @@ public class AbpDbContextOptions DefaultConfigureAction = action; } - public void ConfigureDefaultConvention([NotNull] Action action) + public void ConfigureDefaultConvention([NotNull] Action action, bool overrideExisting = false) { Check.NotNull(action, nameof(action)); - DefaultConventionAction = action; + if (overrideExisting) + { + DefaultConventionAction = action; + } + else + { + DefaultConventionAction += action; + } } public void ConfigureConventions([NotNull] Action action) @@ -83,18 +90,32 @@ public class AbpDbContextOptions actions.Add(action); } - public void ConfigureDefaultOnModelCreating([NotNull] Action action) + public void ConfigureDefaultOnModelCreating([NotNull] Action action, bool overrideExisting = false) { Check.NotNull(action, nameof(action)); - DefaultOnModelCreatingAction = action; + if (overrideExisting) + { + DefaultOnModelCreatingAction = action; + } + else + { + DefaultOnModelCreatingAction += action; + } } - - public void ConfigureDefaultOnConfiguring([NotNull] Action action) + + public void ConfigureDefaultOnConfiguring([NotNull] Action action, bool overrideExisting = false) { Check.NotNull(action, nameof(action)); - DefaultOnConfiguringAction = action; + if (overrideExisting) + { + DefaultOnConfiguringAction = action; + } + else + { + DefaultOnConfiguringAction += action; + } } public void ConfigureOnModelCreating([NotNull] Action action) @@ -114,7 +135,7 @@ public class AbpDbContextOptions actions.Add(action); } - + public void ConfigureOnConfiguring([NotNull] Action action) where TDbContext : AbpDbContext {