diff --git a/src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs b/src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs
index c929be5238c..e2cb039e6cb 100644
--- a/src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs
+++ b/src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs
@@ -315,6 +315,93 @@ public virtual Task TransactionRolledBackAsync(
CancellationToken cancellationToken = default)
=> Task.CompletedTask;
+ ///
+ public virtual InterceptionResult CreatingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ => result;
+
+ ///
+ public virtual void CreatedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ }
+
+ ///
+ public virtual Task CreatingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ => Task.FromResult(result);
+
+ ///
+ public virtual Task CreatedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
+
+ ///
+ public virtual InterceptionResult RollingBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ => result;
+
+ ///
+ public virtual void RolledBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ }
+
+ ///
+ public virtual Task RollingBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ => Task.FromResult(result);
+
+ ///
+ public virtual Task RolledBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
+
+ ///
+ public virtual InterceptionResult ReleasingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ => result;
+
+ ///
+ public virtual void ReleasedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ }
+
+ ///
+ public virtual Task ReleasingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ => Task.FromResult(result);
+
+ ///
+ public virtual Task ReleasedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
+
///
/// Called when use of a has failed with an exception.
///
diff --git a/src/EFCore.Relational/Diagnostics/IDbTransactionInterceptor.cs b/src/EFCore.Relational/Diagnostics/IDbTransactionInterceptor.cs
index e28bb6c89a4..6da9f99cd4b 100644
--- a/src/EFCore.Relational/Diagnostics/IDbTransactionInterceptor.cs
+++ b/src/EFCore.Relational/Diagnostics/IDbTransactionInterceptor.cs
@@ -320,6 +320,213 @@ Task TransactionRolledBackAsync(
[NotNull] TransactionEndEventData eventData,
CancellationToken cancellationToken = default);
+ ///
+ /// Called just before EF intends to create a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ InterceptionResult CreatingSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result);
+
+ ///
+ /// Called immediately after EF creates a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ void CreatedSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData);
+
+ ///
+ /// Called just before EF intends to create a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ /// The cancellation token.
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ Task CreatingSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Called immediately after EF calls .
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ /// The cancellation token.
+ /// A representing the asynchronous operation.
+ Task CreatedSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Called just before EF intends to roll back to a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ InterceptionResult RollingBackToSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result);
+
+ ///
+ /// Called immediately after EF rolls back to a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ void RolledBackToSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData);
+
+ ///
+ /// Called just before EF intends to roll back to a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ /// The cancellation token.
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ Task RollingBackToSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Called immediately after EF rolls back to a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ /// The cancellation token.
+ /// A representing the asynchronous operation.
+ Task RolledBackToSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Called just before EF intends to release a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ InterceptionResult ReleasingSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result);
+
+ ///
+ /// Called immediately after EF releases a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ void ReleasedSavepoint(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData);
+
+ ///
+ /// Called just before EF intends to release a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ ///
+ /// Represents the current result if one exists.
+ /// This value will have set to if some previous
+ /// interceptor suppressed execution by calling .
+ /// This value is typically used as the return value for the implementation of this method.
+ ///
+ /// The cancellation token.
+ ///
+ /// If is false, the EF will continue as normal.
+ /// If is true, then EF will suppress the operation
+ /// it was about to perform.
+ /// A normal implementation of this method for any interceptor that is not attempting to suppress
+ /// the operation is to return the value passed in.
+ ///
+ Task ReleasingSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Called immediately after EF releases a transaction savepoint.
+ ///
+ /// The transaction.
+ /// Contextual information about connection and transaction.
+ /// The cancellation token.
+ /// A representing the asynchronous operation.
+ Task ReleasedSavepointAsync(
+ [NotNull] DbTransaction transaction,
+ [NotNull] TransactionEventData eventData,
+ CancellationToken cancellationToken = default);
+
///
/// Called when use of a has failed with an exception.
///
diff --git a/src/EFCore.Relational/Diagnostics/Internal/DbTransactionInterceptorAggregator.cs b/src/EFCore.Relational/Diagnostics/Internal/DbTransactionInterceptorAggregator.cs
index 2e5453b70ba..eb9a94dc0b0 100644
--- a/src/EFCore.Relational/Diagnostics/Internal/DbTransactionInterceptorAggregator.cs
+++ b/src/EFCore.Relational/Diagnostics/Internal/DbTransactionInterceptorAggregator.cs
@@ -220,6 +220,156 @@ await _interceptors[i].TransactionRolledBackAsync(transaction, eventData, cancel
}
}
+ public InterceptionResult CreatingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = _interceptors[i].CreatingSavepoint(transaction, eventData, result);
+ }
+
+ return result;
+ }
+
+ public void CreatedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ _interceptors[i].CreatedSavepoint(transaction, eventData);
+ }
+ }
+
+ public async Task CreatingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = await _interceptors[i].CreatingSavepointAsync(transaction, eventData, result, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ return result;
+ }
+
+ public async Task CreatedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ await _interceptors[i].CreatedSavepointAsync(transaction, eventData, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+
+ public InterceptionResult RollingBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = _interceptors[i].RollingBackToSavepoint(transaction, eventData, result);
+ }
+
+ return result;
+ }
+
+ public void RolledBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ _interceptors[i].RolledBackToSavepoint(transaction, eventData);
+ }
+ }
+
+ public async Task RollingBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = await _interceptors[i].RollingBackToSavepointAsync(transaction, eventData, result, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ return result;
+ }
+
+ public async Task RolledBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ await _interceptors[i].RolledBackToSavepointAsync(transaction, eventData, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+
+ public InterceptionResult ReleasingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = _interceptors[i].ReleasingSavepoint(transaction, eventData, result);
+ }
+
+ return result;
+ }
+
+ public void ReleasedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ _interceptors[i].ReleasedSavepoint(transaction, eventData);
+ }
+ }
+
+ public async Task ReleasingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ result = await _interceptors[i].ReleasingSavepointAsync(transaction, eventData, result, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ return result;
+ }
+
+ public async Task ReleasedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ for (var i = 0; i < _interceptors.Length; i++)
+ {
+ await _interceptors[i].ReleasedSavepointAsync(transaction, eventData, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+
public void TransactionFailed(DbTransaction transaction, TransactionErrorEventData eventData)
{
for (var i = 0; i < _interceptors.Length; i++)
diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
index 59d6799b24c..8fcff84a13e 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
@@ -52,6 +52,12 @@ private enum Id
TransactionStarting,
TransactionCommitting,
TransactionRollingBack,
+ CreatingTransactionSavepoint,
+ CreatedTransactionSavepoint,
+ RollingBackToTransactionSavepoint,
+ RolledBackToTransactionSavepoint,
+ ReleasingTransactionSavepoint,
+ ReleasedTransactionSavepoint,
// DataReader events
DataReaderDisposing = CoreEventId.RelationalBaseId + 300,
@@ -314,6 +320,84 @@ private enum Id
///
public static readonly EventId TransactionRolledBack = MakeTransactionId(Id.TransactionRolledBack);
+ ///
+ ///
+ /// A database transaction savepoint is being created.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId CreatingTransactionSavepoint = MakeTransactionId(Id.CreatingTransactionSavepoint);
+
+ ///
+ ///
+ /// A database transaction savepoint has been created.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId CreatedTransactionSavepoint = MakeTransactionId(Id.CreatedTransactionSavepoint);
+
+ ///
+ ///
+ /// A database transaction is being rolled back to a savepoint.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId RollingBackToTransactionSavepoint = MakeTransactionId(Id.RollingBackToTransactionSavepoint);
+
+ ///
+ ///
+ /// A database transaction has been rolled back to a savepoint.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId RolledBackToTransactionSavepoint = MakeTransactionId(Id.RolledBackToTransactionSavepoint);
+
+ ///
+ ///
+ /// A database transaction savepoint is being released.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId ReleasingTransactionSavepoint = MakeTransactionId(Id.ReleasingTransactionSavepoint);
+
+ ///
+ ///
+ /// A database transaction savepoint has been released.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId ReleasedTransactionSavepoint = MakeTransactionId(Id.ReleasedTransactionSavepoint);
+
///
///
/// A database transaction has been disposed.
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
index 79a53c893c0..15401e9e2b5 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
@@ -2745,6 +2745,732 @@ private static void LogTransactionRollingBack(
}
}
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The result of execution, which may have been modified by an interceptor.
+ public static InterceptionResult CreatingTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogCreatingTransactionSavepoint(diagnostics);
+
+ LogCreatingTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastCreatingTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.CreatingSavepoint(transaction, eventData, default);
+ }
+ }
+
+ return default;
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task CreatingTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogCreatingTransactionSavepoint(diagnostics);
+
+ LogCreatingTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastCreatingTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.CreatingSavepointAsync(transaction, eventData, default, cancellationToken);
+ }
+ }
+
+ return Task.FromResult(default(InterceptionResult));
+ }
+
+ private static TransactionEventData BroadcastCreatingTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogCreatingTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ public static void CreatedTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogCreatedTransactionSavepoint(diagnostics);
+
+ LogCreatedTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastCreatedTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ interceptor?.CreatedSavepoint(transaction, eventData);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task CreatedTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogCreatedTransactionSavepoint(diagnostics);
+
+ LogCreatedTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastCreatedTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.CreatedSavepointAsync(transaction, eventData, cancellationToken);
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private static TransactionEventData BroadcastCreatedTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogCreatedTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The result of execution, which may have been modified by an interceptor.
+ public static InterceptionResult RollingBackToTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogRollingBackToTransactionSavepoint(diagnostics);
+
+ LogRollingBackToTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastRollingBackToTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.RollingBackToSavepoint(transaction, eventData, default);
+ }
+ }
+
+ return default;
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task RollingBackToTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogRollingBackToTransactionSavepoint(diagnostics);
+
+ LogRollingBackToTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastRollingBackToTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.RollingBackToSavepointAsync(transaction, eventData, default, cancellationToken);
+ }
+ }
+
+ return Task.FromResult(default(InterceptionResult));
+ }
+
+ private static TransactionEventData BroadcastRollingBackToTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogRollingBackToTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ public static void RolledBackToTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogRolledBackToTransactionSavepoint(diagnostics);
+
+ LogRolledBackToTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastRolledBackToTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ interceptor?.RolledBackToSavepoint(transaction, eventData);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task RolledBackToTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogRolledBackToTransactionSavepoint(diagnostics);
+
+ LogCreatedTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastRolledBackToTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.RolledBackToSavepointAsync(transaction, eventData, cancellationToken);
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private static TransactionEventData BroadcastRolledBackToTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogRolledBackToTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The result of execution, which may have been modified by an interceptor.
+ public static InterceptionResult ReleasingTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogReleasingTransactionSavepoint(diagnostics);
+
+ LogReleasingTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastReleasingTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.ReleasingSavepoint(transaction, eventData, default);
+ }
+ }
+
+ return default;
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task ReleasingTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogReleasingTransactionSavepoint(diagnostics);
+
+ LogReleasingTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastReleasingTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.ReleasingSavepointAsync(transaction, eventData, default, cancellationToken);
+ }
+ }
+
+ return Task.FromResult(default(InterceptionResult));
+ }
+
+ private static TransactionEventData BroadcastReleasingTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogReleasingTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ public static void ReleasedTransactionSavepoint(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime)
+ {
+ var definition = RelationalResources.LogReleasedTransactionSavepoint(diagnostics);
+
+ LogReleasedTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastReleasedTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ false,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ interceptor?.ReleasedSavepoint(transaction, eventData);
+ }
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The connection.
+ /// The transaction.
+ /// The correlation ID associated with the .
+ /// The time that the operation was started.
+ /// The cancellation token.
+ /// A representing the async operation.
+ public static Task ReleasedTransactionSavepointAsync(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IRelationalConnection connection,
+ [NotNull] DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogReleasedTransactionSavepoint(diagnostics);
+
+ LogReleasedTransactionSavepoint(diagnostics, definition);
+
+ if (diagnostics.NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastReleasedTransactionSavepoint(
+ diagnostics,
+ connection,
+ transaction,
+ transactionId,
+ startTime,
+ definition,
+ true,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ if (interceptor != null)
+ {
+ return interceptor.ReleasedSavepointAsync(transaction, eventData, cancellationToken);
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private static TransactionEventData BroadcastReleasedTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ IRelationalConnection connection,
+ DbTransaction transaction,
+ Guid transactionId,
+ DateTimeOffset startTime,
+ EventDefinition definition,
+ bool async,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new TransactionEventData(
+ definition,
+ (d, p) => ((EventDefinition)d).GenerateMessage(),
+ transaction,
+ connection.Context,
+ transactionId,
+ connection.ConnectionId,
+ async,
+ startTime);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+ }
+
+ private static void LogReleasedTransactionSavepoint(
+ IDiagnosticsLogger diagnostics,
+ EventDefinition definition)
+ {
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+ }
+
///
/// Logs for the event.
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
index eb528cc2a3d..0e67c8edf53 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
@@ -142,6 +142,60 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase LogRolledBackTransaction;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogCreatingTransactionSavepoint;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogRollingBackToTransactionSavepoint;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogCreatedTransactionSavepoint;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogRolledBackToTransactionSavepoint;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogReleasingTransactionSavepoint;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase LogReleasedTransactionSavepoint;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index 55ecbf8ed08..bfa56d43f12 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -1203,6 +1203,150 @@ public static EventDefinition LogRolledBackTransaction([NotNull] IDiagnosticsLog
return (EventDefinition)definition;
}
+ ///
+ /// Creating transaction savepoint.
+ ///
+ public static EventDefinition LogCreatingTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogCreatingTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogCreatingTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.CreatingTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.CreatingTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.CreatingTransactionSavepoint,
+ _resourceManager.GetString("LogCreatingTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
+ ///
+ /// Created transaction savepoint.
+ ///
+ public static EventDefinition LogCreatedTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogCreatedTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogCreatedTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.CreatedTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.CreatedTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.CreatedTransactionSavepoint,
+ _resourceManager.GetString("LogCreatedTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
+ ///
+ /// Rolling back to transaction savepoint.
+ ///
+ public static EventDefinition LogRollingBackToTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogRollingBackToTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogRollingBackToTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.RollingBackToTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.RollingBackToTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.RollingBackToTransactionSavepoint,
+ _resourceManager.GetString("LogRollingBackToTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
+ ///
+ /// Rolled back to transaction savepoint.
+ ///
+ public static EventDefinition LogRolledBackToTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogRolledBackToTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogRolledBackToTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.RolledBackToTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.RolledBackToTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.RolledBackToTransactionSavepoint,
+ _resourceManager.GetString("LogRolledBackToTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
+ ///
+ /// Releasing transaction savepoint.
+ ///
+ public static EventDefinition LogReleasingTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogReleasingTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogReleasingTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.ReleasingTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.ReleasingTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.ReleasingTransactionSavepoint,
+ _resourceManager.GetString("LogReleasingTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
+ ///
+ /// Released transaction savepoint.
+ ///
+ public static EventDefinition LogReleasedTransactionSavepoint([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogReleasedTransactionSavepoint;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogReleasedTransactionSavepoint,
+ () => new EventDefinition(
+ logger.Options,
+ RelationalEventId.ReleasedTransactionSavepoint,
+ LogLevel.Debug,
+ "RelationalEventId.ReleasedTransactionSavepoint",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.ReleasedTransactionSavepoint,
+ _resourceManager.GetString("LogReleasedTransactionSavepoint"))));
+ }
+
+ return (EventDefinition)definition;
+ }
+
///
/// Disposing transaction.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index b59a504cf75..bd038924836 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -208,6 +208,30 @@
Rolled back transaction.
Debug RelationalEventId.TransactionRolledBack
+
+ Creating transaction savepoint.
+ Debug RelationalEventId.CreatingTransactionSavepoint
+
+
+ Created transaction savepoint.
+ Debug RelationalEventId.CreatedTransactionSavepoint
+
+
+ Rolling back to transaction savepoint..
+ Debug RelationalEventId.RollingBackToTransactionSavepoint
+
+
+ Rolled back to transaction savepoint..
+ Debug RelationalEventId.RolledBackToTransactionSavepoint
+
+
+ Releasing transaction savepoint.
+ Debug RelationalEventId.ReleasingTransactionSavepoint
+
+
+ Released transaction savepoint.
+ Debug RelationalEventId.ReleasedTransactionSavepoint
+
Disposing transaction.
Debug RelationalEventId.TransactionDisposed
diff --git a/src/EFCore.Relational/Storage/RelationalTransaction.cs b/src/EFCore.Relational/Storage/RelationalTransaction.cs
index 4198521c6c4..61ce2618675 100644
--- a/src/EFCore.Relational/Storage/RelationalTransaction.cs
+++ b/src/EFCore.Relational/Storage/RelationalTransaction.cs
@@ -264,19 +264,90 @@ await Logger.TransactionErrorAsync(
///
public virtual void Save(string savepointName)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointSaveSql(savepointName);
- command.ExecuteNonQuery();
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = Logger.CreatingTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointSaveSql(savepointName);
+ command.ExecuteNonQuery();
+ }
+
+ Logger.CreatedTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+ }
+ catch (Exception e)
+ {
+ Logger.TransactionError(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "CreateSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed);
+
+ throw;
+ }
}
///
- public virtual Task SaveAsync(string savepointName, CancellationToken cancellationToken = default)
+ public virtual async Task SaveAsync(string savepointName, CancellationToken cancellationToken = default)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointSaveSql(savepointName);
- return command.ExecuteNonQueryAsync(cancellationToken);
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = await Logger.CreatingTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointSaveSql(savepointName);
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ await Logger.CreatedTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ await Logger.TransactionErrorAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "CreateSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed,
+ cancellationToken).ConfigureAwait(false);
+
+ throw;
+ }
}
///
@@ -290,19 +361,90 @@ public virtual Task SaveAsync(string savepointName, CancellationToken cancellati
///
public virtual void Rollback(string savepointName)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointRollbackSql(savepointName);
- command.ExecuteNonQuery();
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = Logger.RollingBackToTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointRollbackSql(savepointName);
+ command.ExecuteNonQuery();
+ }
+
+ Logger.RolledBackToTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+ }
+ catch (Exception e)
+ {
+ Logger.TransactionError(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "RollbackToSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed);
+
+ throw;
+ }
}
///
- public virtual Task RollbackAsync(string savepointName, CancellationToken cancellationToken = default)
+ public virtual async Task RollbackAsync(string savepointName, CancellationToken cancellationToken = default)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointRollbackSql(savepointName);
- return command.ExecuteNonQueryAsync(cancellationToken);
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = await Logger.RollingBackToTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointRollbackSql(savepointName);
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ await Logger.RolledBackToTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ await Logger.TransactionErrorAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "RollbackToSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed,
+ cancellationToken).ConfigureAwait(false);
+
+ throw;
+ }
}
///
@@ -316,19 +458,90 @@ public virtual Task RollbackAsync(string savepointName, CancellationToken cancel
///
public virtual void Release(string savepointName)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointReleaseSql(savepointName);
- command.ExecuteNonQuery();
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = Logger.ReleasingTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointReleaseSql(savepointName);
+ command.ExecuteNonQuery();
+ }
+
+ Logger.ReleasedTransactionSavepoint(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime);
+ }
+ catch (Exception e)
+ {
+ Logger.TransactionError(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "ReleaseSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed);
+
+ throw;
+ }
}
///
- public virtual Task ReleaseAsync(string savepointName, CancellationToken cancellationToken = default)
+ public virtual async Task ReleaseAsync(string savepointName, CancellationToken cancellationToken = default)
{
- using var command = Connection.DbConnection.CreateCommand();
- command.Transaction = _dbTransaction;
- command.CommandText = GetSavepointReleaseSql(savepointName);
- return command.ExecuteNonQueryAsync(cancellationToken);
+ var startTime = DateTimeOffset.UtcNow;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ var interceptionResult = await Logger.ReleasingTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+
+ if (!interceptionResult.IsSuppressed)
+ {
+ using var command = Connection.DbConnection.CreateCommand();
+ command.Transaction = _dbTransaction;
+ command.CommandText = GetSavepointReleaseSql(savepointName);
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ await Logger.ReleasedTransactionSavepointAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ startTime,
+ cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ await Logger.TransactionErrorAsync(
+ Connection,
+ _dbTransaction,
+ TransactionId,
+ "ReleaseSavepoint",
+ e,
+ startTime,
+ stopwatch.Elapsed,
+ cancellationToken).ConfigureAwait(false);
+
+ throw;
+ }
}
///
diff --git a/test/EFCore.Relational.Specification.Tests/TransactionInterceptionTestBase.cs b/test/EFCore.Relational.Specification.Tests/TransactionInterceptionTestBase.cs
index 846a4b39ffc..47ed17cb16d 100644
--- a/test/EFCore.Relational.Specification.Tests/TransactionInterceptionTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/TransactionInterceptionTestBase.cs
@@ -375,6 +375,109 @@ public virtual async Task Intercept_Rollback_to_suppress(bool async)
}
}
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public virtual async Task Intercept_CreateSavepoint(bool async)
+ {
+ var (context, interceptor) = CreateContext();
+ using (context)
+ {
+ using var contextTransaction = async
+ ? await context.Database.BeginTransactionAsync()
+ : context.Database.BeginTransaction();
+ interceptor.Reset();
+
+ using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
+ if (async)
+ {
+ await contextTransaction.SaveAsync("dummy");
+ }
+ else
+ {
+ contextTransaction.Save("dummy");
+ }
+
+ AssertCreateSavepoint(context, contextTransaction, interceptor, async);
+
+ AssertCreateSavepointEvents(listener);
+ }
+ }
+
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public virtual async Task Intercept_RollbackToSavepoint(bool async)
+ {
+ var (context, interceptor) = CreateContext();
+ using (context)
+ {
+ using var contextTransaction = async
+ ? await context.Database.BeginTransactionAsync()
+ : context.Database.BeginTransaction();
+ if (async)
+ {
+ await contextTransaction.SaveAsync("dummy");
+ }
+ else
+ {
+ contextTransaction.Save("dummy");
+ }
+ interceptor.Reset();
+
+ using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
+ if (async)
+ {
+ await contextTransaction.RollbackAsync("dummy");
+ }
+ else
+ {
+ contextTransaction.Rollback("dummy");
+ }
+
+ AssertRollbackToSavepoint(context, contextTransaction, interceptor, async);
+
+ AssertRollbackToSavepointEvents(listener);
+ }
+ }
+
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public virtual async Task Intercept_ReleaseSavepoint(bool async)
+ {
+ var (context, interceptor) = CreateContext();
+ using (context)
+ {
+ using var contextTransaction = async
+ ? await context.Database.BeginTransactionAsync()
+ : context.Database.BeginTransaction();
+ if (async)
+ {
+ await contextTransaction.SaveAsync("dummy");
+ }
+ else
+ {
+ contextTransaction.Save("dummy");
+ }
+ interceptor.Reset();
+
+ using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
+ if (async)
+ {
+ await contextTransaction.ReleaseAsync("dummy");
+ }
+ else
+ {
+ contextTransaction.Release("dummy");
+ }
+
+ AssertReleaseSavepoint(context, contextTransaction, interceptor, async);
+
+ AssertReleaseSavepointEvents(listener);
+ }
+ }
+
protected class CommitSuppressingTransactionInterceptor : TransactionInterceptor
{
public override InterceptionResult TransactionCommitting(
@@ -554,6 +657,12 @@ private static void AssertBeginTransaction(DbContext context, TransactionInterce
Assert.False(interceptor.CommittedCalled);
Assert.False(interceptor.RollingBackCalled);
Assert.False(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
Assert.False(interceptor.FailedCalled);
Assert.Same(context, interceptor.Context);
}
@@ -573,6 +682,12 @@ private static void AssertUseTransaction(
Assert.False(interceptor.CommittedCalled);
Assert.False(interceptor.RollingBackCalled);
Assert.False(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
Assert.False(interceptor.StartedCalled);
Assert.False(interceptor.FailedCalled);
Assert.Same(context, interceptor.Context);
@@ -592,6 +707,12 @@ private static void AssertCommit(
Assert.True(interceptor.CommittedCalled);
Assert.False(interceptor.RollingBackCalled);
Assert.False(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
Assert.False(interceptor.UsedCalled);
Assert.False(interceptor.StartingCalled);
Assert.False(interceptor.StartedCalled);
@@ -613,6 +734,93 @@ private static void AssertRollBack(
Assert.False(interceptor.CommittedCalled);
Assert.True(interceptor.RollingBackCalled);
Assert.True(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
+ Assert.False(interceptor.UsedCalled);
+ Assert.False(interceptor.StartingCalled);
+ Assert.False(interceptor.StartedCalled);
+ Assert.False(interceptor.FailedCalled);
+ Assert.Same(context, interceptor.Context);
+ Assert.Equal(contextTransaction.TransactionId, interceptor.TransactionId);
+ }
+
+ private static void AssertCreateSavepoint(
+ DbContext context,
+ IDbContextTransaction contextTransaction,
+ TransactionInterceptor interceptor,
+ bool async)
+ {
+ Assert.Equal(async, interceptor.AsyncCalled);
+ Assert.NotEqual(async, interceptor.SyncCalled);
+ Assert.NotEqual(interceptor.AsyncCalled, interceptor.SyncCalled);
+ Assert.False(interceptor.CommittingCalled);
+ Assert.False(interceptor.CommittedCalled);
+ Assert.False(interceptor.RollingBackCalled);
+ Assert.False(interceptor.RolledBackCalled);
+ Assert.True(interceptor.CreatingSavepointCalled);
+ Assert.True(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
+ Assert.False(interceptor.UsedCalled);
+ Assert.False(interceptor.StartingCalled);
+ Assert.False(interceptor.StartedCalled);
+ Assert.False(interceptor.FailedCalled);
+ Assert.Same(context, interceptor.Context);
+ Assert.Equal(contextTransaction.TransactionId, interceptor.TransactionId);
+ }
+
+ private static void AssertRollbackToSavepoint(
+ DbContext context,
+ IDbContextTransaction contextTransaction,
+ TransactionInterceptor interceptor,
+ bool async)
+ {
+ Assert.Equal(async, interceptor.AsyncCalled);
+ Assert.NotEqual(async, interceptor.SyncCalled);
+ Assert.NotEqual(interceptor.AsyncCalled, interceptor.SyncCalled);
+ Assert.False(interceptor.CommittingCalled);
+ Assert.False(interceptor.CommittedCalled);
+ Assert.False(interceptor.RollingBackCalled);
+ Assert.False(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.True(interceptor.RollingBackToSavepointCalled);
+ Assert.True(interceptor.RolledBackToSavepointCalled);
+ Assert.False(interceptor.ReleasingSavepointCalled);
+ Assert.False(interceptor.ReleasedSavepointCalled);
+ Assert.False(interceptor.UsedCalled);
+ Assert.False(interceptor.StartingCalled);
+ Assert.False(interceptor.StartedCalled);
+ Assert.False(interceptor.FailedCalled);
+ Assert.Same(context, interceptor.Context);
+ Assert.Equal(contextTransaction.TransactionId, interceptor.TransactionId);
+ }
+
+ private static void AssertReleaseSavepoint(
+ DbContext context,
+ IDbContextTransaction contextTransaction,
+ TransactionInterceptor interceptor,
+ bool async)
+ {
+ Assert.Equal(async, interceptor.AsyncCalled);
+ Assert.NotEqual(async, interceptor.SyncCalled);
+ Assert.NotEqual(interceptor.AsyncCalled, interceptor.SyncCalled);
+ Assert.False(interceptor.CommittingCalled);
+ Assert.False(interceptor.CommittedCalled);
+ Assert.False(interceptor.RollingBackCalled);
+ Assert.False(interceptor.RolledBackCalled);
+ Assert.False(interceptor.CreatingSavepointCalled);
+ Assert.False(interceptor.CreatedSavepointCalled);
+ Assert.False(interceptor.RollingBackToSavepointCalled);
+ Assert.False(interceptor.RolledBackToSavepointCalled);
+ Assert.True(interceptor.ReleasingSavepointCalled);
+ Assert.True(interceptor.ReleasedSavepointCalled);
Assert.False(interceptor.UsedCalled);
Assert.False(interceptor.StartingCalled);
Assert.False(interceptor.StartedCalled);
@@ -651,6 +859,21 @@ private static void AssertRollBackEvents(ITestDiagnosticListener listener)
RelationalEventId.TransactionRollingBack.Name,
RelationalEventId.TransactionRolledBack.Name);
+ private static void AssertCreateSavepointEvents(ITestDiagnosticListener listener)
+ => listener.AssertEventsInOrder(
+ RelationalEventId.CreatingTransactionSavepoint.Name,
+ RelationalEventId.CreatedTransactionSavepoint.Name);
+
+ private static void AssertRollbackToSavepointEvents(ITestDiagnosticListener listener)
+ => listener.AssertEventsInOrder(
+ RelationalEventId.RollingBackToTransactionSavepoint.Name,
+ RelationalEventId.RolledBackToTransactionSavepoint.Name);
+
+ private static void AssertReleaseSavepointEvents(ITestDiagnosticListener listener)
+ => listener.AssertEventsInOrder(
+ RelationalEventId.ReleasingTransactionSavepoint.Name,
+ RelationalEventId.ReleasedTransactionSavepoint.Name);
+
protected class TransactionInterceptor : IDbTransactionInterceptor
{
public DbContext Context { get; set; }
@@ -667,6 +890,12 @@ protected class TransactionInterceptor : IDbTransactionInterceptor
public bool CommittedCalled { get; set; }
public bool RollingBackCalled { get; set; }
public bool RolledBackCalled { get; set; }
+ public bool CreatingSavepointCalled { get; set; }
+ public bool CreatedSavepointCalled { get; set; }
+ public bool RollingBackToSavepointCalled { get; set; }
+ public bool RolledBackToSavepointCalled { get; set; }
+ public bool ReleasingSavepointCalled { get; set; }
+ public bool ReleasedSavepointCalled { get; set; }
public bool FailedCalled { get; set; }
public void Reset()
@@ -683,6 +912,12 @@ public void Reset()
CommittedCalled = false;
RollingBackCalled = false;
RolledBackCalled = false;
+ CreatingSavepointCalled = false;
+ CreatedSavepointCalled = false;
+ RollingBackToSavepointCalled = false;
+ RolledBackToSavepointCalled = false;
+ ReleasingSavepointCalled = false;
+ ReleasedSavepointCalled = false;
FailedCalled = false;
}
@@ -762,6 +997,72 @@ protected virtual void AssertRolledBack(TransactionEndEventData eventData)
RolledBackCalled = true;
}
+ protected virtual void AssertCreatingSavepoint(TransactionEventData eventData)
+ {
+ Assert.NotNull(eventData.Context);
+ Assert.NotEqual(default, eventData.ConnectionId);
+ Assert.NotEqual(default, eventData.TransactionId);
+
+ Context = eventData.Context;
+ TransactionId = eventData.TransactionId;
+ ConnectionId = eventData.ConnectionId;
+
+ CreatingSavepointCalled = true;
+ }
+
+ protected virtual void AssertCreatedSavepoint(TransactionEventData eventData)
+ {
+ Assert.Same(Context, eventData.Context);
+ Assert.Equal(TransactionId, eventData.TransactionId);
+ Assert.Equal(ConnectionId, eventData.ConnectionId);
+
+ CreatedSavepointCalled = true;
+ }
+
+ protected virtual void AssertRollingBackToSavepoint(TransactionEventData eventData)
+ {
+ Assert.NotNull(eventData.Context);
+ Assert.NotEqual(default, eventData.ConnectionId);
+ Assert.NotEqual(default, eventData.TransactionId);
+
+ Context = eventData.Context;
+ TransactionId = eventData.TransactionId;
+ ConnectionId = eventData.ConnectionId;
+
+ RollingBackToSavepointCalled = true;
+ }
+
+ protected virtual void AssertRolledBackToSavepoint(TransactionEventData eventData)
+ {
+ Assert.Same(Context, eventData.Context);
+ Assert.Equal(TransactionId, eventData.TransactionId);
+ Assert.Equal(ConnectionId, eventData.ConnectionId);
+
+ RolledBackToSavepointCalled = true;
+ }
+
+ protected virtual void AssertReleasingSavepoint(TransactionEventData eventData)
+ {
+ Assert.NotNull(eventData.Context);
+ Assert.NotEqual(default, eventData.ConnectionId);
+ Assert.NotEqual(default, eventData.TransactionId);
+
+ Context = eventData.Context;
+ TransactionId = eventData.TransactionId;
+ ConnectionId = eventData.ConnectionId;
+
+ ReleasingSavepointCalled = true;
+ }
+
+ protected virtual void AssertReleasedSavepoint(TransactionEventData eventData)
+ {
+ Assert.Same(Context, eventData.Context);
+ Assert.Equal(TransactionId, eventData.TransactionId);
+ Assert.Equal(ConnectionId, eventData.ConnectionId);
+
+ ReleasedSavepointCalled = true;
+ }
+
protected virtual void AssertFailed(TransactionErrorEventData eventData)
{
Assert.Same(Context, eventData.Context);
@@ -952,6 +1253,144 @@ public virtual Task TransactionRolledBackAsync(
return Task.CompletedTask;
}
+ public virtual InterceptionResult CreatingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertCreatingSavepoint(eventData);
+
+ return result;
+ }
+
+ public virtual void CreatedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertCreatedSavepoint(eventData);
+ }
+
+ public virtual Task CreatingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertCreatingSavepoint(eventData);
+
+ return Task.FromResult(result);
+ }
+
+ public virtual Task CreatedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertCreatedSavepoint(eventData);
+
+ return Task.CompletedTask;
+ }
+
+ public virtual InterceptionResult RollingBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertRollingBackToSavepoint(eventData);
+
+ return result;
+ }
+
+ public virtual void RolledBackToSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertRolledBackToSavepoint(eventData);
+ }
+
+ public virtual Task RollingBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertRollingBackToSavepoint(eventData);
+
+ return Task.FromResult(result);
+ }
+
+ public virtual Task RolledBackToSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertRolledBackToSavepoint(eventData);
+
+ return Task.CompletedTask;
+ }
+
+ public virtual InterceptionResult ReleasingSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertReleasingSavepoint(eventData);
+
+ return result;
+ }
+
+ public virtual void ReleasedSavepoint(
+ DbTransaction transaction,
+ TransactionEventData eventData)
+ {
+ Assert.False(eventData.IsAsync);
+ SyncCalled = true;
+ AssertReleasedSavepoint(eventData);
+ }
+
+ public virtual Task ReleasingSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertReleasingSavepoint(eventData);
+
+ return Task.FromResult(result);
+ }
+
+ public virtual Task ReleasedSavepointAsync(
+ DbTransaction transaction,
+ TransactionEventData eventData,
+ CancellationToken cancellationToken = default)
+ {
+ Assert.True(eventData.IsAsync);
+ AsyncCalled = true;
+ AssertReleasedSavepoint(eventData);
+
+ return Task.CompletedTask;
+ }
+
public virtual void TransactionFailed(
DbTransaction transaction,
TransactionErrorEventData eventData)
diff --git a/test/EFCore.SqlServer.FunctionalTests/TransactionInterceptionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/TransactionInterceptionSqlServerTest.cs
index de15b2cfacc..2e7b2e3e157 100644
--- a/test/EFCore.SqlServer.FunctionalTests/TransactionInterceptionSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/TransactionInterceptionSqlServerTest.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
@@ -35,6 +36,9 @@ public TransactionInterceptionSqlServerTest(InterceptionSqlServerFixture fixture
{
}
+ // ReleaseSavepoint is unsupported by SQL Server and is ignored
+ public override Task Intercept_ReleaseSavepoint(bool async) => Task.CompletedTask;
+
public class InterceptionSqlServerFixture : InterceptionSqlServerFixtureBase
{
protected override bool ShouldSubscribeToDiagnosticListener => false;
@@ -50,6 +54,9 @@ public TransactionInterceptionWithDiagnosticsSqlServerTest(InterceptionSqlServer
{
}
+ // ReleaseSavepoint is unsupported by SQL Server and is ignored
+ public override Task Intercept_ReleaseSavepoint(bool async) => Task.CompletedTask;
+
public class InterceptionSqlServerFixture : InterceptionSqlServerFixtureBase
{
protected override bool ShouldSubscribeToDiagnosticListener => true;