diff --git a/All.sln.DotSettings b/All.sln.DotSettings
index d1773f1c53d..0142bfa3d5e 100644
--- a/All.sln.DotSettings
+++ b/All.sln.DotSettings
@@ -220,6 +220,7 @@ Licensed under the Apache License, Version 2.0. See License.txt in the project r
True
True
True
+ True
True
True
True
diff --git a/src/EFCore.Relational/Storage/ISqlGenerationHelper.cs b/src/EFCore.Relational/Storage/ISqlGenerationHelper.cs
index ea889383ab4..5fd2b278641 100644
--- a/src/EFCore.Relational/Storage/ISqlGenerationHelper.cs
+++ b/src/EFCore.Relational/Storage/ISqlGenerationHelper.cs
@@ -120,5 +120,26 @@ public interface ISqlGenerationHelper
/// The comment text.
/// The generated SQL.
string GenerateComment([NotNull] string text);
+
+ ///
+ /// Generates an SQL statement which creates a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be created.
+ /// An SQL string to create the savepoint.
+ string GenerateCreateSavepointStatement([NotNull] string name);
+
+ ///
+ /// Generates an SQL statement which which rolls back to a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be rolled back to.
+ /// An SQL string to roll back the savepoint.
+ string GenerateRollbackToSavepointStatement([NotNull] string name);
+
+ ///
+ /// Generates an SQL statement which which releases a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be released.
+ /// An SQL string to release the savepoint.
+ string GenerateReleaseSavepointStatement([NotNull] string name);
}
}
diff --git a/src/EFCore.Relational/Storage/RelationalSqlGenerationHelper.cs b/src/EFCore.Relational/Storage/RelationalSqlGenerationHelper.cs
index 61a8db574c7..c5116dff609 100644
--- a/src/EFCore.Relational/Storage/RelationalSqlGenerationHelper.cs
+++ b/src/EFCore.Relational/Storage/RelationalSqlGenerationHelper.cs
@@ -199,5 +199,29 @@ public virtual string GenerateComment(string text)
return builder.ToString();
}
+
+ ///
+ /// Generates an SQL statement which creates a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be created.
+ /// An SQL string to create the savepoint.
+ public virtual string GenerateCreateSavepointStatement(string name)
+ => "SAVEPOINT " + DelimitIdentifier(name);
+
+ ///
+ /// Generates an SQL statement which which rolls back to a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be rolled back to.
+ /// An SQL string to roll back the savepoint.
+ public virtual string GenerateRollbackToSavepointStatement(string name)
+ => "ROLLBACK TO " + DelimitIdentifier(name);
+
+ ///
+ /// Generates an SQL statement which which releases a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be released.
+ /// An SQL string to release the savepoint.
+ public virtual string GenerateReleaseSavepointStatement(string name)
+ => "RELEASE SAVEPOINT " + DelimitIdentifier(name);
}
}
diff --git a/src/EFCore.Relational/Storage/RelationalTransaction.cs b/src/EFCore.Relational/Storage/RelationalTransaction.cs
index e315541a62c..ad41391ab47 100644
--- a/src/EFCore.Relational/Storage/RelationalTransaction.cs
+++ b/src/EFCore.Relational/Storage/RelationalTransaction.cs
@@ -26,6 +26,7 @@ public class RelationalTransaction : IDbContextTransaction, IInfrastructure
/// A value indicating whether the transaction is owned by this class (i.e. if it can be disposed when this class is disposed).
///
+ /// The SQL generation helper to use.
public RelationalTransaction(
[NotNull] IRelationalConnection connection,
[NotNull] DbTransaction transaction,
Guid transactionId,
[NotNull] IDiagnosticsLogger logger,
- bool transactionOwned)
+ bool transactionOwned,
+ [NotNull] ISqlGenerationHelper sqlGenerationHelper)
{
Check.NotNull(connection, nameof(connection));
Check.NotNull(transaction, nameof(transaction));
Check.NotNull(logger, nameof(logger));
+ Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper));
if (connection.DbConnection != transaction.Connection)
{
@@ -62,6 +66,7 @@ public RelationalTransaction(
_dbTransaction = transaction;
Logger = logger;
_transactionOwned = transactionOwned;
+ _sqlGenerationHelper = sqlGenerationHelper;
}
///
@@ -279,7 +284,7 @@ public virtual void CreateSavepoint(string name)
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetCreateSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateCreateSavepointStatement(name);
command.ExecuteNonQuery();
}
@@ -323,7 +328,7 @@ public virtual async Task CreateSavepointAsync(string name, CancellationToken ca
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetCreateSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateCreateSavepointStatement(name);
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
@@ -350,15 +355,6 @@ await Logger.TransactionErrorAsync(
}
}
- ///
- /// When implemented in a provider supporting transaction savepoints, this method should return an
- /// SQL statement which creates a savepoint with the given name.
- ///
- /// The name of the savepoint to be created.
- /// An SQL string to create the savepoint.
- protected virtual string GetCreateSavepointSql([NotNull] string name)
- => "SAVEPOINT " + name;
-
///
public virtual void RollbackToSavepoint(string name)
{
@@ -377,7 +373,7 @@ public virtual void RollbackToSavepoint(string name)
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetRollbackToSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateRollbackToSavepointStatement(name);
command.ExecuteNonQuery();
}
@@ -421,7 +417,7 @@ public virtual async Task RollbackToSavepointAsync(string name, CancellationToke
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetRollbackToSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateRollbackToSavepointStatement(name);
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
@@ -448,15 +444,6 @@ await Logger.TransactionErrorAsync(
}
}
- ///
- /// When implemented in a provider supporting transaction savepoints, this method should return an
- /// SQL statement which rolls back a savepoint with the given name.
- ///
- /// The name of the savepoint to be created.
- /// An SQL string to create the savepoint.
- protected virtual string GetRollbackToSavepointSql([NotNull] string name)
- => "ROLLBACK TO " + name;
-
///
public virtual void ReleaseSavepoint(string name)
{
@@ -475,7 +462,7 @@ public virtual void ReleaseSavepoint(string name)
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetReleaseSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateReleaseSavepointStatement(name);
command.ExecuteNonQuery();
}
@@ -519,7 +506,7 @@ public virtual async Task ReleaseSavepointAsync(string name, CancellationToken c
{
using var command = Connection.DbConnection.CreateCommand();
command.Transaction = _dbTransaction;
- command.CommandText = GetReleaseSavepointSql(name);
+ command.CommandText = _sqlGenerationHelper.GenerateReleaseSavepointStatement(name);
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
@@ -546,21 +533,6 @@ await Logger.TransactionErrorAsync(
}
}
- ///
- ///
- /// When implemented in a provider supporting transaction savepoints, this method should return an
- /// SQL statement which releases a savepoint with the given name.
- ///
- ///
- /// If savepoint release isn't supported, and should
- /// be overridden to do nothing.
- ///
- ///
- /// The name of the savepoint to be created.
- /// An SQL string to create the savepoint.
- protected virtual string GetReleaseSavepointSql([NotNull] string name)
- => "RELEASE SAVEPOINT " + name;
-
///
public virtual bool SupportsSavepoints
=> true;
diff --git a/src/EFCore.Relational/Storage/RelationalTransactionFactory.cs b/src/EFCore.Relational/Storage/RelationalTransactionFactory.cs
index f626f3ee349..d4438e2dbb4 100644
--- a/src/EFCore.Relational/Storage/RelationalTransactionFactory.cs
+++ b/src/EFCore.Relational/Storage/RelationalTransactionFactory.cs
@@ -58,6 +58,7 @@ public virtual RelationalTransaction Create(
Guid transactionId,
IDiagnosticsLogger logger,
bool transactionOwned)
- => new RelationalTransaction(connection, transaction, transactionId, logger, transactionOwned);
+ => new RelationalTransaction(
+ connection, transaction, transactionId, logger, transactionOwned, Dependencies.SqlGenerationHelper);
}
}
diff --git a/src/EFCore.Relational/Storage/RelationalTransactionFactoryDependencies.cs b/src/EFCore.Relational/Storage/RelationalTransactionFactoryDependencies.cs
index 5beb3c6f8b8..b0b340754e7 100644
--- a/src/EFCore.Relational/Storage/RelationalTransactionFactoryDependencies.cs
+++ b/src/EFCore.Relational/Storage/RelationalTransactionFactoryDependencies.cs
@@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.EntityFrameworkCore.Storage
@@ -51,8 +53,24 @@ public sealed class RelationalTransactionFactoryDependencies
///
///
[EntityFrameworkInternal]
- public RelationalTransactionFactoryDependencies()
+ public RelationalTransactionFactoryDependencies([NotNull] ISqlGenerationHelper sqlGenerationHelper)
{
+ Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper));
+
+ SqlGenerationHelper = sqlGenerationHelper;
}
+
+ ///
+ /// Helpers for SQL generation.
+ ///
+ public ISqlGenerationHelper SqlGenerationHelper { get; }
+
+ ///
+ /// Clones this dependency parameter object with one service replaced.
+ ///
+ /// A replacement for the current dependency of this type.
+ /// A new parameter object with the given service replaced.
+ public RelationalTransactionFactoryDependencies With([NotNull] ISqlGenerationHelper sqlGenerationHelper)
+ => new RelationalTransactionFactoryDependencies(sqlGenerationHelper);
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerSqlGenerationHelper.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerSqlGenerationHelper.cs
index 6cd4dccb921..aa7d139010b 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerSqlGenerationHelper.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerSqlGenerationHelper.cs
@@ -101,5 +101,29 @@ public override void DelimitIdentifier(StringBuilder builder, string identifier)
EscapeIdentifier(builder, identifier);
builder.Append(']');
}
+
+ ///
+ /// Generates an SQL statement which creates a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be created.
+ /// An SQL string to create the savepoint.
+ public override string GenerateCreateSavepointStatement(string name)
+ => "SAVE TRANSACTION " + DelimitIdentifier(name);
+
+ ///
+ /// Generates an SQL statement which which rolls back to a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be rolled back to.
+ /// An SQL string to roll back the savepoint.
+ public override string GenerateRollbackToSavepointStatement(string name)
+ => "ROLLBACK TRANSACTION " + DelimitIdentifier(name);
+
+ ///
+ /// Generates an SQL statement which which releases a savepoint with the given name.
+ ///
+ /// The name of the savepoint to be released.
+ /// An SQL string to release the savepoint.
+ public override string GenerateReleaseSavepointStatement(string name)
+ => throw new NotSupportedException("SQL Server does not support releasing a savepoint");
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransaction.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransaction.cs
index 56ca777fa36..bf4bd97bb40 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransaction.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransaction.cs
@@ -30,18 +30,13 @@ public SqlServerTransaction(
[NotNull] DbTransaction transaction,
Guid transactionId,
[NotNull] IDiagnosticsLogger logger,
- bool transactionOwned)
- : base(connection, transaction, transactionId, logger, transactionOwned)
+ bool transactionOwned,
+ [NotNull] ISqlGenerationHelper sqlGenerationHelper)
+ : base(connection, transaction, transactionId, logger, transactionOwned, sqlGenerationHelper)
{
}
- ///
- protected override string GetCreateSavepointSql(string name)
- => "SAVE TRANSACTION " + name;
-
- ///
- protected override string GetRollbackToSavepointSql(string name)
- => "ROLLBACK TRANSACTION " + name;
+ // SQL Server doesn't support releasing savepoints. Override to do nothing.
///
public override void ReleaseSavepoint(string name) { }
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransactionFactory.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransactionFactory.cs
index da75b8ce65d..8a208dfaad6 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransactionFactory.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransactionFactory.cs
@@ -3,8 +3,10 @@
using System;
using System.Data.Common;
+using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
{
@@ -16,6 +18,22 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
///
public class SqlServerTransactionFactory : IRelationalTransactionFactory
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Parameter object containing dependencies for this service.
+ public SqlServerTransactionFactory([NotNull] RelationalTransactionFactoryDependencies dependencies)
+ {
+ Check.NotNull(dependencies, nameof(dependencies));
+
+ Dependencies = dependencies;
+ }
+
+ ///
+ /// Parameter object containing dependencies for this service.
+ ///
+ protected virtual RelationalTransactionFactoryDependencies Dependencies { get; }
+
///
/// 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
@@ -28,6 +46,6 @@ public virtual RelationalTransaction Create(
Guid transactionId,
IDiagnosticsLogger logger,
bool transactionOwned)
- => new SqlServerTransaction(connection, transaction, transactionId, logger, transactionOwned);
+ => new SqlServerTransaction(connection, transaction, transactionId, logger, transactionOwned, Dependencies.SqlGenerationHelper);
}
}
diff --git a/src/EFCore/Storage/IDbContextTransaction.cs b/src/EFCore/Storage/IDbContextTransaction.cs
index cfc0ab93988..d06f3f41ccd 100644
--- a/src/EFCore/Storage/IDbContextTransaction.cs
+++ b/src/EFCore/Storage/IDbContextTransaction.cs
@@ -86,15 +86,27 @@ Task RollbackToSavepointAsync([NotNull] string name, CancellationToken cancellat
=> throw new NotSupportedException();
///
- /// Destroys a savepoint previously defined in the current transaction. This allows the system to
- /// reclaim some resources before the transaction ends.
+ ///
+ /// Destroys a savepoint previously defined in the current transaction. This allows the system to
+ /// reclaim some resources before the transaction ends.
+ ///
+ ///
+ /// If savepoint release isn't supported, and should
+ /// do nothing rather than throw. This is the default behavior.
+ ///
///
/// The name of the savepoint to release.
void ReleaseSavepoint([NotNull] string name) { }
///
- /// Destroys a savepoint previously defined in the current transaction. This allows the system to
- /// reclaim some resources before the transaction ends.
+ ///
+ /// Destroys a savepoint previously defined in the current transaction. This allows the system to
+ /// reclaim some resources before the transaction ends.
+ ///
+ ///
+ /// If savepoint release isn't supported, and should
+ /// do nothing rather than throw. This is the default behavior.
+ ///
///
/// The name of the savepoint to release.
/// The cancellation token.
diff --git a/test/EFCore.Relational.Specification.Tests/TransactionTestBase.cs b/test/EFCore.Relational.Specification.Tests/TransactionTestBase.cs
index c6624a7babc..bdcec1d5507 100644
--- a/test/EFCore.Relational.Specification.Tests/TransactionTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/TransactionTestBase.cs
@@ -1209,6 +1209,136 @@ public virtual async Task Externally_closed_connections_are_handled_correctly(bo
Assert.Equal(ConnectionState.Closed, connection.State);
}
+ [ConditionalTheory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public virtual async Task Savepoint_can_be_rolled_back(bool async)
+ {
+ using (var context = CreateContext())
+ {
+ await using var transaction = await context.Database.BeginTransactionAsync();
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.CreateSavepointAsync("FooSavepoint");
+ }
+ else
+ {
+ transaction.CreateSavepoint("FooSavepoint");
+ }
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.RollbackToSavepointAsync("FooSavepoint");
+ }
+ else
+ {
+ transaction.RollbackToSavepoint("FooSavepoint");
+ }
+
+ await transaction.CommitAsync();
+ }
+
+ using (var context = CreateContext())
+ {
+ Assert.Equal(Customers.Count - 1, context.Set().Count());
+ }
+ }
+
+ [ConditionalTheory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public virtual async Task Savepoint_can_be_released(bool async)
+ {
+ using (var context = CreateContext())
+ {
+ await using var transaction = await context.Database.BeginTransactionAsync();
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.CreateSavepointAsync("FooSavepoint");
+ }
+ else
+ {
+ transaction.CreateSavepoint("FooSavepoint");
+ }
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.ReleaseSavepointAsync("FooSavepoint");
+ await Assert.ThrowsAnyAsync(
+ async () => await transaction.ReleaseSavepointAsync("FooSavepoint"));
+ }
+ else
+ {
+ transaction.ReleaseSavepoint("FooSavepoint");
+ Assert.ThrowsAny(
+ () => transaction.ReleaseSavepoint("FooSavepoint"));
+ }
+
+ await transaction.CommitAsync();
+ }
+
+ using (var context = CreateContext())
+ {
+ Assert.Equal(Customers.Count - 2, context.Set().Count());
+ }
+ }
+
+ [ConditionalTheory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public virtual async Task Savepoint_name_is_quoted(bool async)
+ {
+ using (var context = CreateContext())
+ {
+ await using var transaction = await context.Database.BeginTransactionAsync();
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.CreateSavepointAsync("Name with spaces");
+ }
+ else
+ {
+ transaction.CreateSavepoint("Name with spaces");
+ }
+
+ context.Entry(context.Set().OrderBy(c => c.Id).First()).State = EntityState.Deleted;
+ await context.SaveChangesAsync();
+
+ if (async)
+ {
+ await transaction.RollbackToSavepointAsync("Name with spaces");
+ }
+ else
+ {
+ transaction.RollbackToSavepoint("Name with spaces");
+ }
+
+ await transaction.CommitAsync();
+ }
+
+ using (var context = CreateContext())
+ {
+ Assert.Equal(Customers.Count - 1, context.Set().Count());
+ }
+ }
+
protected virtual void AssertStoreInitialState()
{
using var context = CreateContext();
diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTransactionExtensionsTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTransactionExtensionsTest.cs
index 1b25fa4ea0f..854386af98d 100644
--- a/test/EFCore.Relational.Tests/Storage/RelationalTransactionExtensionsTest.cs
+++ b/test/EFCore.Relational.Tests/Storage/RelationalTransactionExtensionsTest.cs
@@ -39,7 +39,9 @@ public void GetDbTransaction_returns_the_DbTransaction()
new DiagnosticListener("Fake"),
new TestRelationalLoggingDefinitions(),
new NullDbContextLogger()),
- false);
+ false,
+ new RelationalSqlGenerationHelper(
+ new RelationalSqlGenerationHelperDependencies()));
Assert.Equal(dbTransaction, transaction.GetDbTransaction());
}
diff --git a/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeRelationalConnection.cs b/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeRelationalConnection.cs
index 65d41ce72e6..71bcd002573 100644
--- a/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeRelationalConnection.cs
+++ b/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeRelationalConnection.cs
@@ -36,7 +36,10 @@ public FakeRelationalConnection(IDbContextOptions options = null)
new TestRelationalLoggingDefinitions(),
new NullDbContextLogger()),
new NamedConnectionStringResolver(options ?? CreateOptions()),
- new RelationalTransactionFactory(new RelationalTransactionFactoryDependencies()),
+ new RelationalTransactionFactory(
+ new RelationalTransactionFactoryDependencies(
+ new RelationalSqlGenerationHelper(
+ new RelationalSqlGenerationHelperDependencies()))),
new CurrentDbContext(new FakeDbContext())))
{
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestRelationalTransaction.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestRelationalTransaction.cs
index bf4d47aba23..49bd7b055f4 100644
--- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestRelationalTransaction.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestRelationalTransaction.cs
@@ -12,13 +12,20 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities
{
public class TestRelationalTransactionFactory : IRelationalTransactionFactory
{
+ public TestRelationalTransactionFactory(RelationalTransactionFactoryDependencies dependencies)
+ {
+ Dependencies = dependencies;
+ }
+
+ protected virtual RelationalTransactionFactoryDependencies Dependencies { get; }
+
public RelationalTransaction Create(
IRelationalConnection connection,
DbTransaction transaction,
Guid transactionId,
IDiagnosticsLogger logger,
bool transactionOwned)
- => new TestRelationalTransaction(connection, transaction, logger, transactionOwned);
+ => new TestRelationalTransaction(connection, transaction, logger, transactionOwned, Dependencies.SqlGenerationHelper);
}
public class TestRelationalTransaction : RelationalTransaction
@@ -29,8 +36,9 @@ public TestRelationalTransaction(
IRelationalConnection connection,
DbTransaction transaction,
IDiagnosticsLogger logger,
- bool transactionOwned)
- : base(connection, transaction, new Guid(), logger, transactionOwned)
+ bool transactionOwned,
+ ISqlGenerationHelper sqlGenerationHelper)
+ : base(connection, transaction, new Guid(), logger, transactionOwned, sqlGenerationHelper)
{
_testConnection = (TestSqlServerConnection)connection;
}
@@ -86,14 +94,6 @@ public override async Task CommitAsync(CancellationToken cancellationToken = def
public override bool SupportsSavepoints
=> true;
- ///
- protected override string GetCreateSavepointSql(string name)
- => "SAVE TRANSACTION " + name;
-
- ///
- protected override string GetRollbackToSavepointSql(string name)
- => "ROLLBACK TRANSACTION " + name;
-
///
public override void ReleaseSavepoint(string name) { }
diff --git a/test/EFCore.SqlServer.FunctionalTests/TransactionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/TransactionSqlServerTest.cs
index fce8f6710fe..a3360e8da1b 100644
--- a/test/EFCore.SqlServer.FunctionalTests/TransactionSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/TransactionSqlServerTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -14,6 +15,10 @@ public TransactionSqlServerTest(TransactionSqlServerFixture fixture)
{
}
+ // Savepoints cannot be released in SQL Server
+ public override Task Savepoint_can_be_released(bool async)
+ => Task.CompletedTask;
+
protected override bool SnapshotSupported
=> true;
diff --git a/test/EFCore.SqlServer.Tests/SqlServerConnectionTest.cs b/test/EFCore.SqlServer.Tests/SqlServerConnectionTest.cs
index 312a228d2a8..dc2c5bec8f8 100644
--- a/test/EFCore.SqlServer.Tests/SqlServerConnectionTest.cs
+++ b/test/EFCore.SqlServer.Tests/SqlServerConnectionTest.cs
@@ -81,7 +81,10 @@ public static RelationalConnectionDependencies CreateDependencies(DbContextOptio
new SqlServerLoggingDefinitions(),
new NullDbContextLogger()),
new NamedConnectionStringResolver(options),
- new RelationalTransactionFactory(new RelationalTransactionFactoryDependencies()),
+ new RelationalTransactionFactory(
+ new RelationalTransactionFactoryDependencies(
+ new RelationalSqlGenerationHelper(
+ new RelationalSqlGenerationHelperDependencies()))),
new CurrentDbContext(new FakeDbContext()));
}