Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

API review: add interception for DbCommand creation #16716

Merged
merged 1 commit into from
Jul 25, 2019
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
78 changes: 78 additions & 0 deletions src/EFCore.Relational/Diagnostics/CommandCorrelatedEventData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Data.Common;
using System.Diagnostics;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Diagnostics
{
/// <summary>
/// The <see cref="DiagnosticSource" /> event payload for events correlated with a <see cref="DbCommand"/>.
/// </summary>
public class CommandCorrelatedEventData : DbContextEventData
{
/// <summary>
/// Constructs the event payload.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connection"> The <see cref="DbConnection"/> being used. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="executeMethod"> The <see cref="DbCommand" /> method. </param>
/// <param name="commandId"> A correlation ID that identifies the <see cref="DbCommand" /> instance being used. </param>
/// <param name="connectionId"> A correlation ID that identifies the <see cref="DbConnection" /> instance being used. </param>
/// <param name="async"> Indicates whether or not the command was executed asynchronously. </param>
/// <param name="startTime"> The start time of this event. </param>
public CommandCorrelatedEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[CanBeNull] DbContext context,
DbCommandMethod executeMethod,
Guid commandId,
Guid connectionId,
bool async,
DateTimeOffset startTime)
: base(eventDefinition, messageGenerator, context)
{
Connection = connection;
CommandId = commandId;
ConnectionId = connectionId;
ExecuteMethod = executeMethod;
IsAsync = async;
StartTime = startTime;
}

/// <summary>
/// The <see cref="DbConnection" />.
/// </summary>
public virtual DbConnection Connection { get; }

/// <summary>
/// A correlation ID that identifies the <see cref="DbCommand" /> instance being used.
/// </summary>
public virtual Guid CommandId { get; }

/// <summary>
/// A correlation ID that identifies the <see cref="DbConnection" /> instance being used.
/// </summary>
public virtual Guid ConnectionId { get; }

/// <summary>
/// The <see cref="DbCommandMethod" /> method.
/// </summary>
public virtual DbCommandMethod ExecuteMethod { get; }

/// <summary>
/// Indicates whether or not the operation is being executed asynchronously.
/// </summary>
public virtual bool IsAsync { get; }

/// <summary>
/// The start time of this event.
/// </summary>
public virtual DateTimeOffset StartTime { get; }
}
}
3 changes: 3 additions & 0 deletions src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class CommandEndEventData : CommandEventData
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connection"> The <see cref="DbConnection"/> being used. </param>
/// <param name="command"> The <see cref="DbCommand" />. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="executeMethod"> The <see cref="DbCommand" /> method. </param>
Expand All @@ -31,6 +32,7 @@ public class CommandEndEventData : CommandEventData
public CommandEndEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[NotNull] DbCommand command,
[CanBeNull] DbContext context,
DbCommandMethod executeMethod,
Expand All @@ -43,6 +45,7 @@ public CommandEndEventData(
: base(
eventDefinition,
messageGenerator,
connection,
command,
context,
executeMethod,
Expand Down
3 changes: 3 additions & 0 deletions src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CommandErrorEventData : CommandEndEventData, IErrorEventData
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connection"> The <see cref="DbConnection"/> being used. </param>
/// <param name="command"> The <see cref="DbCommand" /> that was executing when it failed. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="executeMethod"> The <see cref="DbCommand" /> method that was used to execute the command. </param>
Expand All @@ -31,6 +32,7 @@ public class CommandErrorEventData : CommandEndEventData, IErrorEventData
public CommandErrorEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[NotNull] DbCommand command,
[CanBeNull] DbContext context,
DbCommandMethod executeMethod,
Expand All @@ -44,6 +46,7 @@ public CommandErrorEventData(
: base(
eventDefinition,
messageGenerator,
connection,
command,
context,
executeMethod,
Expand Down
45 changes: 13 additions & 32 deletions src/EFCore.Relational/Diagnostics/CommandEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// The <see cref="DiagnosticSource" /> event payload for
/// <see cref="RelationalEventId" /> command events.
/// </summary>
public class CommandEventData : DbContextEventData
public class CommandEventData : CommandCorrelatedEventData
{
/// <summary>
/// Constructs the event payload.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connection"> The <see cref="DbConnection"/> being used. </param>
/// <param name="command"> The <see cref="DbCommand" />. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="executeMethod"> The <see cref="DbCommand" /> method. </param>
Expand All @@ -30,6 +31,7 @@ public class CommandEventData : DbContextEventData
public CommandEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[NotNull] DbCommand command,
[CanBeNull] DbContext context,
DbCommandMethod executeMethod,
Expand All @@ -38,50 +40,29 @@ public CommandEventData(
bool async,
bool logParameterValues,
DateTimeOffset startTime)
: base(eventDefinition, messageGenerator, context)
: base(
eventDefinition,
messageGenerator,
connection,
context,
executeMethod,
commandId,
connectionId,
async,
startTime)
{
Command = command;
CommandId = commandId;
ConnectionId = connectionId;
ExecuteMethod = executeMethod;
IsAsync = async;
LogParameterValues = logParameterValues;
StartTime = startTime;
}

/// <summary>
/// The <see cref="DbCommand" />.
/// </summary>
public virtual DbCommand Command { get; }

/// <summary>
/// A correlation ID that identifies the <see cref="DbCommand" /> instance being used.
/// </summary>
public virtual Guid CommandId { get; }

/// <summary>
/// A correlation ID that identifies the <see cref="DbConnection" /> instance being used.
/// </summary>
public virtual Guid ConnectionId { get; }

/// <summary>
/// The <see cref="DbCommand" /> method.
/// </summary>
public virtual DbCommandMethod ExecuteMethod { get; }

/// <summary>
/// Indicates whether or not the operation is being executed asynchronously.
/// </summary>
public virtual bool IsAsync { get; }

/// <summary>
/// Indicates whether or not the application allows logging of parameter values.
/// </summary>
public virtual bool LogParameterValues { get; }

/// <summary>
/// The start time of this event.
/// </summary>
public virtual DateTimeOffset StartTime { get; }
}
}
3 changes: 3 additions & 0 deletions src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CommandExecutedEventData : CommandEndEventData
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connection"> The <see cref="DbConnection"/> being used. </param>
/// <param name="command"> The <see cref="DbCommand" /> that was executing when it failed. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="executeMethod"> The <see cref="DbCommand" /> method that was used to execute the command. </param>
Expand All @@ -31,6 +32,7 @@ public class CommandExecutedEventData : CommandEndEventData
public CommandExecutedEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[NotNull] DbCommand command,
[CanBeNull] DbContext context,
DbCommandMethod executeMethod,
Expand All @@ -44,6 +46,7 @@ public CommandExecutedEventData(
: base(
eventDefinition,
messageGenerator,
connection,
command,
context,
executeMethod,
Expand Down
49 changes: 45 additions & 4 deletions src/EFCore.Relational/Diagnostics/DbCommandInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,48 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// </summary>
public abstract class DbCommandInterceptor : IDbCommandInterceptor
{
/// <summary>
/// Called just before EF intends to call <see cref="DbConnection.CreateCommand"/>.
/// </summary>
/// <param name="eventData"> Contextual information about the command and execution. </param>
/// <param name="result">
/// Represents the current result if one exists.
/// This value will have <see cref="InterceptionResult{DbCommand}.HasResult"/> set to true if some previous
/// interceptor suppressed execution by calling <see cref="InterceptionResult{DbCommand}.SuppressWithResult"/>.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// If <see cref="InterceptionResult{DbCommand}.HasResult"/> is false, the EF will continue as normal.
/// If <see cref="InterceptionResult{DbCommand}.HasResult"/> is true, then EF will suppress the operation it
/// was about to perform and use <see cref="InterceptionResult{DbCommand}.Result"/> instead.
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
public virtual InterceptionResult<DbCommand> CommandCreating(CommandCorrelatedEventData eventData, InterceptionResult<DbCommand> result)
=> result;

/// <summary>
/// <para>
/// Called immediately after EF calls <see cref="DbConnection.CreateCommand"/>.
/// </para>
/// <para>
/// This method is still called if an interceptor suppressed creation of a command in <see cref="IDbCommandInterceptor.CommandCreating" />.
/// In this case, <paramref name="result" /> is the result returned by <see cref="IDbCommandInterceptor.CommandCreating" />.
/// </para>
/// </summary>
/// <param name="eventData"> Contextual information about the command and execution. </param>
/// <param name="result">
/// The result of the call to <see cref="DbConnection.CreateCommand" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The result that EF will use.
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
public virtual DbCommand CommandCreated(CommandEndEventData eventData, DbCommand result)
=> result;

/// <summary>
/// Called just before EF intends to call <see cref="DbCommand.ExecuteReader()" />.
/// </summary>
Expand All @@ -37,9 +79,7 @@ public virtual InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
return result;
}
=> result;

/// <summary>
/// Called just before EF intends to call <see cref="DbCommand.ExecuteScalar()" />.
Expand All @@ -62,7 +102,8 @@ public virtual InterceptionResult<DbDataReader> ReaderExecuting(
public virtual InterceptionResult<object> ScalarExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<object> result) => result;
InterceptionResult<object> result)
=> result;

/// <summary>
/// Called just before EF intends to call <see cref="DbCommand.ExecuteNonQuery()" />.
Expand Down
44 changes: 44 additions & 0 deletions src/EFCore.Relational/Diagnostics/IDbCommandInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,50 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// </summary>
public interface IDbCommandInterceptor : IInterceptor
{
/// <summary>
/// Called just before EF intends to call <see cref="DbConnection.CreateCommand"/>.
/// </summary>
/// <param name="eventData"> Contextual information about the command and execution. </param>
/// <param name="result">
/// Represents the current result if one exists.
/// This value will have <see cref="InterceptionResult{DbCommand}.HasResult"/> set to true if some previous
/// interceptor suppressed execution by calling <see cref="InterceptionResult{DbCommand}.SuppressWithResult"/>.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// If <see cref="InterceptionResult{DbCommand}.HasResult"/> is false, the EF will continue as normal.
/// If <see cref="InterceptionResult{DbCommand}.HasResult"/> is true, then EF will suppress the operation it
/// was about to perform and use <see cref="InterceptionResult{DbCommand}.Result"/> instead.
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
InterceptionResult<DbCommand> CommandCreating(
[NotNull] CommandCorrelatedEventData eventData,
InterceptionResult<DbCommand> result);

/// <summary>
/// <para>
/// Called immediately after EF calls <see cref="DbConnection.CreateCommand"/>.
/// </para>
/// <para>
/// This method is still called if an interceptor suppressed creation of a command in <see cref="CommandCreating" />.
/// In this case, <paramref name="result" /> is the result returned by <see cref="CommandCreating" />.
/// </para>
/// </summary>
/// <param name="eventData"> Contextual information about the command and execution. </param>
/// <param name="result">
/// The result of the call to <see cref="DbConnection.CreateCommand" />.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The result that EF will use.
/// A normal implementation of this method for any interceptor that is not attempting to change the result
/// is to return the <paramref name="result" /> value passed in.
/// </returns>
DbCommand CommandCreated(
[NotNull] CommandEndEventData eventData,
[NotNull] DbCommand result);

/// <summary>
/// Called just before EF intends to call <see cref="DbCommand.ExecuteReader()" />.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ public CompositeDbCommandInterceptor([NotNull] IEnumerable<IDbCommandInterceptor
_interceptors = interceptors.ToArray();
}

public InterceptionResult<DbCommand> CommandCreating(
CommandCorrelatedEventData eventData,
InterceptionResult<DbCommand> result)
{
for (var i = 0; i < _interceptors.Length; i++)
{
result = _interceptors[i].CommandCreating(eventData, result);
}

return result;
}

public DbCommand CommandCreated(
CommandEndEventData eventData,
DbCommand result)
{
for (var i = 0; i < _interceptors.Length; i++)
{
result = _interceptors[i].CommandCreated(eventData, result);
}

return result;
}

public InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
Expand Down
Loading