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 {