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())); }