Skip to content

Commit

Permalink
Implement ExecuteDelete
Browse files Browse the repository at this point in the history
Part of #795
  • Loading branch information
smitpatel committed Aug 1, 2022
1 parent a946ad5 commit 0dbba3d
Show file tree
Hide file tree
Showing 76 changed files with 3,909 additions and 60 deletions.
2 changes: 1 addition & 1 deletion eng/helix.proj
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@
<XUnitRuntimeTargetFramework>netcoreapp2.0</XUnitRuntimeTargetFramework>
<XUnitRunnerVersion>2.4.2-pre.9</XUnitRunnerVersion>
<XUnitArguments></XUnitArguments>
<XUnitWorkItemTimeout Condition = "'$(XUnitWorkItemTimeout)' == ''">01:00:00</XUnitWorkItemTimeout>
<XUnitWorkItemTimeout Condition = "'$(XUnitWorkItemTimeout)' == ''">02:00:00</XUnitWorkItemTimeout>
</PropertyGroup>
</Project>
14 changes: 14 additions & 0 deletions src/EFCore.Relational/Diagnostics/RelationalEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ private enum Id
Obsolete_QueryPossibleExceptionWithAggregateOperatorWarning,
Obsolete_ValueConversionSqlLiteralWarning,
MultipleCollectionIncludeWarning,
BulkOperationFailed,

// Model validation events
ModelValidationKeyDefaultValueWarning = CoreEventId.RelationalBaseId + 600,
Expand Down Expand Up @@ -741,6 +742,19 @@ private static EventId MakeQueryId(Id id)
/// </remarks>
public static readonly EventId MultipleCollectionIncludeWarning = MakeQueryId(Id.MultipleCollectionIncludeWarning);

/// <summary>
/// An error occurred while executing a bulk operation.
/// </summary>
/// <remarks>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// <para>
/// This event uses the <see cref="DbContextTypeErrorEventData" /> payload when used with a <see cref="DiagnosticSource" />.
/// </para>
/// </remarks>
public static readonly EventId BulkOperationFailed = MakeQueryId(Id.BulkOperationFailed);

private static readonly string _validationPrefix = DbLoggerCategory.Model.Validation.Name + ".";

private static EventId MakeValidationId(Id id)
Expand Down
40 changes: 40 additions & 0 deletions src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2312,6 +2312,46 @@ private static string QueryPossibleUnintendedUseOfEqualsWarning(EventDefinitionB
return d.GenerateMessage(p.Left.Print(), p.Right.Print());
}

/// <summary>
/// Logs for the <see cref="RelationalEventId.BulkOperationFailed" /> event.
/// </summary>
/// <param name="diagnostics">The diagnostics logger to use.</param>
/// <param name="contextType">The <see cref="DbContext" /> type being used.</param>
/// <param name="exception">The exception that caused this failure.</param>
public static void BulkOperationFailed(
this IDiagnosticsLogger<DbLoggerCategory.Query> diagnostics,
Type contextType,
Exception exception)
{
var definition = RelationalResources.LogExceptionDuringBulkOperation(diagnostics);

if (diagnostics.ShouldLog(definition))
{
definition.Log(
diagnostics,
contextType, Environment.NewLine, exception,
exception);
}

if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
{
var eventData = new DbContextTypeErrorEventData(
definition,
BulkOperationFailed,
contextType,
exception);

diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
}
}

private static string BulkOperationFailed(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<Type, string, Exception>)definition;
var p = (DbContextTypeErrorEventData)payload;
return d.GenerateMessage(p.ContextType, Environment.NewLine, p.Exception);
}

/// <summary>
/// Logs for the <see cref="RelationalEventId.MultipleCollectionIncludeWarning" /> event.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,4 +636,13 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase? LogColumnOrderIgnoredWarning;

/// <summary>
/// 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.
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase? LogExceptionDuringBulkOperation;
}
59 changes: 59 additions & 0 deletions src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static DbCommand CreateDbCommand(this IQueryable source)
throw new NotSupportedException(RelationalStrings.NoDbCommand);
}

#region FromSql

/// <summary>
/// Creates a LINQ query based on a raw SQL query.
/// </summary>
Expand Down Expand Up @@ -162,6 +164,10 @@ private static FromSqlQueryRootExpression GenerateFromSqlQueryRoot(
Expression.Constant(arguments));
}

#endregion

#region SplitQuery

