Skip to content

BulkUpdates: Add custom CommandSource and exception messages #28583

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
1 commit merged into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional to explicitly set a value only here? I don't think we need explicit enum values here in general, but if we do, shouldn't we do it for all values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added here so that if we accidentally remove the obsolete value, the enum doesn't change values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc: @AndriySvyryd - Thoughts?


/// <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