Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class RuntimeRelationalPropertyOverrides : AnnotatableBase, IRelationalPr
/// Initializes a new instance of the <see cref="RuntimeRelationalPropertyOverrides"/> class.
/// </summary>
/// <param name="property"> The property for which the overrides are applied. </param>
/// <param name="columnNameOverriden"> Whether the column name is overriden. </param>
/// <param name="columnNameOverriden"> Whether the column name is overridden. </param>
/// <param name="columnName"> The column name. </param>
public RuntimeRelationalPropertyOverrides(
RuntimeProperty property,
Expand Down
20 changes: 10 additions & 10 deletions src/EFCore.Relational/Storage/RelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public virtual void EnlistTransaction(Transaction? transaction)
}

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.EnlistTransaction" /> but can be overriden
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.EnlistTransaction" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
/// <param name="transaction"> The transaction to be used. </param>
Expand Down Expand Up @@ -351,7 +351,7 @@ public virtual IDbContextTransaction BeginTransaction(IsolationLevel isolationLe
}

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.BeginDbTransaction" /> but can be overriden
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.BeginDbTransaction" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
/// <param name="isolationLevel"> The isolation level to use for the transaction. </param>
Expand Down Expand Up @@ -407,7 +407,7 @@ public virtual async Task<IDbContextTransaction> BeginTransactionAsync(

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.BeginDbTransactionAsync" /> but can be
/// overriden by providers to make a different call instead.
/// overridden by providers to make a different call instead.
/// </summary>
/// <param name="isolationLevel"> The isolation level to use for the transaction. </param>
/// <param name="cancellationToken"> A <see cref="CancellationToken" /> to observe while waiting for the task to complete. </param>
Expand Down Expand Up @@ -728,7 +728,7 @@ private void OpenInternal(bool errorsExpected)
}

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.Open" /> but can be overriden
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.Open" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
/// <param name="errorsExpected"> Indicates if the connection errors are expected and should be logged as debug message. </param>
Expand Down Expand Up @@ -782,7 +782,7 @@ await logger.ConnectionErrorAsync(
}

/// <summary>
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.OpenAsync" /> but can be overriden
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.OpenAsync" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
/// <param name="errorsExpected"> Indicates if the connection errors are expected and should be logged as debug message. </param>
Expand Down Expand Up @@ -899,7 +899,7 @@ public virtual bool Close()
}

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.Close" /> but can be overriden
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.Close" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
protected virtual void CloseDbConnection()
Expand Down Expand Up @@ -975,14 +975,14 @@ await Dependencies.ConnectionLogger.ConnectionErrorAsync(
}

/// <summary>
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.CloseAsync" /> but can be overriden
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.CloseAsync" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
protected virtual Task CloseDbConnectionAsync()
=> DbConnection.CloseAsync();

/// <summary>
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.State" /> but can be overriden
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.State" /> but can be overridden
/// by providers to make a different call instead.
/// </summary>
protected virtual ConnectionState DbConnectionState => DbConnection.State;
Expand Down Expand Up @@ -1080,14 +1080,14 @@ protected virtual async ValueTask ResetStateAsync(bool disposeDbConnection)
}

/// <summary>
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.Dispose" /> but can be overriden by
/// Template method that by default calls <see cref="M:System.Data.Common.DbConnection.Dispose" /> but can be overridden by
/// providers to make a different call instead.
/// </summary>
protected virtual void DisposeDbConnection()
=> DbConnection.Dispose();