/// <summary>
/// Returns a new query which is configured to load the collections in the query results in a single database query.
/// </summary>
Expand Down Expand Up @@ -224,4 +230,57 @@ public static IQueryable<TEntity> AsSplitQuery<TEntity>(

internal static readonly MethodInfo AsSplitQueryMethodInfo
= typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(AsSplitQuery))!;

#endregion

#region ExecuteDelete

/// <summary>
/// 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>The total number of entity instances deleted from the database.</returns>
public static int ExecuteDelete<TSource>(this IQueryable<TSource> source)
=> source.Provider.Execute<int>(Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression));

/// <summary>
/// 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>The total number of entity instances deleted from the database.</returns>
public static Task<int> ExecuteDeleteAsync<TSource>(this IQueryable<TSource> source, CancellationToken cancellationToken = default)
=> source.Provider is IAsyncQueryProvider provider
? provider.ExecuteAsync<Task<int>>(
Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression), cancellationToken)
: throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);

internal static readonly MethodInfo ExecuteDeleteMethodInfo
= typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteDelete))!;

#endregion
}
89 changes: 89 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.

28 changes: 28 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@
<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="BulkDeleteOnTableSplitting" xml:space="preserve">
<value>The operation '{operation}' is being applied on the table '{tableName}' which contains data for multiple entity types. Applying this delete operation will also delete data for other entity type(s), hence it is not supported.</value>
</data>
<data name="BulkOperationOnEntitySplitting" xml:space="preserve">
<value>The operation '{operation}' is being applied on entity type '{entityType}', which uses entity splitting. Bulk operations on entity types using entity splitting is not supported.</value>
</data>
<data name="BulkOperationOnKeylessEntityTypeWithUnsupportedOperator" xml:space="preserve">
<value>The operation '{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="BulkOperationOnNonEntityType" xml:space="preserve">
<value>The operation '{operation}' requires an entity type which corresponds to the database table to be modified. The current operation is being applied on a non-entity projection. Remove any projection to non-entity types.</value>
</data>
<data name="BulkOperationOnTPC" xml:space="preserve">
<value>The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPC mapping strategy and is not a leaf type. Bulk operations on entity types participating in TPC hierarchies is only supported for leaf types.</value>
</data>
<data name="BulkOperationOnTPT" xml:space="preserve">
<value>The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPT mapping strategy. Bulk operations on hierarchies mapped as TPT is not supported.</value>
</data>
<data name="BulkOperationWithUnsupportedOperatorInSqlGeneration" xml:space="preserve">
<value>The operation '{operation}' contains a select expression feature that isn't supported in the query SQL generator, but has been declared as supported by provider during translation phase. This is a bug in your EF Core provider, please file an issue.</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 Expand Up @@ -591,6 +612,10 @@
<value>The configured column orders for the table '{table}' contains duplicates. Ensure the specified column order values are distinct. Conflicting columns: {columns}</value>
<comment>Error RelationalEventId.DuplicateColumnOrders string string</comment>
</data>
<data name="LogExceptionDuringBulkOperation" xml:space="preserve">
<value>An exception occurred while executing a bulk operation for context type '{contextType}'.{newline}{error}</value>
<comment>Error RelationalEventId.BulkOperationFailed Type string Exception</comment>
</data>
<data name="LogExecutedCommand" xml:space="preserve">
<value>Executed DbCommand ({elapsed}ms) [Parameters=[{parameters}], CommandType='{commandType}', CommandTimeout='{commandTimeout}']{newLine}{commandText}</value>
<comment>Information RelationalEventId.CommandExecuted string string System.Data.CommandType int string string</comment>
Expand Down Expand Up @@ -792,6 +817,9 @@
<data name="NoneRelationalTypeMappingOnARelationalTypeMappingSource" xml:space="preserve">
<value>'FindMapping' was called on a 'RelationalTypeMappingSource' with a non-relational 'TypeMappingInfo'.</value>
</data>
<data name="NonQueryTranslationFailedWithDetails" xml:space="preserve">
<value>The LINQ expression '{expression}' could not be translated. Additional information: {details} See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.</value>
</data>
<data name="NonScalarFunctionCannotBeNullable" xml:space="preserve">
<value>Cannot set 'IsNullable' on DbFunction '{functionName}' since the function does not represent a scalar function.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public override bool Equals(object? obj)

public bool Equals(CommandCacheKey commandCacheKey)
{
// Intentionally reference equals
// Intentionally reference equal, don't check internal components
if (!ReferenceEquals(_queryExpression, commandCacheKey._queryExpression))
{
return false;
Expand Down
Loading

0 comments on commit 0dbba3d

Please sign in to comment.