Skip to content

Commit 5a4654c

Browse files
authored
Merge pull request #117 from koenbeuk/trigger-service-configuration
Trigger service configuration
2 parents a59eaf7 + bf85a64 commit 5a4654c

16 files changed

Lines changed: 297 additions & 23 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
6+
7+
using System.ComponentModel;
8+
9+
// ReSharper disable once CheckNamespace
10+
namespace System.Runtime.CompilerServices
11+
{
12+
/// <summary>
13+
/// Reserved to be used by the compiler for tracking metadata.
14+
/// This class should not be used by developers in source code.
15+
/// </summary>
16+
[EditorBrowsable(EditorBrowsableState.Never)]
17+
internal static class IsExternalInit
18+
{
19+
}
20+
}
21+
22+
#endif
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace EntityFrameworkCore.Triggered
8+
{
9+
public record TriggerConfiguration
10+
{
11+
public TriggerConfiguration(bool disabled, int maxCascadeCycles)
12+
{
13+
Disabled = disabled;
14+
MaxCascadeCycles = maxCascadeCycles;
15+
}
16+
17+
public bool Disabled { get; init; }
18+
19+
public int MaxCascadeCycles { get; init; }
20+
}
21+
}
Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,80 @@
11
using System;
2+
using System.Threading.Tasks;
3+
using System.Threading;
24
using Microsoft.EntityFrameworkCore;
35
using Microsoft.EntityFrameworkCore.Infrastructure;
46

57
namespace EntityFrameworkCore.Triggered.Extensions
68
{
79
public static class DbContextExtensions
810
{
11+
/// <summary>
12+
/// Gets the <c>ITriggerService</c> that is used to create trigger sessions.Allows for just in time configuration
13+
/// </summary>
14+
public static ITriggerService GetTriggerService(this DbContext dbContext)
15+
{
16+
if (dbContext is null)
17+
{
18+
throw new ArgumentNullException(nameof(dbContext));
19+
}
20+
21+
return dbContext.GetService<ITriggerService>() ?? throw new InvalidOperationException("Triggers are not configured for this DbContext");
22+
}
23+
24+
/// <summary>
25+
/// Gets or creates a <c>ITriggerSession</c> that can be used to manually invoke triggers
26+
/// </summary>
927
public static ITriggerSession CreateTriggerSession(this DbContext dbContext, IServiceProvider? serviceProvider = null)
1028
{
11-
var triggerService = dbContext.GetService<ITriggerService>() ?? throw new InvalidOperationException("Trigger service infrastructure is not configured");
29+
var triggerService = GetTriggerService(dbContext);
1230

1331
return triggerService.CreateSession(dbContext, serviceProvider);
1432
}
33+
34+
/// <summary>
35+
/// Calls dbContext.SaveChanges without invoking triggers
36+
/// </summary>
37+
public static int SaveChangesWithoutTriggers(this DbContext dbContext, bool acceptAllChangesOnSuccess = true)
38+
{
39+
var triggerService = GetTriggerService(dbContext);
40+
var initialConfiguration = triggerService.Configuration;
41+
42+
try
43+
{
44+
triggerService.Configuration = initialConfiguration with { Disabled = true };
45+
46+
return dbContext.SaveChanges(acceptAllChangesOnSuccess);
47+
}
48+
finally
49+
{
50+
triggerService.Configuration = initialConfiguration;
51+
}
52+
}
53+
54+
/// <summary>
55+
/// Calls dbContext.SaveChanges without invoking triggers
56+
/// </summary>
57+
public static Task<int> SaveChangesWithoutTriggersAsync(this DbContext dbContext, CancellationToken cancellationToken = default)
58+
=> SaveChangesWithoutTriggersAsync(dbContext, true, cancellationToken);
59+
60+
/// <summary>
61+
/// Calls dbContext.SaveChanges without invoking triggers
62+
/// </summary>
63+
public static Task<int> SaveChangesWithoutTriggersAsync(this DbContext dbContext, bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
64+
{
65+
var triggerService = GetTriggerService(dbContext);
66+
var initialConfiguration = triggerService.Configuration;
67+
68+
try
69+
{
70+
triggerService.Configuration = initialConfiguration with { Disabled = true };
71+
72+
return dbContext.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
73+
}
74+
finally
75+
{
76+
triggerService.Configuration = initialConfiguration;
77+
}
78+
}
1579
}
1680
}

src/EntityFrameworkCore.Triggered/ITriggerService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace EntityFrameworkCore.Triggered
55
{
66
public interface ITriggerService
77
{
8+
TriggerConfiguration Configuration { get; set; }
9+
810
ITriggerSession CreateSession(DbContext context, IServiceProvider? serviceProvider = null);
911

1012
ITriggerSession? Current { get; set; }

src/EntityFrameworkCore.Triggered/Internal/CascadingTriggerContextDiscoveryStrategy.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ public CascadingTriggerContextDiscoveryStrategy(string name, bool skipDetectedCh
2626
_skipDetectedChanges = skipDetectedChanges;
2727
}
2828

29-
public IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerOptions options, TriggerContextTracker tracker, ILogger logger)
29+
public IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerConfiguration configuration, TriggerContextTracker tracker, ILogger logger)
3030
{
31-
var maxCascadingCycles = options.MaxCascadeCycles;
31+
var maxCascadingCycles = configuration.MaxCascadeCycles;
3232
_discoveryStarted(logger, _name, maxCascadingCycles, null);
3333

3434
var iteration = 0;

src/EntityFrameworkCore.Triggered/Internal/ITriggerContextDiscoveryStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ namespace EntityFrameworkCore.Triggered.Internal
55
{
66
public interface ITriggerContextDiscoveryStrategy
77
{
8-
IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerOptions options, TriggerContextTracker tracker, ILogger logger);
8+
IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerConfiguration configuration, TriggerContextTracker tracker, ILogger logger);
99
}
1010
}