/// <summary>
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.DisposeAsync" /> but can be overriden by
/// Template method that by default calls <see cref="System.Data.Common.DbConnection.DisposeAsync" /> but can be overridden by
/// providers to make a different call instead.
/// </summary>
protected virtual ValueTask DisposeDbConnectionAsync()
Expand Down
126 changes: 67 additions & 59 deletions src/EFCore/DbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ namespace Microsoft.EntityFrameworkCore
/// </para>
/// </remarks>
public class DbContext :
IDisposable,
IAsyncDisposable,
IInfrastructure<IServiceProvider>,
IDbContextDependencies,
IDbSetCache,
Expand Down Expand Up @@ -724,43 +722,46 @@ void IDbContextPoolable.ClearLease()
/// </summary>
[EntityFrameworkInternal]
void IDbContextPoolable.SetLease(DbContextLease lease)
{
SetLeaseInternal(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
/// 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]
Task IDbContextPoolable.SetLeaseAsync(DbContextLease lease, CancellationToken cancellationToken)
{
SetLeaseInternal(lease);

return Task.CompletedTask;
}

private void SetLeaseInternal(DbContextLease lease)
{
_lease = lease;
_disposed = false;
++_leaseCount;

if (_configurationSnapshot?.AutoDetectChangesEnabled != null)
{
Check.DebugAssert(
_configurationSnapshot.QueryTrackingBehavior.HasValue, "!configurationSnapshot.QueryTrackingBehavior.HasValue");
Check.DebugAssert(_configurationSnapshot.LazyLoadingEnabled.HasValue, "!configurationSnapshot.LazyLoadingEnabled.HasValue");
Check.DebugAssert(
_configurationSnapshot.CascadeDeleteTiming.HasValue, "!configurationSnapshot.CascadeDeleteTiming.HasValue");
Check.DebugAssert(
_configurationSnapshot.DeleteOrphansTiming.HasValue, "!configurationSnapshot.DeleteOrphansTiming.HasValue");

var changeTracker = ChangeTracker;
changeTracker.AutoDetectChangesEnabled = _configurationSnapshot.AutoDetectChangesEnabled.Value;
changeTracker.QueryTrackingBehavior = _configurationSnapshot.QueryTrackingBehavior.Value;
changeTracker.LazyLoadingEnabled = _configurationSnapshot.LazyLoadingEnabled.Value;
changeTracker.CascadeDeleteTiming = _configurationSnapshot.CascadeDeleteTiming.Value;
changeTracker.DeleteOrphansTiming = _configurationSnapshot.DeleteOrphansTiming.Value;
}
else
{
((IResettableService?)_changeTracker)?.ResetState();
}
Check.DebugAssert(_configurationSnapshot != null, "configurationSnapshot is null");

if (_database != null)
{
_database.AutoTransactionsEnabled
= _configurationSnapshot?.AutoTransactionsEnabled == null
|| _configurationSnapshot.AutoTransactionsEnabled.Value;
var changeTracker = ChangeTracker;
changeTracker.AutoDetectChangesEnabled = _configurationSnapshot.AutoDetectChangesEnabled;
changeTracker.QueryTrackingBehavior = _configurationSnapshot.QueryTrackingBehavior;
changeTracker.LazyLoadingEnabled = _configurationSnapshot.LazyLoadingEnabled;
changeTracker.CascadeDeleteTiming = _configurationSnapshot.CascadeDeleteTiming;
changeTracker.DeleteOrphansTiming = _configurationSnapshot.DeleteOrphansTiming;

_database.AutoSavepointsEnabled
= _configurationSnapshot?.AutoSavepointsEnabled == null
|| _configurationSnapshot.AutoSavepointsEnabled.Value;
}
var database = Database;
database.AutoTransactionsEnabled = _configurationSnapshot.AutoTransactionsEnabled;
database.AutoSavepointsEnabled = _configurationSnapshot.AutoSavepointsEnabled;

SavingChanges = _configurationSnapshot.SavingChanges;
SavedChanges = _configurationSnapshot.SavedChanges;
SaveChangesFailed = _configurationSnapshot.SaveChangesFailed;
}

/// <summary>
Expand All @@ -771,14 +772,21 @@ void IDbContextPoolable.SetLease(DbContextLease lease)
/// </summary>
[EntityFrameworkInternal]
void IDbContextPoolable.SnapshotConfiguration()
=> _configurationSnapshot = new DbContextPoolConfigurationSnapshot(
_changeTracker?.AutoDetectChangesEnabled,
_changeTracker?.QueryTrackingBehavior,
_database?.AutoTransactionsEnabled,
_database?.AutoSavepointsEnabled,
_changeTracker?.LazyLoadingEnabled,
_changeTracker?.CascadeDeleteTiming,
_changeTracker?.DeleteOrphansTiming);
{
var changeTracker = ChangeTracker;
var database = Database;
_configurationSnapshot = new DbContextPoolConfigurationSnapshot(
changeTracker.AutoDetectChangesEnabled,
changeTracker.QueryTrackingBehavior,
database.AutoTransactionsEnabled,
database.AutoSavepointsEnabled,
changeTracker.LazyLoadingEnabled,
changeTracker.CascadeDeleteTiming,
changeTracker.DeleteOrphansTiming,
SavingChanges,
SavedChanges,
SaveChangesFailed);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -794,8 +802,6 @@ void IResettableService.ResetState()
service.ResetState();
}

ClearEvents();

_disposed = true;
}

Expand All @@ -813,8 +819,6 @@ async Task IResettableService.ResetStateAsync(CancellationToken cancellationToke
await service.ResetStateAsync(cancellationToken).ConfigureAwait(false);
}

ClearEvents();

_disposed = true;
}

Expand Down Expand Up @@ -851,22 +855,22 @@ private IEnumerable<IResettableService> GetResettableServices()
/// </summary>
public virtual void Dispose()
{
if (DisposeSync())
var leaseActive = _lease.IsActive;
var contextDisposed = leaseActive && _lease.ContextDisposed();

if (DisposeSync(leaseActive, contextDisposed))
{
_serviceScope?.Dispose();
}
}

