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

Add Add correlation ID for DbContext #16290

Merged
merged 1 commit into from
Jul 3, 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
41 changes: 30 additions & 11 deletions src/EFCore/DbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public class DbContext :
private bool _initializing;
private bool _disposed;

private readonly Guid _contextId = Guid.NewGuid();
private int _lease;

/// <summary>
/// <para>
/// Initializes a new instance of the <see cref="DbContext" /> class. The
Expand Down Expand Up @@ -137,6 +140,18 @@ public virtual IModel Model
[DebuggerStepThrough] get => DbContextDependencies.Model;
}

/// <summary>
/// <para>
/// A unique identifier for the context instance and pool lease, if any.
/// </para>
/// <para>
/// This identifier is primarily intended as a correlation ID for logging and debugging such
/// that it is easy to identify that multiple events are using the same or different context instances.
/// </para>
/// </summary>
public virtual DbContextId ContextId
=> new DbContextId(_contextId, _lease);

/// <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 Expand Up @@ -207,7 +222,8 @@ public virtual IModel Model
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
IDiagnosticsLogger<DbLoggerCategory.Infrastructure> IDbContextDependencies.InfrastructureLogger => DbContextDependencies.InfrastructureLogger;
IDiagnosticsLogger<DbLoggerCategory.Infrastructure> IDbContextDependencies.InfrastructureLogger
=> DbContextDependencies.InfrastructureLogger;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -478,7 +494,7 @@ private void TryDetectChanges(EntityEntry entry)
{
if (ChangeTracker.AutoDetectChangesEnabled)
{
entry.DetectChanges();
entry.DetectChanges();
}
}

Expand Down Expand Up @@ -584,6 +600,7 @@ public virtual async Task<int> SaveChangesAsync(
void IDbContextPoolable.SetPool(IDbContextPool contextPool)
{
_dbContextPool = contextPool;
_lease = 1;
}

/// <summary>
Expand All @@ -610,6 +627,7 @@ DbContextPoolConfigurationSnapshot IDbContextPoolable.SnapshotConfiguration()
void IDbContextPoolable.Resurrect(DbContextPoolConfigurationSnapshot configurationSnapshot)
{
_disposed = false;
++_lease;

if (configurationSnapshot.AutoDetectChangesEnabled != null)
{
Expand Down Expand Up @@ -904,7 +922,7 @@ public virtual async ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -947,7 +965,7 @@ public virtual EntityEntry<TEntity> Attach<TEntity>([NotNull] TEntity entity)
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1108,7 +1126,7 @@ public virtual async ValueTask<EntityEntry> AddAsync(
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1149,7 +1167,7 @@ public virtual EntityEntry Attach([NotNull] object entity)
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1280,7 +1298,7 @@ public virtual Task AddRangeAsync([NotNull] params object[] entities)
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1317,7 +1335,7 @@ public virtual void AttachRange([NotNull] params object[] entities)
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1435,7 +1453,7 @@ await SetEntityStateAsync(
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Unchanged" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1472,7 +1490,7 @@ public virtual void AttachRange([NotNull] IEnumerable<object> entities)
/// to anything other than the CLR default for the property type.
/// </para>
/// <para>
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified"/>.
/// For entity types without generated keys, the state set is always <see cref="EntityState.Modified" />.
/// </para>
/// <para>
/// Use <see cref="EntityEntry.State" /> to set the state of only a single entity.
Expand Down Expand Up @@ -1574,7 +1592,8 @@ public virtual ValueTask<object> FindAsync([NotNull] Type entityType, [CanBeNull
/// <param name="keyValues">The values of the primary key for the entity to be found.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>The entity found, or null.</returns>
public virtual ValueTask<object> FindAsync([NotNull] Type entityType, [CanBeNull] object[] keyValues, CancellationToken cancellationToken)
public virtual ValueTask<object> FindAsync(
[NotNull] Type entityType, [CanBeNull] object[] keyValues, CancellationToken cancellationToken)
{
CheckDisposed();

Expand Down
100 changes: 100 additions & 0 deletions src/EFCore/DbContextId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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.Globalization;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// <para>
/// A unique identifier for the context instance and pool lease, if any.
/// </para>
/// <para>
/// This identifier is primarily intended as a correlation ID for logging and debugging such
/// that it is easy to identify that multiple events are using the same or different context instances.
/// </para>
/// </summary>
public readonly struct DbContextId
{
/// <summary>
/// Compares this ID to another ID to see if they represent the same leased context.
/// </summary>
/// <param name="other"> The other ID. </param>
/// <returns> True if they represent the same leased context; false otherwise. </returns>
public bool Equals(DbContextId other)
=> InstanceId == other.InstanceId
&& Lease == other.Lease;

/// <summary>
/// Compares this ID to another ID to see if they represent the same leased context.
/// </summary>
/// <param name="obj"> The other ID. </param>
/// <returns> True if they represent the same leased context; false otherwise. </returns>
public override bool Equals(object obj)
=> obj is DbContextId other && Equals(other);

/// <summary>
/// A hash code for this ID.
/// </summary>
/// <returns> The hash code. </returns>
public override int GetHashCode()
=> HashCode.Combine(InstanceId, Lease);

/// <summary>
/// Compares one ID to another ID to see if they represent the same leased context.
/// </summary>
/// <param name="left"> The first ID. </param>
/// <param name="right"> The second ID. </param>
/// <returns> True if they represent the same leased context; false otherwise. </returns>
public static bool operator ==(DbContextId left, DbContextId right) => left.Equals(right);

/// <summary>
/// Compares one ID to another ID to see if they represent different leased contexts.
/// </summary>
/// <param name="left"> The first ID. </param>
/// <param name="right"> The second ID. </param>
/// <returns> True if they represent different leased contexts; false otherwise. </returns>
public static bool operator !=(DbContextId left, DbContextId right) => !left.Equals(right);

/// <summary>
/// Creates a new <see cref="DbContextId" /> with the given <see cref="InstanceId" /> and lease number.
/// </summary>
/// <param name="id"> A unique identifier for the <see cref="DbContext" /> being used. </param>
/// <param name="lease"> A number indicating whether this is the first, second, third, etc. lease of this instance. </param>
public DbContextId(Guid id, int lease)
{
InstanceId = id;
Lease = lease;
}

/// <summary>
/// <para>
/// A unique identifier for the <see cref="DbContext" /> being used.
/// </para>
/// <para>
/// When context pooling is being used, then this ID must be combined with
/// the <see cref="Lease" /> in order to get a unique ID for the effective instance being used.
/// </para>
/// </summary>
public Guid InstanceId { get; }

/// <summary>
/// <para>
/// A number that is incremented each time this particular <see cref="DbContext" /> instance is leased
/// from the context pool.
/// </para>
/// <para>
/// Will be zero if context pooling is not being used.
/// </para>
/// </summary>
public int Lease { get; }

/// <summary>Returns the fully qualified type name of this instance.</summary>
/// <returns>The fully qualified type name.</returns>
public override string ToString()
{
return InstanceId + ":" + Lease.ToString(CultureInfo.InvariantCulture);
}
}
}
Loading