src/EntityFrameworkCore.Triggered/Internal/NonCascadingTriggerContextDiscoveryStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public NonCascadingTriggerContextDiscoveryStrategy(string name)
1919
_name = name ?? throw new ArgumentNullException(nameof(name));
2020
}
2121

22-
public IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerOptions options, TriggerContextTracker tracker, ILogger logger)
22+
public IEnumerable<IEnumerable<TriggerContextDescriptor>> Discover(TriggerConfiguration configuration, TriggerContextTracker tracker, ILogger logger)
2323
{
2424
var changes = tracker.DiscoveredChanges ?? throw new InvalidOperationException("Trigger discovery process has not yet started. Please ensure that TriggerSession.DiscoverChanges() or TriggerSession.RaiseBeforeSaveTriggers() has been called");
2525

src/EntityFrameworkCore.Triggered/TriggerService.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ namespace EntityFrameworkCore.Triggered
1212
{
1313
public class TriggerService : ITriggerService, IResettableService
1414
{
15+
1516
readonly ITriggerDiscoveryService _triggerDiscoveryService;
1617
readonly ICascadeStrategy _cascadingStrategy;
1718
readonly ILoggerFactory _loggerFactory;
18-
readonly TriggerOptions _options;
19+
readonly TriggerConfiguration _defaultConfiguration;
1920

2021
ITriggerSession? _currentTriggerSession;
2122

22-
public TriggerService(ITriggerDiscoveryService triggerDiscoveryService, ICascadeStrategy cascadingStrategy, ILoggerFactory loggerFactory, IOptionsSnapshot<TriggerOptions> triggerOptionsSnapshot)
23+
public TriggerService(ITriggerDiscoveryService triggerDiscoveryService, ICascadeStrategy cascadingStrategy, ILoggerFactory loggerFactory, IOptions<TriggerOptions> triggerOptions)
2324
{
2425
_triggerDiscoveryService = triggerDiscoveryService ?? throw new ArgumentNullException(nameof(triggerDiscoveryService));
2526
_cascadingStrategy = cascadingStrategy ?? throw new ArgumentNullException(nameof(cascadingStrategy));
2627
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
27-
_options = triggerOptionsSnapshot.Value;
28+
_defaultConfiguration = new TriggerConfiguration(false, triggerOptions.Value.MaxCascadeCycles);
29+
30+
Configuration = _defaultConfiguration;
2831
}
2932

3033
public ITriggerSession? Current
@@ -33,6 +36,8 @@ public ITriggerSession? Current
3336
set => _currentTriggerSession = value;
3437
}
3538

39+
public TriggerConfiguration Configuration { get; set; }
40+
3641
public ITriggerSession CreateSession(DbContext context, IServiceProvider? serviceProvider)
3742
{
3843
if (context is null)
@@ -47,7 +52,7 @@ public ITriggerSession CreateSession(DbContext context, IServiceProvider? servic
4752
_triggerDiscoveryService.ServiceProvider = serviceProvider;
4853
}
4954

50-
var triggerSession = new TriggerSession(this, _options, _triggerDiscoveryService, triggerContextTracker, _loggerFactory.CreateLogger<TriggerSession>());
55+
var triggerSession = new TriggerSession(this, Configuration, _triggerDiscoveryService, triggerContextTracker, _loggerFactory.CreateLogger<TriggerSession>());
5156

5257
_currentTriggerSession = triggerSession;
5358

@@ -61,6 +66,8 @@ public void ResetState()
6166
_currentTriggerSession.Dispose();
6267
_currentTriggerSession = null;
6368
}
69+
70+
Configuration = _defaultConfiguration;
6471
}
6572

6673
public Task ResetStateAsync(CancellationToken cancellationToken = default)

src/EntityFrameworkCore.Triggered/TriggerSession.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ public class TriggerSession : ITriggerSession
1717
static ITriggerContextDiscoveryStrategy? _afterSaveFailedTriggerContextDiscoveryStrategy;
1818

1919
readonly ITriggerService _triggerService;
20-
readonly TriggerOptions _options;
20+
readonly TriggerConfiguration _configuration;
2121
readonly ITriggerDiscoveryService _triggerDiscoveryService;
2222
readonly TriggerContextTracker _tracker;
2323
readonly ILogger<TriggerSession> _logger;
2424

2525
bool _raiseBeforeSaveTriggersCalled;
2626

27-
public TriggerSession(ITriggerService triggerService, TriggerOptions options, ITriggerDiscoveryService triggerDiscoveryService, TriggerContextTracker tracker, ILogger<TriggerSession> logger)
27+
public TriggerSession(ITriggerService triggerService, TriggerConfiguration configuration, ITriggerDiscoveryService triggerDiscoveryService, TriggerContextTracker tracker, ILogger<TriggerSession> logger)
2828
{
2929
_triggerService = triggerService ?? throw new ArgumentNullException(nameof(triggerService));
30-
_options = options ?? throw new ArgumentNullException(nameof(options));
30+
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
3131
_triggerDiscoveryService = triggerDiscoveryService ?? throw new ArgumentNullException(nameof(triggerDiscoveryService));
3232
_tracker = tracker ?? throw new ArgumentNullException(nameof(tracker));
3333
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -48,9 +48,14 @@ public async Task RaiseTriggers(Type openTriggerType, Exception? exception, ITri
4848
throw new ArgumentNullException(nameof(triggerContextDiscoveryStrategy));
4949
}
5050

51+
if (_configuration.Disabled)
52+
{
53+
return;
54+
}
55+
5156
cancellationToken.ThrowIfCancellationRequested();
5257

53-
var triggerContextDescriptorBatches = triggerContextDiscoveryStrategy.Discover(_options, _tracker, _logger);
58+
var triggerContextDescriptorBatches = triggerContextDiscoveryStrategy.Discover(_configuration, _tracker, _logger);
5459
foreach (var triggerContextDescriptorBatch in triggerContextDescriptorBatches)
5560
{
5661
List<(TriggerContextDescriptor triggerContextDescriptor, TriggerDescriptor triggerDescriptor)>? triggerInvocations = null;

src/EntityFrameworkCore.Triggered/TriggeredDbContext.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
4848

4949
public void SetTriggerServiceProvider(IServiceProvider? serviceProvider)
5050
=> _triggerServiceProvider = serviceProvider;
51-
51+
5252
public override int SaveChanges(bool acceptAllChangesOnSuccess)
5353
{
5454
bool RaiseAfterSavFailedTriggers(Exception exception)
@@ -62,11 +62,11 @@ bool RaiseAfterSavFailedTriggers(Exception exception)
6262

6363
var createdTriggerSession = false;
6464

65-
if (_triggerSession == null)
65+
if (_triggerSession is null)
6666
{
6767
var triggerService = this.GetService<ITriggerService>() ?? throw new InvalidOperationException("Triggers are not configured");
6868

69-
if (triggerService.Current != null)
69+
if (triggerService.Current is not null)
7070
{
7171
_triggerSession = triggerService.Current;
7272
}

0 commit comments

Comments
 (0)