private bool DisposeSync()
private bool DisposeSync(bool leaseActive, bool contextDisposed)
{
if (_lease.IsActive)
if (leaseActive)
{
if (_lease.ContextDisposed())
if (contextDisposed)
{
_disposed = true;

ClearEvents();

_lease = DbContextLease.InactiveLease;
}
}
Expand All @@ -883,8 +887,11 @@ private bool DisposeSync()
_dbContextDependencies = null;
_changeTracker = null;
_database = null;
_configurationSnapshot = null;

ClearEvents();
SavingChanges = null;
SavedChanges = null;
SaveChangesFailed = null;

return true;
}
Expand All @@ -895,14 +902,15 @@ private bool DisposeSync()
/// <summary>
/// Releases the allocated resources for this context.
/// </summary>
public virtual ValueTask DisposeAsync()
=> DisposeSync() ? _serviceScope.DisposeAsyncIfAvailable() : default;

private void ClearEvents()
public virtual async ValueTask DisposeAsync()
{
SavingChanges = null;
SavedChanges = null;
SaveChangesFailed = null;
var leaseActive = _lease.IsActive;
var contextDisposed = leaseActive && await _lease.ContextDisposedAsync();

if (DisposeSync(leaseActive, contextDisposed))
{
await _serviceScope.DisposeAsyncIfAvailable();
}
}

/// <summary>
Expand Down
20 changes: 19 additions & 1 deletion src/EFCore/IDbContextFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// Defines a factory for creating <see cref="DbContext" /> instances.
/// </summary>
/// <typeparam name="TContext"> The <see cref="DbContext" /> type to create. </typeparam>
public interface IDbContextFactory<out TContext>
public interface IDbContextFactory<TContext>
where TContext : DbContext
{
/// <summary>
Expand All @@ -20,5 +24,19 @@ public interface IDbContextFactory<out TContext>
/// </summary>
/// <returns> A new context instance. </returns>
TContext CreateDbContext();

/// <summary>
/// <para>
/// Creates a new <see cref="DbContext" /> instance in an async context.
/// </para>
/// <para>
/// The caller is responsible for disposing the context; it will not be disposed by any dependency injection container.
/// </para>
/// </summary>
/// <param name="cancellationToken"> A <see cref="CancellationToken" /> to observe while waiting for the task to complete. </param>
/// <returns> A task containing the created context that represents the asynchronous operation. </returns>
/// <exception cref="OperationCanceledException"> If the <see cref="CancellationToken" /> is canceled. </exception>
Task<TContext> CreateDbContextAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(CreateDbContext());
}
}
16 changes: 15 additions & 1 deletion src/EFCore/Infrastructure/DatabaseFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
/// Instances of this class are typically obtained from <see cref="DbContext.Database" /> and it is not designed
/// to be directly constructed in your application code.
/// </summary>
public class DatabaseFacade : IInfrastructure<IServiceProvider>, IDatabaseFacadeDependenciesAccessor
public class DatabaseFacade : IInfrastructure<IServiceProvider>, IDatabaseFacadeDependenciesAccessor, IResettableService
{
private readonly DbContext _context;
private IDatabaseFacadeDependencies? _dependencies;
Expand Down Expand Up @@ -378,6 +378,20 @@ IDatabaseFacadeDependencies IDatabaseFacadeDependenciesAccessor.Dependencies
DbContext IDatabaseFacadeDependenciesAccessor.Context
=> _context;

/// <inheritdoc />
void IResettableService.ResetState()
{
AutoTransactionsEnabled = true;
AutoSavepointsEnabled = true;
}

Task IResettableService.ResetStateAsync(CancellationToken cancellationToken)
{
((IResettableService)this).ResetState();

return Task.CompletedTask;
}

#region Hidden System.Object members

/// <summary>
Expand Down
18 changes: 17 additions & 1 deletion src/EFCore/Infrastructure/PooledDbContextFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace Microsoft.EntityFrameworkCore.Infrastructure
Expand Down Expand Up @@ -49,6 +51,20 @@ public PooledDbContextFactory(DbContextOptions<TContext> options, int poolSize =

/// <inheritdoc />
public virtual TContext CreateDbContext()
=> (TContext)new DbContextLease(_pool, standalone: true).Context;
{
var lease = new DbContextLease(_pool, standalone: true);
lease.Context.SetLease(lease);

return (TContext)lease.Context;
}

/// <inheritdoc />
public virtual async Task<TContext> CreateDbContextAsync(CancellationToken cancellationToken = default)
{
var lease = new DbContextLease(_pool, standalone: true);
await lease.Context.SetLeaseAsync(lease, cancellationToken);

return (TContext)lease.Context;
}
}
}
Loading