Skip to content

Commit 6980069

Browse files
Merge pull request #187 from AntonioFalcao/feature/code-smell
Evolving DbContext dependency injection, adding sql serve execution s…
2 parents 3b34fda + d4d0c13 commit 6980069

14 files changed

Lines changed: 143 additions & 111 deletions

File tree

src/Dotnet6.GraphQL4.Repositories.Abstractions/DependencyInjection/Extensions/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Transactions;
22
using Dotnet6.GraphQL4.CrossCutting;
3+
using Dotnet6.GraphQL4.Repositories.Abstractions.DependencyInjection.Options;
34
using Dotnet6.GraphQL4.Repositories.Abstractions.UnitsOfWork;
45
using Microsoft.Extensions.Configuration;
56
using Microsoft.Extensions.DependencyInjection;

src/Dotnet6.GraphQL4.Repositories.Abstractions/DependencyInjection/ApplicationTransactionOptions.cs renamed to src/Dotnet6.GraphQL4.Repositories.Abstractions/DependencyInjection/Options/ApplicationTransactionOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Transactions;
22

3-
namespace Dotnet6.GraphQL4.Repositories.Abstractions.DependencyInjection
3+
namespace Dotnet6.GraphQL4.Repositories.Abstractions.DependencyInjection.Options
44
{
55
public class ApplicationTransactionOptions
66
{

src/Dotnet6.GraphQL4.Repositories.Abstractions/Transactions/Extensions/TransactionScopeExecuterExtensions.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace Dotnet6.GraphQL4.Repositories.Abstractions.Transactions.Extensions
6+
{
7+
public static class TransactionScopeExecutorExtensions
8+
{
9+
public static TransactionScopeExecutor<T> BeginTransactionScope<T>(this Func<T> operation)
10+
=> new(operation);
11+
12+
public static TransactionScopeExecutor<T> BeginTransactionScope<T>(this Func<CancellationToken, Task<T>> operationAsync)
13+
=> new(operationAsync);
14+
}
15+
}

src/Dotnet6.GraphQL4.Repositories.Abstractions/UnitsOfWork/UnitOfWork.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Threading.Tasks;
44
using System.Transactions;
55
using Dotnet6.GraphQL4.CrossCutting.Notifications;
6-
using Dotnet6.GraphQL4.Repositories.Abstractions.DependencyInjection;
6+
using Dotnet6.GraphQL4.Repositories.Abstractions.DependencyInjection.Options;
77
using Dotnet6.GraphQL4.Repositories.Abstractions.Transactions.Extensions;
88
using Microsoft.EntityFrameworkCore;
99
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -16,13 +16,16 @@ public class UnitOfWork : IUnitOfWork
1616
{
1717
private readonly DatabaseFacade _database;
1818
private readonly DbContext _dbContext;
19-
private readonly IOptionsMonitor<ApplicationTransactionOptions> _options;
19+
private readonly ApplicationTransactionOptions _options;
2020
private readonly INotificationContext _notificationContext;
2121

22-
public UnitOfWork(DbContext dbContext, IOptionsMonitor<ApplicationTransactionOptions> options, INotificationContext notificationContext)
22+
public UnitOfWork(
23+
DbContext dbContext,
24+
IOptionsMonitor<ApplicationTransactionOptions> optionsMonitor,
25+
INotificationContext notificationContext)
2326
{
2427
_dbContext = dbContext;
25-
_options = options;
28+
_options = optionsMonitor.CurrentValue;
2629
_database = _dbContext.Database;
2730
_notificationContext = notificationContext;
2831
}
@@ -35,18 +38,18 @@ public Task<TResult> ExecuteInTransactionAsync<TResult>(Func<CancellationToken,
3538

3639
private Task<TResult> ExecuteWithScopeAsync<TResult>(Func<CancellationToken, Task<TResult>> operationAsync, Func<CancellationToken, Task<bool>> condition, CancellationToken cancellationToken)
3740
=> operationAsync
38-
.TransactionAsync()
41+
.BeginTransactionScope()
3942
.WithScopeOption(TransactionScopeOption.Required)
40-
.WithOptions(options => options.IsolationLevel = _options.CurrentValue.IsolationLevel)
43+
.WithOptions(options => options.IsolationLevel = _options.IsolationLevel)
4144
.WithScopeAsyncFlowOption(TransactionScopeAsyncFlowOption.Enabled)
4245
.WithConditionAsync(condition ?? (_ => _notificationContext.AllValidAsync))
4346
.ExecuteAsync(cancellationToken);
4447

4548
private TResult ExecuteWithScope<TResult>(Func<TResult> operation, Func<bool> condition)
4649
=> operation
47-
.Transaction()
50+
.BeginTransactionScope()
4851
.WithScopeOption(TransactionScopeOption.Required)
49-
.WithOptions(options => options.IsolationLevel = _options.CurrentValue.IsolationLevel)
52+
.WithOptions(options => options.IsolationLevel = _options.IsolationLevel)
5053
.WithScopeAsyncFlowOption(TransactionScopeAsyncFlowOption.Enabled)
5154
.WithCondition(condition ?? (() => _notificationContext.AllValid))
5255
.Execute();
@@ -55,9 +58,9 @@ private IExecutionStrategy CreateExecutionStrategy()
5558
=> _database.CreateExecutionStrategy();
5659

5760
public bool SaveChanges()
58-
=> _dbContext.SaveChanges(true) > default(int);
61+
=> _dbContext.SaveChanges(false) > default(int);
5962

6063
public async Task<bool> SaveChangesAsync(CancellationToken cancellationToken)
61-
=> await _dbContext.SaveChangesAsync(true, cancellationToken) > default(int);
64+
=> await _dbContext.SaveChangesAsync(false, cancellationToken) > default(int);
6265
}
6366
}

src/Dotnet6.GraphQL4.Store.Repositories/Contexts/StoreDbContext.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,33 @@
11
using Dotnet6.GraphQL4.Store.Domain.Entities.Products;
22
using Dotnet6.GraphQL4.Store.Domain.Entities.Reviews;
33
using Dotnet6.GraphQL4.Store.Domain.ValueObjects.ProductTypes;
4+
using Dotnet6.GraphQL4.Store.Repositories.DependencyInjection.Options;
45
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.EntityFrameworkCore.Infrastructure;
7+
using Microsoft.Extensions.Configuration;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Options;
510

611
namespace Dotnet6.GraphQL4.Store.Repositories.Contexts
712
{
813
public class StoreDbContext : DbContext
914
{
1015
private const string SqlLatin1GeneralCp1CsAs = "SQL_Latin1_General_CP1_CS_AS";
16+
private readonly IConfiguration _configuration;
17+
private readonly ILoggerFactory _loggerFactory;
18+
private readonly SqlServerRetryingOptions _options;
1119

12-
public StoreDbContext(DbContextOptions options)
13-
: base(options) { }
20+
public StoreDbContext(
21+
DbContextOptions options,
22+
ILoggerFactory loggerFactory,
23+
IConfiguration configuration,
24+
IOptionsMonitor<SqlServerRetryingOptions> optionsMonitor)
25+
: base(options)
26+
{
27+
_loggerFactory = loggerFactory;
28+
_configuration = configuration;
29+
_options = optionsMonitor.CurrentValue;
30+
}
1431

1532
public DbSet<Product> Products { get; set; }
1633
public DbSet<ProductType> ProductTypes { get; set; }
@@ -23,5 +40,27 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
2340
modelBuilder.Seed();
2441
base.OnModelCreating(modelBuilder);
2542
}
43+
44+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
45+
{
46+
if (optionsBuilder.IsConfigured) return;
47+
48+
optionsBuilder.EnableDetailedErrors()
49+
.EnableSensitiveDataLogging()
50+
.UseSqlServer(
51+
connectionString:_configuration.GetConnectionString("DefaultConnection"),
52+
sqlServerOptionsAction: SqlServerOptionsAction)
53+
.UseLoggerFactory(_loggerFactory);
54+
}
55+
56+
private void SqlServerOptionsAction(SqlServerDbContextOptionsBuilder optionsBuilder)
57+
=> optionsBuilder
58+
.ExecutionStrategy(
59+
dependencies => new SqlServerRetryingExecutionStrategy(
60+
dependencies: dependencies,
61+
maxRetryCount: _options.MaxRetryCount,
62+
maxRetryDelay: _options.MaxRetryDelay,
63+
errorNumbersToAdd: _options.ErrorNumbersToAdd))
64+
.MigrationsAssembly(assemblyName: typeof(StoreDbContext).Assembly.GetName().Name);
2665
}
2766
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Dotnet6.GraphQL4.Store.Repositories.Contexts;
2+
using Dotnet6.GraphQL4.Store.Repositories.DependencyInjection.Options;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Options;
7+
8+
namespace Dotnet6.GraphQL4.Store.Repositories.DependencyInjection.Extensions
9+
{
10+
public static class ServiceCollectionExtensions
11+
{
12+
public static IServiceCollection AddApplicationDbContext(this IServiceCollection services)
13+
=> services
14+
.AddScoped<DbContext, StoreDbContext>()
15+
.AddDbContext<StoreDbContext>();
16+
17+
public static OptionsBuilder<SqlServerRetryingOptions> ConfigureSqlServerRetryingOptions(this IServiceCollection services, IConfigurationSection section)
18+
=> services
19+
.AddOptions<SqlServerRetryingOptions>()
20+
.Bind(section)
21+
.Validate(
22+
validation: options => options.MaxRetryCount <= 10 || options.MaxSecondsRetryDelay <= 10,
23+
failureMessage: "Max value for Retry or Delay must be 10.");
24+
}
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
3+
namespace Dotnet6.GraphQL4.Store.Repositories.DependencyInjection.Options
4+
{
5+
public class SqlServerRetryingOptions
6+
{
7+
private const int DefaultRetryCount = 5;
8+
private const int DefaultRetryDelay = 5;
9+
10+
private int _maxRetryCount;
11+
private int _maxSecondsRetryDelay;
12+
13+
public int MaxRetryCount
14+
{
15+
get => _maxRetryCount <= 0 ? DefaultRetryCount : _maxRetryCount;
16+
set => _maxRetryCount = value;
17+
}
18+
19+
public int MaxSecondsRetryDelay
20+
{
21+
get => _maxSecondsRetryDelay <= 0 ? DefaultRetryDelay : _maxSecondsRetryDelay;
22+
set => _maxSecondsRetryDelay = value;
23+
}
24+
25+
public int[] ErrorNumbersToAdd { get; set; }
26+
27+
internal TimeSpan MaxRetryDelay
28+
=> TimeSpan.FromSeconds(MaxSecondsRetryDelay);
29+
}
30+
}

src/Dotnet6.GraphQL4.Store.Repositories/Dotnet6.GraphQL4.Store.Repositories.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<ItemGroup>
44
<PackageReference Include="Bogus" Version="33.0.2" />
5+
<PackageReference Include="EntityFramework" Version="$(Microsoft_EntityFrameworkCore_Version)" />
56
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(Microsoft_EntityFrameworkCore_Version)" />
67
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="$(Microsoft_EntityFrameworkCore_Version)" />
78
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(Microsoft_EntityFrameworkCore_Version)" />

src/Dotnet6.GraphQL4.Store.Repositories/Extensions/DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)