diff --git a/src/EFCore.Relational/Update/SelectingUpdateSqlGenerator.cs b/src/EFCore.Relational/Update/SelectingUpdateSqlGenerator.cs
new file mode 100644
index 00000000000..441aaf51b7e
--- /dev/null
+++ b/src/EFCore.Relational/Update/SelectingUpdateSqlGenerator.cs
@@ -0,0 +1,308 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.Update;
+
+///
+///
+/// A base class for the service that is typically inherited from by database providers.
+/// The implementation uses a separate SELECT query after the update SQL to retrieve any database-generated values or for
+/// concurrency checking.
+///
+///
+/// This type is typically used by database providers; it is generally not used in application code.
+///
+///
+///
+///
+/// The service lifetime is . This means a single instance is used by many
+/// instances. The implementation must be thread-safe. This service cannot depend on services registered
+/// as .
+///
+///
+/// See Implementation of database providers and extensions for more
+/// information and examples.
+///
+///
+public abstract class SelectingUpdateSqlGenerator : UpdateSqlGenerator
+{
+ ///
+ /// Initializes a new instance of the this class.
+ ///
+ /// Parameter object containing dependencies for this service.
+ protected SelectingUpdateSqlGenerator(UpdateSqlGeneratorDependencies dependencies)
+ : base(dependencies)
+ {
+ }
+
+ ///
+ public override ResultSetMapping AppendInsertOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ => AppendInsertAndSelectOperations(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for inserting a row to the commands being built, via an INSERT followed by an optional SELECT to retrieve any
+ /// database-generated values.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected virtual ResultSetMapping AppendInsertAndSelectOperations(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var operations = command.ColumnModifications;
+
+ var writeOperations = operations.Where(o => o.IsWrite).ToList();
+ var readOperations = operations.Where(o => o.IsRead).ToList();
+
+ AppendInsertCommand(commandStringBuilder, name, schema, writeOperations, readOperations: Array.Empty());
+
+ if (readOperations.Count > 0)
+ {
+ var keyOperations = operations.Where(o => o.IsKey).ToList();
+
+ requiresTransaction = true;
+
+ return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations, commandPosition);
+ }
+
+ requiresTransaction = false;
+
+ return AppendSelectAffectedCountCommand(commandStringBuilder, name, schema, commandPosition);
+ }
+
+ ///
+ public override ResultSetMapping AppendUpdateOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ => AppendUpdateAndSelectOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for updating a row to the commands being built, via an UPDATE followed by a SELECT to retrieve any
+ /// database-generated values or for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected virtual ResultSetMapping AppendUpdateAndSelectOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var operations = command.ColumnModifications;
+
+ var writeOperations = operations.Where(o => o.IsWrite).ToList();
+ var conditionOperations = operations.Where(o => o.IsCondition).ToList();
+ var readOperations = operations.Where(o => o.IsRead).ToList();
+
+ AppendUpdateCommand(commandStringBuilder, name, schema, writeOperations, Array.Empty(), conditionOperations);
+
+ if (readOperations.Count > 0)
+ {
+ var keyOperations = operations.Where(o => o.IsKey).ToList();
+
+ requiresTransaction = true;
+
+ return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations, commandPosition);
+ }
+
+ requiresTransaction = false;
+
+ return AppendSelectAffectedCountCommand(commandStringBuilder, name, schema, commandPosition);
+ }
+
+ ///
+ public override ResultSetMapping AppendDeleteOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ => AppendDeleteAndSelectOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for updating a row to the commands being built, via a DELETE followed by a SELECT for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected virtual ResultSetMapping AppendDeleteAndSelectOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var operations = command.ColumnModifications;
+
+ var conditionOperations = operations.Where(o => o.IsCondition).ToList();
+
+ requiresTransaction = false;
+
+ AppendDeleteCommand(commandStringBuilder, name, schema, Array.Empty(), conditionOperations);
+
+ return AppendSelectAffectedCountCommand(commandStringBuilder, name, schema, commandPosition);
+ }
+
+ ///
+ /// Appends a SQL command for selecting affected data.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The name of the table.
+ /// The table schema, or to use the default schema.
+ /// The operations representing the data to be read.
+ /// The operations used to generate the WHERE clause for the select.
+ /// The ordinal of the command for which rows affected it being returned.
+ /// The for this command.
+ protected virtual ResultSetMapping AppendSelectAffectedCommand(
+ StringBuilder commandStringBuilder,
+ string name,
+ string? schema,
+ IReadOnlyList readOperations,
+ IReadOnlyList conditionOperations,
+ int commandPosition)
+ {
+ AppendSelectCommandHeader(commandStringBuilder, readOperations);
+ AppendFromClause(commandStringBuilder, name, schema);
+ AppendWhereAffectedClause(commandStringBuilder, conditionOperations);
+ commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator)
+ .AppendLine();
+
+ return ResultSetMapping.LastInResultSet;
+ }
+
+ ///
+ /// Appends a SQL fragment for starting a SELECT.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The operations representing the data to be read.
+ protected virtual void AppendSelectCommandHeader(
+ StringBuilder commandStringBuilder,
+ IReadOnlyList operations)
+ => commandStringBuilder
+ .Append("SELECT ")
+ .AppendJoin(
+ operations,
+ SqlGenerationHelper,
+ (sb, o, helper) => helper.DelimitIdentifier(sb, o.ColumnName));
+
+ ///
+ /// Appends a SQL fragment for starting a FROM clause.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The name of the table.
+ /// The table schema, or to use the default schema.
+ protected virtual void AppendFromClause(
+ StringBuilder commandStringBuilder,
+ string name,
+ string? schema)
+ {
+ commandStringBuilder
+ .AppendLine()
+ .Append("FROM ");
+ SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
+ }
+
+ ///
+ /// Appends a WHERE clause involving rows affected.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The operations from which to build the conditions.
+ protected virtual void AppendWhereAffectedClause(
+ StringBuilder commandStringBuilder,
+ IReadOnlyList operations)
+ {
+ commandStringBuilder
+ .AppendLine()
+ .Append("WHERE ");
+
+ AppendRowsAffectedWhereCondition(commandStringBuilder, 1);
+
+ if (operations.Count > 0)
+ {
+ commandStringBuilder
+ .Append(" AND ")
+ .AppendJoin(
+ operations, (sb, v) =>
+ {
+ if (v.IsKey)
+ {
+ if (!v.IsRead)
+ {
+ AppendWhereCondition(sb, v, v.UseOriginalValueParameter);
+ return true;
+ }
+ }
+
+ if (IsIdentityOperation(v))
+ {
+ AppendIdentityWhereCondition(sb, v);
+ return true;
+ }
+
+ return false;
+ }, " AND ");
+ }
+ }
+
+ ///
+ /// Returns a value indicating whether the given modification represents an auto-incrementing column.
+ ///
+ /// The column modification.
+ /// if the given modification represents an auto-incrementing column.
+ protected virtual bool IsIdentityOperation(IColumnModification modification)
+ => modification.IsKey && modification.IsRead;
+
+ ///
+ /// Appends a WHERE condition checking rows affected.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The expected number of rows affected.
+ protected abstract void AppendRowsAffectedWhereCondition(
+ StringBuilder commandStringBuilder,
+ int expectedRowsAffected);
+
+ ///
+ /// Appends a WHERE condition for the identity (i.e. key value) of the given column.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The column for which the condition is being generated.
+ protected abstract void AppendIdentityWhereCondition(
+ StringBuilder commandStringBuilder,
+ IColumnModification columnModification);
+
+ ///
+ /// Appends a SQL command for selecting the number of rows affected.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The name of the table.
+ /// The table schema, or to use the default schema.
+ /// The ordinal of the command for which rows affected it being returned.
+ /// The for this command.
+ protected abstract ResultSetMapping AppendSelectAffectedCountCommand(
+ StringBuilder commandStringBuilder,
+ string name,
+ string? schema,
+ int commandPosition);
+}
diff --git a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
index 0d18aff8a37..3c03360075d 100644
--- a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
+++ b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
@@ -7,8 +7,8 @@ namespace Microsoft.EntityFrameworkCore.Update;
///
///
-/// A base class for the service that is typically inherited from
-/// by database providers.
+/// A base class for the service that is typically inherited from by database providers.
+/// The implementation uses a SQL RETURNING clause to retrieve any database-generated values or for concurrency checking.
///
///
/// This type is typically used by database providers; it is generally not used in application code.
@@ -16,13 +16,13 @@ namespace Microsoft.EntityFrameworkCore.Update;
///
///
///
-/// The service lifetime is . This means a single instance
-/// is used by many instances. The implementation must be thread-safe.
-/// This service cannot depend on services registered as .
+/// The service lifetime is . This means a single instance is used by many
+/// instances. The implementation must be thread-safe. This service cannot depend on services registered
+/// as .
///
///
-/// See Implementation of database providers and extensions
-/// for more information and examples.
+/// See Implementation of database providers and extensions for more
+/// information and examples.
///
///
public abstract class UpdateSqlGenerator : IUpdateSqlGenerator
@@ -60,6 +60,22 @@ public virtual ResultSetMapping AppendInsertOperation(
IReadOnlyModificationCommand command,
int commandPosition,
out bool requiresTransaction)
+ => AppendInsertReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for inserting a row to the commands being built, via an INSERT containing an optional RETURNING clause to retrieve
+ /// any database-generated values.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ public virtual ResultSetMapping AppendInsertReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
{
var name = command.TableName;
var schema = command.Schema;
@@ -88,6 +104,22 @@ public virtual ResultSetMapping AppendUpdateOperation(
IReadOnlyModificationCommand command,
int commandPosition,
out bool requiresTransaction)
+ => AppendUpdateReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for updating a row to the commands being built, via an UPDATE containing an RETURNING clause to retrieve any
+ /// database-generated values or for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected virtual ResultSetMapping AppendUpdateReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
{
var name = command.TableName;
var schema = command.Schema;
@@ -119,6 +151,21 @@ public virtual ResultSetMapping AppendDeleteOperation(
IReadOnlyModificationCommand command,
int commandPosition,
out bool requiresTransaction)
+ => AppendDeleteReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for deleting a row to the commands being built, via a DELETE containing a RETURNING clause for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected virtual ResultSetMapping AppendDeleteReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
{
var name = command.TableName;
var schema = command.Schema;
@@ -202,33 +249,6 @@ protected virtual void AppendDeleteCommand(
commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
}
- ///
- /// Appends a SQL command for selecting affected data.
- ///
- /// The builder to which the SQL should be appended.
- /// The name of the table.
- /// The table schema, or to use the default schema.
- /// The operations representing the data to be read.
- /// The operations used to generate the WHERE clause for the select.
- /// The ordinal of the command for which rows affected it being returned.
- /// The for this command.
- protected virtual ResultSetMapping AppendSelectAffectedCommand(
- StringBuilder commandStringBuilder,
- string name,
- string? schema,
- IReadOnlyList readOperations,
- IReadOnlyList conditionOperations,
- int commandPosition)
- {
- AppendSelectCommandHeader(commandStringBuilder, readOperations);
- AppendFromClause(commandStringBuilder, name, schema);
- AppendWhereAffectedClause(commandStringBuilder, conditionOperations);
- commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator)
- .AppendLine();
-
- return ResultSetMapping.LastInResultSet;
- }
-
///
/// Appends a SQL fragment for starting an INSERT.
///
@@ -307,38 +327,6 @@ protected virtual void AppendUpdateCommandHeader(
});
}
- ///
- /// Appends a SQL fragment for starting a SELECT.
- ///
- /// The builder to which the SQL should be appended.
- /// The operations representing the data to be read.
- protected virtual void AppendSelectCommandHeader(
- StringBuilder commandStringBuilder,
- IReadOnlyList operations)
- => commandStringBuilder
- .Append("SELECT ")
- .AppendJoin(
- operations,
- SqlGenerationHelper,
- (sb, o, helper) => helper.DelimitIdentifier(sb, o.ColumnName));
-
- ///
- /// Appends a SQL fragment for starting a FROM clause.
- ///
- /// The builder to which the SQL should be appended.
- /// The name of the table.
- /// The table schema, or to use the default schema.
- protected virtual void AppendFromClause(
- StringBuilder commandStringBuilder,
- string name,
- string? schema)
- {
- commandStringBuilder
- .AppendLine()
- .Append("FROM ");
- SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
- }
-
///
/// Appends a SQL fragment for a VALUES.
///
@@ -446,65 +434,6 @@ protected virtual void AppendWhereClause(
}
}
- ///
- /// Appends a WHERE clause involving rows affected.
- ///
- /// The builder to which the SQL should be appended.
- /// The operations from which to build the conditions.
- protected virtual void AppendWhereAffectedClause(
- StringBuilder commandStringBuilder,
- IReadOnlyList operations)
- {
- commandStringBuilder
- .AppendLine()
- .Append("WHERE ");
-
- AppendRowsAffectedWhereCondition(commandStringBuilder, 1);
-
- if (operations.Count > 0)
- {
- commandStringBuilder
- .Append(" AND ")
- .AppendJoin(
- operations, (sb, v) =>
- {
- if (v.IsKey)
- {
- if (!v.IsRead)
- {
- AppendWhereCondition(sb, v, v.UseOriginalValueParameter);
- return true;
- }
- }
-
- if (IsIdentityOperation(v))
- {
- AppendIdentityWhereCondition(sb, v);
- return true;
- }
-
- return false;
- }, " AND ");
- }
- }
-
- ///
- /// Returns a value indicating whether the given modification represents an auto-incrementing column.
- ///
- /// The column modification.
- /// if the given modification represents an auto-incrementing column.
- protected virtual bool IsIdentityOperation(IColumnModification modification)
- => modification.IsKey && modification.IsRead;
-
- ///
- /// Appends a WHERE condition checking rows affected.
- ///
- /// The builder to which the SQL should be appended.
- /// The expected number of rows affected.
- protected abstract void AppendRowsAffectedWhereCondition(
- StringBuilder commandStringBuilder,
- int expectedRowsAffected);
-
///
/// Appends a WHERE condition for the given column.
///
@@ -545,15 +474,6 @@ protected virtual void AppendWhereCondition(
}
}
- ///
- /// Appends a WHERE condition for the identity (i.e. key value) of the given column.
- ///
- /// The builder to which the SQL should be appended.
- /// The column for which the condition is being generated.
- protected abstract void AppendIdentityWhereCondition(
- StringBuilder commandStringBuilder,
- IColumnModification columnModification);
-
///
/// Appends SQL text that defines the start of a batch.
///
@@ -596,7 +516,15 @@ public virtual void AppendNextSequenceValueOperation(StringBuilder commandString
SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
}
- private static void AppendSqlLiteral(
+ ///
+ /// Appends the literal value for to the command being built by
+ /// .
+ ///
+ /// The builder to which the SQL fragment should be appended.
+ /// The column modification whose literal should get appended.
+ /// The table name of the column, used when an exception is thrown.
+ /// The schema of the column, used when an exception is thrown.
+ protected static void AppendSqlLiteral(
StringBuilder commandStringBuilder,
IColumnModification modification,
string? tableName,
diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
index c633fc85db5..c5c3624ee25 100644
--- a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Update.Internal;
/// 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.
///
-public class SqlServerUpdateSqlGenerator : UpdateSqlGenerator, ISqlServerUpdateSqlGenerator
+public class SqlServerUpdateSqlGenerator : SelectingUpdateSqlGenerator, ISqlServerUpdateSqlGenerator
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -49,7 +49,7 @@ public override ResultSetMapping AppendInsertOperation(
// (without INTO), which is also the default behavior, doesn't require a transaction and is the most efficient.
if (command.ColumnModifications.All(o => !o.IsRead) || !HasAnyTriggers(command))
{
- return base.AppendInsertOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+ return AppendInsertReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
}
// SQL Server doesn't allow INSERT ... OUTPUT on tables with triggers.
@@ -107,33 +107,9 @@ public override ResultSetMapping AppendUpdateOperation(
{
// We normally do a simple UPDATE with an OUTPUT clause (either for the generated columns, or for "1" for concurrency checking).
// However, if there are triggers defined, OUTPUT (without INTO) is not supported, so we do UPDATE+SELECT.
- if (!HasAnyTriggers(command))
- {
- return base.AppendUpdateOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
- }
-
- var name = command.TableName;
- var schema = command.Schema;
- var operations = command.ColumnModifications;
-
- var writeOperations = operations.Where(o => o.IsWrite).ToList();
- var conditionOperations = operations.Where(o => o.IsCondition).ToList();
- var readOperations = operations.Where(o => o.IsRead).ToList();
-
- AppendUpdateCommand(commandStringBuilder, name, schema, writeOperations, Array.Empty(), conditionOperations);
-
- if (readOperations.Count > 0)
- {
- var keyOperations = operations.Where(o => o.IsKey).ToList();
-
- requiresTransaction = true;
-
- return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations, commandPosition);
- }
-
- requiresTransaction = false;
-
- return AppendSelectAffectedCountCommand(commandStringBuilder, name, schema, commandPosition);
+ return HasAnyTriggers(command)
+ ? AppendUpdateAndSelectOperation(commandStringBuilder, command, commandPosition, out requiresTransaction)
+ : AppendUpdateReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
}
///
@@ -172,22 +148,9 @@ public override ResultSetMapping AppendDeleteOperation(
{
// We normally do a simple DELETE, with an OUTPUT clause emitting "1" for concurrency checking.
// However, if there are triggers defined, OUTPUT (without INTO) is not supported, so we do UPDATE+SELECT.
- if (!HasAnyTriggers(command))
- {
- return base.AppendDeleteOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
- }
-
- var name = command.TableName;
- var schema = command.Schema;
- var operations = command.ColumnModifications;
-
- var conditionOperations = operations.Where(o => o.IsCondition).ToList();
-
- requiresTransaction = false;
-
- AppendDeleteCommand(commandStringBuilder, name, schema, Array.Empty(), conditionOperations);
-
- return AppendSelectAffectedCountCommand(commandStringBuilder, name, schema, commandPosition);
+ return HasAnyTriggers(command)
+ ? AppendDeleteAndSelectOperation(commandStringBuilder, command, commandPosition, out requiresTransaction)
+ : AppendDeleteReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
}
///
@@ -352,35 +315,6 @@ public virtual ResultSetMapping AppendBulkInsertOperation(
out requiresTransaction);
}
- ///
- /// 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.
- ///
- protected virtual ResultSetMapping AppendInsertAndSelectOperations(
- StringBuilder commandStringBuilder,
- IReadOnlyModificationCommand command,
- int commandPosition,
- out bool requiresTransaction)
- {
- var name = command.TableName;
- var schema = command.Schema;
- var operations = command.ColumnModifications;
-
- var writeOperations = operations.Where(o => o.IsWrite).ToList();
- var readOperations = operations.Where(o => o.IsRead).ToList();
- var keyOperations = operations.Where(o => o.IsKey).ToList();
-
- Check.DebugAssert(readOperations.Count > 0, "AppendInsertAndSelectOperations called without any read operations");
-
- requiresTransaction = true;
-
- AppendInsertCommand(commandStringBuilder, name, schema, writeOperations, readOperations: Array.Empty());
-
- return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations, commandPosition);
- }
-
private ResultSetMapping AppendInsertMultipleRows(
StringBuilder commandStringBuilder,
IReadOnlyList modificationCommands,
@@ -845,7 +779,7 @@ private ResultSetMapping AppendSelectCommand(
/// 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.
///
- protected virtual ResultSetMapping AppendSelectAffectedCountCommand(
+ protected override ResultSetMapping AppendSelectAffectedCountCommand(
StringBuilder commandStringBuilder,
string name,
string? schema,
diff --git a/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs b/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs
index 5ea20d8b67a..59ad4c0dfed 100644
--- a/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs
+++ b/src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs
@@ -25,28 +25,6 @@ public SqliteUpdateSqlGenerator(UpdateSqlGeneratorDependencies dependencies)
{
}
- ///
- /// 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.
- ///
- protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification)
- {
- SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, "rowid");
- commandStringBuilder.Append(" = ")
- .Append("last_insert_rowid()");
- }
-
- ///
- /// 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.
- ///
- protected override void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected)
- => commandStringBuilder.Append("changes() = ").Append(expectedRowsAffected);
-
///
/// 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/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeSqlGenerator.cs b/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeSqlGenerator.cs
index 84e737066f4..975f725d120 100644
--- a/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeSqlGenerator.cs
+++ b/test/EFCore.Relational.Tests/TestUtilities/FakeProvider/FakeSqlGenerator.cs
@@ -50,14 +50,4 @@ public override void AppendBatchHeader(StringBuilder commandStringBuilder)
AppendBatchHeaderCalls++;
base.AppendBatchHeader(commandStringBuilder);
}
-
- protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification)
- => commandStringBuilder
- .Append(SqlGenerationHelper.DelimitIdentifier(columnModification.ColumnName))
- .Append(" = ")
- .Append("provider_specific_identity()");
-
- protected override void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected)
- => commandStringBuilder
- .Append("provider_specific_rowcount() = ").Append(expectedRowsAffected);
}