Browse Source

Merge pull request #24343 from abpframework/sqlite-busy_timeout

Add SQLite busy timeout support.
pull/24367/head
Yağmur Çelik 2 months ago
committed by GitHub
parent
commit
a3beca7664
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      docs/en/framework/data/entity-framework-core/index.md
  2. 6
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpSqliteOptions.cs
  3. 39
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Interceptors/SqliteBusyTimeoutSaveChangesInterceptor.cs
  4. 22
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs
  5. 21
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  6. 59
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

11
docs/en/framework/data/entity-framework-core/index.md

@ -146,7 +146,7 @@ Configure<AbpDbContextOptions>(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<AbpDbContextOptions>(options =>
@ -170,6 +170,15 @@ Configure<AbpDbContextOptions>(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<YourDbContext>((dbContext, optionsBuilder) =>
{
// This action is called for OnConfiguring method of specific DbContext.
});
});
````

6
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; }
}

39
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;
/// <summary>
/// https://github.com/dotnet/efcore/issues/29514
/// </summary>
public class SqliteBusyTimeoutSaveChangesInterceptor : SaveChangesInterceptor
{
private readonly string _pragmaCommand;
public SqliteBusyTimeoutSaveChangesInterceptor(int timeoutMilliseconds)
{
_pragmaCommand = $"PRAGMA busy_timeout={timeoutMilliseconds};";
}
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
if (eventData.Context != null)
{
eventData.Context.Database.ExecuteSqlRaw(_pragmaCommand);
}
return result;
}
public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
if (eventData.Context != null)
{
await eventData.Context.Database.ExecuteSqlRawAsync(_pragmaCommand, cancellationToken: cancellationToken);
}
return await base.SavingChangesAsync(eventData, result, cancellationToken);
}
}

22
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<AbpSqliteOptions>(options =>
{
options.BusyTimeout = 5000;
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpEfCoreGlobalFilterOptions>(options =>
{
options.UseDbFunction = true;
});
var sqliteOptions = context.Services.ExecutePreConfiguredActions<AbpSqliteOptions>();
if (sqliteOptions.BusyTimeout.HasValue)
{
Configure<AbpDbContextOptions>(options =>
{
options.ConfigureDefaultOnConfiguring((dbContext, dbContextOptionsBuilder) =>
{
dbContextOptionsBuilder.AddInterceptors(new SqliteBusyTimeoutSaveChangesInterceptor(sqliteOptions.BusyTimeout.Value));
}, overrideExisting: false);
});
}
}
}

21
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -117,6 +117,17 @@ public abstract class AbpDbContext<TDbContext> : 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)) ?? [])
{
onConfiguringAction.As<Action<DbContext, DbContextOptionsBuilder>>().Invoke(this, optionsBuilder);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
@ -804,7 +815,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder,
mutableEntityType
);
entityTypeBuilder.ConfigureByConvention();
ConfigureGlobalFilters<TEntity>(modelBuilder, mutableEntityType, entityTypeBuilder);
@ -821,7 +832,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
protected virtual void ConfigureGlobalFilters<TEntity>(
ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType,
IMutableEntityType mutableEntityType,
EntityTypeBuilder<TEntity> entityTypeBuilder)
where TEntity : class
{
@ -852,7 +863,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{
return;
}
foreach (var property in mutableEntityType.GetProperties().
Where(property => property.PropertyInfo != null &&
@ -864,7 +875,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
modelBuilder,
mutableEntityType
);
entityTypeBuilder
.Property(property.Name)
.HasConversion(property.ClrType == typeof(DateTime)
@ -874,7 +885,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
protected virtual void ConfigureValueGenerated<TEntity>(
ModelBuilder modelBuilder,
ModelBuilder modelBuilder,
IMutableEntityType mutableEntityType)
where TEntity : class
{

59
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

@ -27,8 +27,12 @@ public class AbpDbContextOptions
internal Action<DbContext, ModelBuilder>? DefaultOnModelCreatingAction { get; set; }
internal Action<DbContext, DbContextOptionsBuilder>? DefaultOnConfiguringAction { get; set; }
internal Dictionary<Type, List<object>> OnModelCreatingActions { get; }
internal Dictionary<Type, List<object>> OnConfiguringActions { get; }
public AbpDbContextOptions()
{
DefaultPreConfigureActions = new List<Action<AbpDbContextConfigurationContext>>();
@ -37,6 +41,7 @@ public class AbpDbContextOptions
DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>();
ConventionActions = new Dictionary<Type, List<object>>();
OnModelCreatingActions = new Dictionary<Type, List<object>>();
OnConfiguringActions = new Dictionary<Type, List<object>>();
}
public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action)
@ -53,11 +58,18 @@ public class AbpDbContextOptions
DefaultConfigureAction = action;
}
public void ConfigureDefaultConvention([NotNull] Action<DbContext, ModelConfigurationBuilder> action)
public void ConfigureDefaultConvention([NotNull] Action<DbContext, ModelConfigurationBuilder> action, bool overrideExisting = false)
{
Check.NotNull(action, nameof(action));
DefaultConventionAction = action;
if (overrideExisting)
{
DefaultConventionAction = action;
}
else
{
DefaultConventionAction += action;
}
}
public void ConfigureConventions<TDbContext>([NotNull] Action<TDbContext, ModelConfigurationBuilder> action)
@ -78,11 +90,32 @@ public class AbpDbContextOptions
actions.Add(action);
}
public void ConfigureDefaultOnModelCreating([NotNull] Action<DbContext, ModelBuilder> action)
public void ConfigureDefaultOnModelCreating([NotNull] Action<DbContext, ModelBuilder> action, bool overrideExisting = false)
{
Check.NotNull(action, nameof(action));
DefaultOnModelCreatingAction = action;
if (overrideExisting)
{
DefaultOnModelCreatingAction = action;
}
else
{
DefaultOnModelCreatingAction += action;
}
}
public void ConfigureDefaultOnConfiguring([NotNull] Action<DbContext, DbContextOptionsBuilder> action, bool overrideExisting = false)
{
Check.NotNull(action, nameof(action));
if (overrideExisting)
{
DefaultOnConfiguringAction = action;
}
else
{
DefaultOnConfiguringAction += action;
}
}
public void ConfigureOnModelCreating<TDbContext>([NotNull] Action<TDbContext, ModelBuilder> action)
@ -103,6 +136,24 @@ public class AbpDbContextOptions
actions.Add(action);
}
public void ConfigureOnConfiguring<TDbContext>([NotNull] Action<TDbContext, DbContextOptionsBuilder> action)
where TDbContext : AbpDbContext<TDbContext>
{
Check.NotNull(action, nameof(action));
var actions = OnConfiguringActions.GetOrDefault(typeof(TDbContext));
if (actions == null)
{
OnConfiguringActions[typeof(TDbContext)] = new List<object>
{
new Action<DbContext, DbContextOptionsBuilder>((dbContext, builder) => action((TDbContext)dbContext, builder))
};
return;
}
actions.Add(action);
}
public bool IsConfiguredDefault()
{
return DefaultConfigureAction != null;

Loading…
Cancel
Save