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

[release/6.0-rc1] Fix #25225 by forcing usage of renting and populating commands when using relational command cache #25668

Merged
merged 3 commits into from
Aug 24, 2021
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
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Internal
{
/// <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 static class RelationCommandCacheExtensions
{
/// <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 static IRelationalCommand RentAndPopulateRelationalCommand(this RelationalCommandCache relationalCommandCache, RelationalQueryContext queryContext)
{
var relationalCommandTemplate = relationalCommandCache.GetRelationalCommandTemplate(queryContext.ParameterValues);
var relationalCommand = queryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
return relationalCommand;
}
}
}
17 changes: 5 additions & 12 deletions src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Query.Internal
Expand Down Expand Up @@ -98,7 +99,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommand(_relationalQueryContext.ParameterValues)
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -225,11 +226,7 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -340,12 +337,8 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
enumerator._relationalQueryContext.Connection,
Expand Down
14 changes: 7 additions & 7 deletions src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ public RelationalCommandCache(
/// 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 IRelationalCommand GetRelationalCommand(IReadOnlyDictionary<string, object?> parameters)
public virtual IRelationalCommandTemplate GetRelationalCommandTemplate(IReadOnlyDictionary<string, object?> parameters)
{
var cacheKey = new CommandCacheKey(_selectExpression, parameters);

if (_memoryCache.TryGetValue(cacheKey, out IRelationalCommand relationalCommand))
if (_memoryCache.TryGetValue(cacheKey, out IRelationalCommandTemplate relationalCommandTemplate))
{
return relationalCommand;
return relationalCommandTemplate;
}

// When multiple threads attempt to start processing the same query (program startup / thundering
Expand All @@ -80,19 +80,19 @@ public virtual IRelationalCommand GetRelationalCommand(IReadOnlyDictionary<strin
{
lock (compilationLock)
{
if (!_memoryCache.TryGetValue(cacheKey, out relationalCommand))
if (!_memoryCache.TryGetValue(cacheKey, out relationalCommandTemplate))
{
var selectExpression = _relationalParameterBasedSqlProcessor.Optimize(
_selectExpression, parameters, out var canCache);
relationalCommand = _querySqlGeneratorFactory.Create().GetCommand(selectExpression);
relationalCommandTemplate = _querySqlGeneratorFactory.Create().GetCommand(selectExpression);

if (canCache)
{
_memoryCache.Set(cacheKey, relationalCommand, new MemoryCacheEntryOptions { Size = 10 });
_memoryCache.Set(cacheKey, relationalCommandTemplate, new MemoryCacheEntryOptions { Size = 10 });
}
}

return relationalCommand;
return relationalCommandTemplate;
}
}
finally
Expand Down
15 changes: 4 additions & 11 deletions src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Query.Internal
Expand Down Expand Up @@ -94,7 +95,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommand(_relationalQueryContext.ParameterValues)
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -221,11 +222,7 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -369,11 +366,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
15 changes: 4 additions & 11 deletions src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Query.Internal
Expand Down Expand Up @@ -100,7 +101,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommand(_relationalQueryContext.ParameterValues)
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -220,11 +221,7 @@ private static bool InitializeReader(Enumerator enumerator)
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -371,11 +368,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
{
EntityFrameworkEventSource.Log.QueryExecuting();

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

var relationalCommand = enumerator._relationalCommand = enumerator._relationalQueryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = enumerator._relationalCommand = enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
Expand Down Expand Up @@ -1403,9 +1404,7 @@ static RelationalDataReader InitializeReader(
RelationalCommandCache relationalCommandCache,
bool detailedErrorsEnabled)
{
var relationalCommandTemplate = relationalCommandCache.GetRelationalCommand(queryContext.ParameterValues);
var relationalCommand = queryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = relationalCommandCache.RentAndPopulateRelationalCommand(queryContext);

return relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -1492,9 +1491,7 @@ async Task<RelationalDataReader> InitializeReaderAsync(
bool detailedErrorsEnabled,
CancellationToken cancellationToken)
{
var relationalCommandTemplate = relationalCommandCache.GetRelationalCommand(queryContext.ParameterValues);
var relationalCommand = queryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
var relationalCommand = relationalCommandCache.RentAndPopulateRelationalCommand(queryContext);

return await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -1734,7 +1731,7 @@ static RelationalDataReader InitializeReader(
RelationalCommandCache relationalCommandCache,
bool detailedErrorsEnabled)
{
var relationalCommand = relationalCommandCache.GetRelationalCommand(queryContext.ParameterValues);
var relationalCommand = relationalCommandCache.RentAndPopulateRelationalCommand(queryContext);

return relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -1813,7 +1810,7 @@ async Task<RelationalDataReader> InitializeReaderAsync(
bool detailedErrorsEnabled,
CancellationToken cancellationToken)
{
var relationalCommand = relationalCommandCache.GetRelationalCommand(queryContext.ParameterValues);
var relationalCommand = relationalCommandCache.RentAndPopulateRelationalCommand(queryContext);

return await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
40 changes: 4 additions & 36 deletions src/EFCore.Relational/Storage/IRelationalCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace Microsoft.EntityFrameworkCore.Storage
{
Expand All @@ -19,18 +16,8 @@ namespace Microsoft.EntityFrameworkCore.Storage
/// not used in application code.
/// </para>
/// </summary>
public interface IRelationalCommand
public interface IRelationalCommand : IRelationalCommandTemplate
{
/// <summary>
/// Gets the command text to be executed.
/// </summary>
string CommandText { get; }

/// <summary>
/// Gets the parameters for the command.
/// </summary>
IReadOnlyList<IRelationalParameter> Parameters { get; }

/// <summary>
/// Executes the command with no results.
/// </summary>
Expand Down Expand Up @@ -92,28 +79,9 @@ Task<RelationalDataReader> ExecuteReaderAsync(
CancellationToken cancellationToken = default);

/// <summary>
/// <para>
/// Called by the execute methods to create a <see cref="DbCommand" /> for the given <see cref="DbConnection" />
/// and configure timeouts and transactions.
/// </para>
/// <para>
/// This method is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <param name="parameterObject"> Parameters for this method. </param>
/// <param name="commandId"> The command correlation ID. </param>
/// <param name="commandMethod"> The method that will be called on the created command. </param>
/// <returns> The created command. </returns>
DbCommand CreateDbCommand(
RelationalCommandParameterObject parameterObject,
Guid commandId,
DbCommandMethod commandMethod);

/// <summary>
/// Populates this command from the provided <paramref name="command"/>.
/// Populates this command from the provided <paramref name="commandTemplate" />.
/// </summary>
/// <param name="command"> A template command from which the command text and parameters will be copied. </param>
void PopulateFrom(IRelationalCommand command);
/// <param name="commandTemplate"> A template command from which the command text and parameters will be copied. </param>
void PopulateFrom(IRelationalCommandTemplate commandTemplate);
}
}
51 changes: 51 additions & 0 deletions src/EFCore.Relational/Storage/IRelationalCommandTemplate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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.Collections.Generic;
using System.Data.Common;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace Microsoft.EntityFrameworkCore.Storage
{
/// <summary>
/// <para>
/// A command template to populate an <see cref="IRelationalCommand" /> or create a <see cref="DbCommand" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public interface IRelationalCommandTemplate
{
/// <summary>
/// Gets the command text to be copied to the destination command.
/// </summary>
string CommandText { get; }

/// <summary>
/// Gets the parameters to be copied to the destination command.
/// </summary>
IReadOnlyList<IRelationalParameter> Parameters { get; }

/// <summary>
/// <para>
/// Called by the execute methods to create a <see cref="DbCommand" /> for the given <see cref="DbConnection" />
/// and configure timeouts and transactions.
/// </para>
/// <para>
/// This method is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <param name="parameterObject"> Parameters for this method. </param>
/// <param name="commandId"> The command correlation ID. </param>
/// <param name="commandMethod"> The method that will be called on the created command. </param>
/// <returns> The created command. </returns>
DbCommand CreateDbCommand(
RelationalCommandParameterObject parameterObject,
Guid commandId,
DbCommandMethod commandMethod);
}
}
Loading