Skip to content

Commit

Permalink
BulkUpdates: Add custom CommandSource and exception messages (#28583)
Browse files Browse the repository at this point in the history
Resolves #28568
  • Loading branch information
smitpatel authored Aug 5, 2022
1 parent a65b456 commit 185cdcc
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 30 deletions.
13 changes: 12 additions & 1 deletion src/EFCore.Relational/Diagnostics/CommandSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,16 @@ public enum CommandSource
/// <summary>
/// The command was generated as part of a bulk update.
/// </summary>
BulkUpdate
[Obsolete("Use ExecuteDelete or ExecuteUpdate instead.")]
BulkUpdate,

/// <summary>
/// The command was generated as part of an 'ExecuteDelete' operation.
/// </summary>
ExecuteDelete = 9,

/// <summary>
/// The command was generated as part of an 'ExecuteUpdate' operation.
/// </summary>
ExecuteUpdate,
}
34 changes: 31 additions & 3 deletions src/EFCore.Relational/Diagnostics/RelationalEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ private enum Id
Obsolete_QueryPossibleExceptionWithAggregateOperatorWarning,
Obsolete_ValueConversionSqlLiteralWarning,
MultipleCollectionIncludeWarning,
BulkOperationFailed,
NonQueryOperationFailed,
ExecuteDeleteFailed,
ExecuteUpdateFailed,

// Model validation events
ModelValidationKeyDefaultValueWarning = CoreEventId.RelationalBaseId + 600,
Expand Down Expand Up @@ -743,7 +745,7 @@ private static EventId MakeQueryId(Id id)
public static readonly EventId MultipleCollectionIncludeWarning = MakeQueryId(Id.MultipleCollectionIncludeWarning);

/// <summary>
/// An error occurred while executing a bulk operation.
/// An error occurred while executing a non-query operation.
/// </summary>
/// <remarks>
/// <para>
Expand All @@ -753,7 +755,33 @@ private static EventId MakeQueryId(Id id)
/// 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);
public static readonly EventId NonQueryOperationFailed = MakeQueryId(Id.NonQueryOperationFailed);

/// <summary>
/// An error occurred while executing an 'ExecuteDelete' 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 ExecuteDeleteFailed = MakeQueryId(Id.ExecuteDeleteFailed);

/// <summary>
/// An error occurred while executing an 'ExecuteUpdate' 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 ExecuteUpdateFailed = MakeQueryId(Id.ExecuteUpdateFailed);

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

Expand Down
90 changes: 85 additions & 5 deletions src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2313,17 +2313,17 @@ private static string QueryPossibleUnintendedUseOfEqualsWarning(EventDefinitionB
}

/// <summary>
/// Logs for the <see cref="RelationalEventId.BulkOperationFailed" /> event.
/// Logs for the <see cref="RelationalEventId.ExecuteDeleteFailed" /> 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(
public static void ExecuteDeleteFailed(
this IDiagnosticsLogger<DbLoggerCategory.Query> diagnostics,
Type contextType,
Exception exception)
{
var definition = RelationalResources.LogExceptionDuringBulkOperation(diagnostics);
var definition = RelationalResources.LogExceptionDuringExecuteDelete(diagnostics);

if (diagnostics.ShouldLog(definition))
{
Expand All @@ -2337,15 +2337,95 @@ public static void BulkOperationFailed(
{
var eventData = new DbContextTypeErrorEventData(
definition,
BulkOperationFailed,
ExecuteDeleteFailed,
contextType,
exception);

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

private static string BulkOperationFailed(EventDefinitionBase definition, EventData payload)
private static string ExecuteDeleteFailed(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.ExecuteUpdateFailed" /> 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 ExecuteUpdateFailed(
this IDiagnosticsLogger<DbLoggerCategory.Query> diagnostics,
Type contextType,
Exception exception)
{
var definition = RelationalResources.LogExceptionDuringExecuteUpdate(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,
ExecuteUpdateFailed,
contextType,
exception);

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

private static string ExecuteUpdateFailed(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.NonQueryOperationFailed" /> 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 NonQueryOperationFailed(
this IDiagnosticsLogger<DbLoggerCategory.Query> diagnostics,
Type contextType,
Exception exception)
{
var definition = RelationalResources.LogExceptionDuringNonQueryOperation(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,
NonQueryOperationFailed,
contextType,
exception);

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

private static string NonQueryOperationFailed(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<Type, string, Exception>)definition;
var p = (DbContextTypeErrorEventData)payload;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,5 +644,23 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase? LogExceptionDuringBulkOperation;
public EventDefinitionBase? LogExceptionDuringNonQueryOperation;

/// <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? LogExceptionDuringExecuteDelete;

/// <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? LogExceptionDuringExecuteUpdate;
}
66 changes: 58 additions & 8 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.

14 changes: 11 additions & 3 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -612,9 +612,17 @@
<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 name="LogExceptionDuringExecuteDelete" xml:space="preserve">
<value>An exception occurred while executing an 'ExecuteDelete' operation for context type '{contextType}'.{newline}{error}</value>
<comment>Error RelationalEventId.ExecuteDeleteFailed Type string Exception</comment>
</data>
<data name="LogExceptionDuringExecuteUpdate" xml:space="preserve">
<value>An exception occurred while executing an 'ExecuteUpdate' operation for context type '{contextType}'.{newline}{error}</value>
<comment>Error RelationalEventId.ExecuteUpdateFailed Type string Exception</comment>
</data>
<data name="LogExceptionDuringNonQueryOperation" xml:space="preserve">
<value>An exception occurred while executing a non-query operation for context type '{contextType}'.{newline}{error}</value>
<comment>Error RelationalEventId.NonQueryOperationFailed 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>
Expand Down
10 changes: 9 additions & 1 deletion src/EFCore.Relational/Query/NonQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ namespace Microsoft.EntityFrameworkCore.Query;
public class NonQueryExpression : Expression, IPrintableExpression
{
public NonQueryExpression(DeleteExpression deleteExpression)
: this(deleteExpression, CommandSource.ExecuteDelete)
{
DeleteExpression = deleteExpression;
}

public NonQueryExpression(DeleteExpression expression, CommandSource commandSource)
{
DeleteExpression = expression;
CommandSource = commandSource;
}

public virtual DeleteExpression DeleteExpression { get; }

public virtual CommandSource CommandSource { get; }

/// <inheritdoc />
public override Type Type => typeof(int);

Expand Down
Loading

0 comments on commit 185cdcc

Please sign in to comment.