Skip to content

Commit

Permalink
Generalize interceptors and add connection and transaction interception
Browse files Browse the repository at this point in the history
Registration of interceptors is now command and moved to core:
```C#
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlite("DataSource=Test.db")
        .AddInterceptors(new MyRelationalInterceptor(), new MyLoggingInterceptor());
}

```

Creating chains (essentially multi-dispatch) is handled automatically, with a mechanism for new interceptors to integrate with the functionality.

Also, added a lot of async paths that will be needed for new ADO.NET async methods. Part of #15917
  • Loading branch information
ajcvickers committed Jun 26, 2019
1 parent 74a1501 commit 3619684
Show file tree
Hide file tree
Showing 85 changed files with 6,388 additions and 1,664 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
Expand Down Expand Up @@ -56,7 +57,6 @@ public static IServiceCollection AddEntityFrameworkDesignTimeServices(
.AddSingleton<ProviderCodeGeneratorDependencies>()
.AddSingleton<TypeMappingSourceDependencies>()
.AddSingleton<RelationalTypeMappingSourceDependencies>()
.AddSingleton<InterceptorsDependencies>()
.AddSingleton<ValueConverterSelectorDependencies>()
.AddSingleton<ICandidateNamingService, CandidateNamingService>()
.AddSingleton<ICSharpDbContextGenerator, CSharpDbContextGenerator>()
Expand Down
20 changes: 20 additions & 0 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.InMemory.Storage.Internal
Expand Down Expand Up @@ -42,6 +44,24 @@ public virtual void Rollback()
{
}

/// <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>
public virtual Task CommitAsync(CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <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>
public virtual Task RollbackAsync(CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <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
Expand Down
350 changes: 0 additions & 350 deletions src/EFCore.Relational/Diagnostics/CompositeDbCommandInterceptor.cs

This file was deleted.

24 changes: 8 additions & 16 deletions src/EFCore.Relational/Diagnostics/ConnectionEndEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,22 @@ public class ConnectionEndEventData : ConnectionEventData
/// </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" />.
/// </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 operation is happening asynchronously.
/// </param>
/// <param name="startTime">
/// The start time of this event.
/// </param>
/// <param name="duration">
/// The duration this event.
/// </param>
/// <param name="connection"> The <see cref="DbConnection" />. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </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 operation is happening asynchronously. </param>
/// <param name="startTime"> The start time of this event. </param>
/// <param name="duration"> The duration this event. </param>
public ConnectionEndEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[CanBeNull] DbContext context,
Guid connectionId,
bool async,
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator, connection, connectionId, async, startTime)
: base(eventDefinition, messageGenerator, connection, context, connectionId, async, startTime)
=> Duration = duration;

/// <summary>
Expand Down
28 changes: 9 additions & 19 deletions src/EFCore.Relational/Diagnostics/ConnectionErrorEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,24 @@ public class ConnectionErrorEventData : ConnectionEndEventData, 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" />.
/// </param>
/// <param name="connectionId">
/// A correlation ID that identifies the <see cref="DbConnection" /> instance being used.
/// </param>
/// <param name="exception">
/// The exception that was thrown when the connection failed.
/// </param>
/// <param name="async">
/// Indicates whether or not the operation is happening asynchronously.
/// </param>
/// <param name="startTime">
/// The start time of this event.
/// </param>
/// <param name="duration">
/// The duration this event.
/// </param>
/// <param name="connection"> The <see cref="DbConnection" />. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </param>
/// <param name="connectionId">A correlation ID that identifies the <see cref="DbConnection" /> instance being used. </param>
/// <param name="exception"> The exception that was thrown when the connection failed. </param>
/// <param name="async"> Indicates whether or not the operation is happening asynchronously. </param>
/// <param name="startTime"> The start time of this event. </param>
/// <param name="duration"> The duration this event. </param>
public ConnectionErrorEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[CanBeNull] DbContext context,
Guid connectionId,
[NotNull] Exception exception,
bool async,
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator, connection, connectionId, async, startTime, duration)
: base(eventDefinition, messageGenerator, connection, context, connectionId, async, startTime, duration)
=> Exception = exception;

/// <summary>
Expand Down
22 changes: 8 additions & 14 deletions src/EFCore.Relational/Diagnostics/ConnectionEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,27 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// The <see cref="DiagnosticSource" /> event payload base class for
/// <see cref="RelationalEventId" /> connection events.
/// </summary>
public class ConnectionEventData : EventData
public class ConnectionEventData : 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" />.
/// </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 operation is happening asynchronously.
/// </param>
/// <param name="startTime">
/// The start time of this event.
/// </param>
/// <param name="connection"> The <see cref="DbConnection" />. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </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 operation is happening asynchronously. </param>
/// <param name="startTime"> The start time of this event. </param>
public ConnectionEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbConnection connection,
[CanBeNull] DbContext context,
Guid connectionId,
bool async,
DateTimeOffset startTime)
: base(eventDefinition, messageGenerator)
: base(eventDefinition, messageGenerator, context)
{
Connection = connection;
ConnectionId = connectionId;
Expand Down
38 changes: 12 additions & 26 deletions src/EFCore.Relational/Diagnostics/DataReaderDisposingEventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,35 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// <summary>
/// <see cref="DiagnosticSource" /> event payload for <see cref="RelationalEventId.DataReaderDisposing" />.
/// </summary>
public class DataReaderDisposingEventData : EventData
public class DataReaderDisposingEventData : DbContextEventData
{
/// <summary>
/// Constructs a <see cref="DiagnosticSource" /> event payload for <see cref="RelationalEventId.DataReaderDisposing" />.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="command">
/// The <see cref="DbCommand" /> that created the reader.
/// </param>
/// <param name="dataReader">
/// The <see cref="DbDataReader" /> that is being disposed.
/// </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="recordsAffected">
/// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
/// </param>
/// <param name="readCount">
/// Gets the number of read operations performed by this reader.
/// </param>
/// <param name="startTime">
/// The start time of this event.
/// </param>
/// <param name="duration">
/// The duration this event.
/// </param>
/// <param name="command"> The <see cref="DbCommand" /> that created the reader. </param>
/// <param name="dataReader"> The <see cref="DbDataReader" /> that is being disposed. </param>
/// <param name="context"> The <see cref="DbContext" /> currently being used, to null if not known. </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="recordsAffected">Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. </param>
/// <param name="readCount">Gets the number of read operations performed by this reader. </param>
/// <param name="startTime">The start time of this event. </param>
/// <param name="duration">The duration this event. </param>
public DataReaderDisposingEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] DbCommand command,
[NotNull] DbDataReader dataReader,
[CanBeNull] DbContext context,
Guid commandId,
Guid connectionId,
int recordsAffected,
int readCount,
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator)
: base(eventDefinition, messageGenerator, context)
{
Command = command;
DataReader = dataReader;
Expand Down
61 changes: 23 additions & 38 deletions src/EFCore.Relational/Diagnostics/DbCommandInterceptor.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// 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.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Diagnostics
{
Expand All @@ -19,40 +15,6 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
/// </summary>
public abstract class DbCommandInterceptor : IDbCommandInterceptor
{
/// <summary>
/// <para>
/// Chains the given <see cref="IDbCommandInterceptor" /> instances into a chain that will
/// call each in order.
/// </para>
/// <para>
/// If only a single interceptor is supplied, then it is simply returned.
/// </para>
/// </summary>
/// <param name="interceptors"> The interceptors to chain. </param>
/// <returns> An interceptor that calls each of the given interceptors in order. </returns>
public static IDbCommandInterceptor CreateChain([NotNull] IEnumerable<IDbCommandInterceptor> interceptors)
=> CreateChain(Check.NotNull(interceptors, nameof(interceptors)).ToArray());

/// <summary>
/// <para>
/// Chains the given <see cref="IDbCommandInterceptor" /> instances into a chain that will
/// call each in order.
/// </para>
/// <para>
/// If only a single interceptor is supplied, then it is simply returned.
/// </para>
/// </summary>
/// <param name="interceptors"> The interceptors to chain. </param>
/// <returns> An interceptor that calls each of the given interceptors in order. </returns>
public static IDbCommandInterceptor CreateChain([NotNull] params IDbCommandInterceptor[] interceptors)
{
Check.NotNull(interceptors, nameof(interceptors));

return interceptors.Length == 1
? interceptors[0]
: new CompositeDbCommandInterceptor(interceptors);
}

/// <summary>
/// Called just before EF intends to call <see cref="DbCommand.ExecuteReader()" />.
/// </summary>
Expand Down Expand Up @@ -389,5 +351,28 @@ public virtual Task CommandFailedAsync(
CommandErrorEventData eventData,
CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <summary>
/// Called when execution of a <see cref="DbDataReader"/> is about to be disposed. />.
/// </summary>
/// <param name="command"> The command. </param>
/// <param name="eventData"> Contextual information about the command and reader. </param>
/// <param name="result">
/// The current result, or null if no result yet exists.
/// This value will be non-null if some previous interceptor suppressed execution by returning a result from
/// its implementation of this method.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// If null, then EF will dispose the reader as normal.
/// If non-null, then disposing the reader is suppressed.
/// 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? DataReaderDisposing(
DbCommand command,
DataReaderDisposingEventData eventData,
InterceptionResult? result)
=> result;
}
}
Loading

0 comments on commit 3619684

Please sign in to comment.