Skip to content

Commit

Permalink
Some docs/exception messages for bulk delete (#28523)
Browse files Browse the repository at this point in the history
* Some docs/exception messages for bulk delete

* Address review comments
  • Loading branch information
roji authored Jul 26, 2022
1 parent 4554d54 commit 2c6ba86
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 14 deletions.
32 changes: 28 additions & 4 deletions src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,19 +236,43 @@ internal static readonly MethodInfo AsSplitQueryMethodInfo
#region BulkDelete

/// <summary>
/// TBD
/// Deletes all entity instances which match the LINQ query from the database.
/// </summary>
/// <remarks>
/// <para>
/// This operation executes immediately against the database, rather than being deferred until
/// <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
/// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
/// to reflect the changes.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
/// for more information and examples.
/// </para>
/// </remarks>
/// <param name="source">The source query.</param>
/// <returns> TBD </returns>
/// <returns>The total number of entity instances deleted from the database.</returns>
public static int BulkDelete<TSource>(this IQueryable<TSource> source)
=> source.Provider.Execute<int>(Expression.Call(BulkDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression));

/// <summary>
/// TBD
/// Asynchronously deletes all entity instances which match the LINQ query from the database.
/// </summary>
/// <remarks>
/// <para>
/// This operation executes immediately against the database, rather than being deferred until
/// <see cref="DbContext.SaveChanges()" /> is called. It also does not interact with the EF change tracker in any way:
/// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
/// to reflect the changes.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-bulk-operations">Executing bulk operations with EF Core</see>
/// for more information and examples.
/// </para>
/// </remarks>
/// <param name="source">The source query.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> TBD </returns>
/// <returns>The total number of entity instances deleted from the database.</returns>
public static Task<int> BulkDeleteAsync<TSource>(this IQueryable<TSource> source, CancellationToken cancellationToken = default)
=> source.Provider is IAsyncQueryProvider provider
? provider.ExecuteAsync<Task<int>>(
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@
<data name="BadSequenceType" xml:space="preserve">
<value>Invalid type for sequence. Valid types are long (the default), int, short, byte and decimal.</value>
</data>
<data name="BulkOperationOnKeylessEntityTypeWithUnsupportedOperator" xml:space="preserve">
<value>The bulk operation cannot be performed on keyless entity type '{entityType}', since it contains an operator not natively supported by the database provider.</value>
</data>
<data name="BulkOperationWithUnsupportedOperatorInSqlGeneration" xml:space="preserve">
<value>The bulk operation contains a select expression feature that isn't supported in the query SQL generator, but has been declared as supported in RelationalQueryableMethodTranslatingExpressionVisitor.</value>
</data>
<data name="CannotChangeWhenOpen" xml:space="preserve">
<value>The instance of DbConnection is currently in use. The connection can only be changed when the existing connection is not being used.</value>
</data>
Expand Down
3 changes: 1 addition & 2 deletions src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
else
{
// TODO: Exception message
throw new InvalidOperationException();
throw new InvalidOperationException(RelationalStrings.BulkOperationWithUnsupportedOperatorInSqlGeneration);
}

return deleteExpression;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ private sealed class SelectExpressionMutableVerifyingExpressionVisitor : Express
{
switch (expression)
{
case SelectExpression selectExpression
when selectExpression.IsMutable():
case SelectExpression selectExpression when selectExpression.IsMutable():
throw new InvalidDataException(selectExpression.Print());

case ShapedQueryExpression shapedQueryExpression:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1029,8 +1029,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
else
{
// TODO: Exception message
throw new InvalidOperationException("specific message about keyless");
throw new InvalidOperationException(
RelationalStrings.BulkOperationOnKeylessEntityTypeWithUnsupportedOperator(entityType.DisplayName()));
}
}
}
Expand All @@ -1042,6 +1042,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
/// <summary>
/// Validates if the current select expression can be used for bulk delete operation or it requires to be pushed into a subquery.
/// </summary>
/// <remarks>
/// <para>
/// By default, only single-table select expressions are supported, and only with a predicate.
/// </para>
/// <para>
/// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
/// When doing this, VisitDelete must also be overridden in the provider's QuerySqlGenerator to add SQL generation support for
/// the feature.
/// </para>
/// </remarks>
/// <param name="selectExpression">The select expression to validate.</param>
/// <param name="entityShaperExpression">The entity shaper expression on which delete operation is being applied.</param>
/// <param name="tableExpression">The table expression from which rows are being deleted.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ protected override Expression VisitExtension(Expression extensionExpression)
: base.VisitExtension(extensionExpression);

/// <summary>
/// asdagv
/// Visits the given <paramref name="nonQueryExpression" />, returning an expression that when compiled, can execute the non-
/// query operation against the database.
/// </summary>
/// <param name="nonQueryExpression">asdasfdsa</param>
/// <returns>asdasd</returns>
/// <param name="nonQueryExpression">The expression to be compiled.</param>
/// <returns>An expression which executes a non-query operation.</returns>
protected virtual Expression VisitNonQuery(NonQueryExpression nonQueryExpression)
{
var relationalCommandCache = new RelationalCommandCache(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
else
{
throw new NotSupportedException();
throw new InvalidOperationException(RelationalStrings.BulkOperationWithUnsupportedOperatorInSqlGeneration);
}

return deleteExpression;
Expand Down

0 comments on commit 2c6ba86

Please sign in to comment.