Skip to content

Commit

Permalink
Recycle various objects participating in query
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Feb 26, 2021
1 parent 416b457 commit 6a696a4
Show file tree
Hide file tree
Showing 18 changed files with 409 additions and 301 deletions.
27 changes: 22 additions & 5 deletions src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ public virtual DbCommand CreateDbCommand()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual string ToQueryString()
=> _relationalQueryContext.RelationalQueryStringFactory.Create(CreateDbCommand());
{
using var dbCommand = CreateDbCommand();
return _relationalQueryContext.RelationalQueryStringFactory.Create(dbCommand);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -160,6 +163,7 @@ private sealed class Enumerator : IEnumerator<T>
private readonly bool _detailedErrorsEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private int[]? _indexMap;

Expand Down Expand Up @@ -224,9 +228,12 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -245,8 +252,12 @@ private static bool InitializeReader(Enumerator enumerator)

public void Dispose()
{
_dataReader?.Dispose();
_dataReader = null;
if (_dataReader is not null)
{
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);
_dataReader?.Dispose();
_dataReader = null;
}
}

public void Reset()
Expand All @@ -265,6 +276,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
private readonly bool _detailedErrorsEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private int[]? _indexMap;

Expand Down Expand Up @@ -331,9 +343,12 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -356,6 +371,8 @@ public ValueTask DisposeAsync()
{
if (_dataReader != null)
{
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);

var dataReader = _dataReader;
_dataReader = null;

Expand Down
29 changes: 23 additions & 6 deletions src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ public virtual DbCommand CreateDbCommand()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual string ToQueryString()
=> _relationalQueryContext.RelationalQueryStringFactory.Create(CreateDbCommand());
{
using var dbCommand = CreateDbCommand();
return _relationalQueryContext.RelationalQueryStringFactory.Create(dbCommand);
}

private sealed class Enumerator : IEnumerator<T>
{
Expand All @@ -128,6 +131,7 @@ private sealed class Enumerator : IEnumerator<T>
private readonly bool _detailedErrorsEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private SingleQueryResultCoordinator? _resultCoordinator;

Expand Down Expand Up @@ -218,9 +222,12 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -239,8 +246,12 @@ private static bool InitializeReader(Enumerator enumerator)

public void Dispose()
{
_dataReader?.Dispose();
_dataReader = null;
if (_dataReader is not null)
{
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);
_dataReader.Dispose();
_dataReader = null;
}
}

public void Reset()
Expand All @@ -258,6 +269,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
private readonly bool _detailedErrorsEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private SingleQueryResultCoordinator? _resultCoordinator;

Expand Down Expand Up @@ -351,9 +363,12 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -374,8 +389,10 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator

public ValueTask DisposeAsync()
{
if (_dataReader != null)
if (_dataReader is not null)
{
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);

var dataReader = _dataReader;
_dataReader = null;

Expand Down
40 changes: 28 additions & 12 deletions src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ public virtual DbCommand CreateDbCommand()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual string ToQueryString()
=> $"{_relationalQueryContext.RelationalQueryStringFactory.Create(CreateDbCommand())}{Environment.NewLine}{Environment.NewLine}{RelationalStrings.SplitQueryString}";
{
using var dbCommand = CreateDbCommand();
return $"{_relationalQueryContext.RelationalQueryStringFactory.Create(dbCommand)}{Environment.NewLine}{Environment.NewLine}{RelationalStrings.SplitQueryString}";
}

private sealed class Enumerator : IEnumerator<T>
{
Expand All @@ -136,6 +139,7 @@ private sealed class Enumerator : IEnumerator<T>
private readonly bool _detailedErrorsEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private SplitQueryResultCoordinator? _resultCoordinator;
private IExecutionStrategy? _executionStrategy;
Expand Down Expand Up @@ -212,9 +216,12 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -233,20 +240,24 @@ private static bool InitializeReader(Enumerator enumerator)

public void Dispose()
{
_dataReader?.Dispose();
if (_resultCoordinator != null)
if (_dataReader is not null)
{
foreach (var dataReader in _resultCoordinator.DataReaders)
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);
_dataReader.Dispose();
if (_resultCoordinator != null)
{
dataReader?.DataReader.Dispose();
}
foreach (var dataReader in _resultCoordinator.DataReaders)
{
dataReader?.DataReader.Dispose();
}

_resultCoordinator.DataReaders.Clear();
_resultCoordinator.DataReaders.Clear();

_resultCoordinator = null;
}
_resultCoordinator = null;
}

_dataReader = null;
_dataReader = null;
}
}

public void Reset()
Expand All @@ -265,6 +276,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
private readonly bool _detailedErrorEnabled;
private readonly IConcurrencyDetector? _concurrencyDetector;

private IRelationalCommand? _relationalCommand;
private RelationalDataReader? _dataReader;
private SplitQueryResultCoordinator? _resultCoordinator;
private IExecutionStrategy? _executionStrategy;
Expand Down Expand Up @@ -348,9 +360,12 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommandCache.GetRelationalCommand(
var relationalCommandTemplate = enumerator._relationalCommandCache.GetRelationalCommand(
enumerator._relationalQueryContext.ParameterValues);

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFromTemplate(relationalCommandTemplate);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand All @@ -373,6 +388,7 @@ public async ValueTask DisposeAsync()
{
if (_dataReader != null)
{
_relationalQueryContext.Connection.ReturnCommand(_relationalCommand!);
await _dataReader.DisposeAsync().ConfigureAwait(false);
if (_resultCoordinator != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1478,14 +1478,14 @@ async Task<bool> InitializeReaderAsync(DbContext _, bool result, CancellationTok
var relationalCommand = relationalCommandCache.GetRelationalCommand(queryContext.ParameterValues);

dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
queryContext.Connection,
queryContext.ParameterValues,
relationalCommandCache.ReaderColumns,
queryContext.Context,
queryContext.CommandLogger,
detailedErrorsEnabled),
cancellationToken)
new RelationalCommandParameterObject(
queryContext.Connection,
queryContext.ParameterValues,
relationalCommandCache.ReaderColumns,
queryContext.Context,
queryContext.CommandLogger,
detailedErrorsEnabled),
cancellationToken)
.ConfigureAwait(false);

return result;
Expand Down
7 changes: 7 additions & 0 deletions src/EFCore.Relational/Storage/IRelationalCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;

#nullable enable
Expand Down Expand Up @@ -111,5 +112,11 @@ public interface IRelationalCommand
RelationalCommandParameterObject parameterObject,
Guid commandId,
DbCommandMethod commandMethod);

/// <summary>
/// Populates this command from the provided <paramref name="templateCommand"/>.
/// </summary>
/// <param name="templateCommand"> A template command from which the command text and parameters will be copied. </param>
void PopulateFromTemplate([NotNull] IRelationalCommand templateCommand);
}
}
11 changes: 11 additions & 0 deletions src/EFCore.Relational/Storage/IRelationalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ public interface IRelationalConnection : IRelationalTransactionManager, IDisposa
/// </returns>
Task<bool> CloseAsync();

/// <summary>
/// Rents a relational command that can be executed with this connection.
/// </summary>
/// <returns> A relational command that can be executed with this connection. </returns>
IRelationalCommand RentCommand();

/// <summary>
/// Returns a relational command to this connection, so that it can be reused in the future.
/// </summary>
void ReturnCommand([NotNull] IRelationalCommand command);

/// <summary>
/// Gets the current transaction.
/// </summary>
Expand Down
Loading

0 comments on commit 6a696a4

Please sign in to comment.