Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
6 changes: 4 additions & 2 deletions src/EFCore.Relational/EFExtensions.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.Collections;

namespace Microsoft.EntityFrameworkCore;

/// <summary>
Expand All @@ -25,10 +27,10 @@ public static class EFExtensions
/// Within the context of an EF LINQ query, forces its argument to be inserted into the query as a multiple parameter expressions.
/// </summary>
/// <remarks>Note that this is a static method accessed through the top-level <see cref="EF" /> static type.</remarks>
/// <typeparam name="T">The type of collection element.</typeparam>
/// <typeparam name="TSource">The type of collection.</typeparam>
/// <param name="argument">The collection to be integrated as parameters into the query.</param>
/// <returns>The same value for further use in the query.</returns>
public static IEnumerable<T> MultipleParameters<T>(IEnumerable<T> argument)
public static TSource MultipleParameters<TSource>(TSource argument) where TSource : IEnumerable
=> throw new InvalidOperationException(RelationalStrings.EFMultipleParametersInvoked);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public virtual TBuilder ExecutionStrategy(
/// </remarks>
[Obsolete("Use UseParameterizedCollectionMode instead.")]
public virtual TBuilder TranslateParameterizedCollectionsToConstants()
=> UseParameterizedCollectionMode(ParameterizedCollectionMode.Constants);
=> UseParameterizedCollectionMode(ParameterTranslationMode.Constant);

/// <summary>
/// Configures the context to translate parameterized collections to a single array-like parameter.
Expand All @@ -202,13 +202,13 @@ public virtual TBuilder TranslateParameterizedCollectionsToConstants()
/// </remarks>
[Obsolete("Use UseParameterizedCollectionMode instead.")]
public virtual TBuilder TranslateParameterizedCollectionsToParameters()
=> UseParameterizedCollectionMode(ParameterizedCollectionMode.Parameter);
=> UseParameterizedCollectionMode(ParameterTranslationMode.Parameter);

/// <summary>
/// Configures the <see cref="ParameterizedCollectionMode" /> to use when translating parameterized collections.
/// Configures mode to use when translating parameterized collections.
/// </summary>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public virtual TBuilder UseParameterizedCollectionMode(ParameterizedCollectionMode parameterizedCollectionMode)
public virtual TBuilder UseParameterizedCollectionMode(ParameterTranslationMode parameterizedCollectionMode)
=> WithOption(e => (TExtension)e.WithUseParameterizedCollectionMode(parameterizedCollectionMode));

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public abstract class RelationalOptionsExtension : IDbContextOptionsExtension
private string? _migrationsHistoryTableName;
private string? _migrationsHistoryTableSchema;
private Func<ExecutionStrategyDependencies, IExecutionStrategy>? _executionStrategyFactory;
private ParameterizedCollectionMode? _parameterizedCollectionMode;
private ParameterTranslationMode? _parameterizedCollectionMode;

/// <summary>
/// Creates a new set of options with everything set to default values.
Expand Down Expand Up @@ -386,16 +386,16 @@ public virtual RelationalOptionsExtension WithExecutionStrategyFactory(
/// <summary>
/// Configured translation mode for parameterized collections.
/// </summary>
public virtual ParameterizedCollectionMode ParameterizedCollectionMode
=> _parameterizedCollectionMode ?? ParameterizedCollectionMode.MultipleParameters;
public virtual ParameterTranslationMode ParameterizedCollectionMode
=> _parameterizedCollectionMode ?? ParameterTranslationMode.MultipleParameters;

/// <summary>
/// Creates a new instance with all options the same as for this instance, but with the given option changed.
/// It is unusual to call this method directly. Instead use <see cref="DbContextOptionsBuilder" />.
/// </summary>
/// <param name="parameterizedCollectionMode">The option to change.</param>
public virtual RelationalOptionsExtension WithUseParameterizedCollectionMode(
ParameterizedCollectionMode parameterizedCollectionMode)
ParameterTranslationMode parameterizedCollectionMode)
{
var clone = Clone();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public RelationalCommandCache(
IRelationalParameterBasedSqlProcessorFactory relationalParameterBasedSqlProcessorFactory,
Expression queryExpression,
bool useRelationalNulls,
ParameterizedCollectionMode parameterizedCollectionMode)
ParameterTranslationMode parameterizedCollectionMode)
{
_memoryCache = memoryCache;
_querySqlGeneratorFactory = querySqlGeneratorFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private SqlParameterExpression VisitSqlParameter(SqlParameterExpression paramete
uniquifiedName,
parameter.Type,
parameter.IsNullable,
parameter.ShouldBeConstantized,
parameter.ParameterTranslationMode,
parameter.TypeMapping);

return _sqlParameters[newParameter.InvariantName] = newParameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public sealed record RelationalParameterBasedSqlProcessorParameters
/// <summary>
/// Which parametrized collection translation mode should be used.
/// </summary>
public ParameterizedCollectionMode ParameterizedCollectionMode { get; init; }
public ParameterTranslationMode ParameterizedCollectionMode { get; init; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the new naming, maybe call this CollectionParameterTranslationMode for consistency etc. (also elsewhere)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UseParameterizedCollectionMode stays?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess in the interest of avoiding another design discussion on the public API at this late hour, let's just keep things as they are - disregard my above comment...


/// <summary>
/// Creates a new instance of <see cref="RelationalParameterBasedSqlProcessorParameters" />.
/// </summary>
/// <param name="useRelationalNulls">A value indicating if relational nulls should be used.</param>
/// <param name="parameterizedCollectionMode">Which translation mode should be used.</param>
[EntityFrameworkInternal]
public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls, ParameterizedCollectionMode parameterizedCollectionMode)
public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls, ParameterTranslationMode parameterizedCollectionMode)
{
UseRelationalNulls = useRelationalNulls;
ParameterizedCollectionMode = parameterizedCollectionMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor : Que
private readonly IRelationalTypeMappingSource _typeMappingSource;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly bool _subquery;
private readonly ParameterizedCollectionMode _parameterizedCollectionMode;
private readonly ParameterTranslationMode _parameterizedCollectionMode;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -244,7 +244,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
&& methodCallExpression.Arguments[0] is ParameterQueryRootExpression parameterSource
&& TranslateExpression(methodCallExpression.Arguments[1]) is SqlExpression item
&& _sqlTranslator.Visit(parameterSource.QueryParameterExpression) is SqlParameterExpression sqlParameterExpression
&& !parameterSource.QueryParameterExpression.ShouldNotBeConstantized)
&& (parameterSource.QueryParameterExpression.ParameterTranslationMode is ParameterTranslationMode.Constant
or null))
{
var inExpression = _sqlExpressionFactory.In(item, sqlParameterExpression);
var selectExpression = new SelectExpression(inExpression, _sqlAliasManager);
Expand Down Expand Up @@ -298,26 +299,25 @@ JsonScalarExpression jsonScalar

var tableAlias = _sqlAliasManager.GenerateTableAlias(sqlParameterExpression.Name.TrimStart('_'));

var constants = queryParameter.ShouldBeConstantized
|| (_parameterizedCollectionMode is ParameterizedCollectionMode.Constants
&& !queryParameter.ShouldNotBeConstantized);
var multipleParameters = _parameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters
&& !queryParameter.ShouldNotBeConstantized;
if (constants || multipleParameters)
{
var valuesExpression = new ValuesExpression(
tableAlias,
sqlParameterExpression,
[ValuesOrderingColumnName, ValuesValueColumnName]);
return CreateShapedQueryExpressionForValuesExpression(
valuesExpression,
tableAlias,
parameterQueryRootExpression.ElementType,
sqlParameterExpression.TypeMapping,
sqlParameterExpression.IsNullable);
}

return TranslatePrimitiveCollection(sqlParameterExpression, property: null, tableAlias);
var parameterMode = queryParameter.ParameterTranslationMode ?? _parameterizedCollectionMode;
return parameterMode switch
{
ParameterTranslationMode.Constant or ParameterTranslationMode.MultipleParameters
=> CreateShapedQueryExpressionForValuesExpression(
new ValuesExpression(
tableAlias,
sqlParameterExpression,
[ValuesOrderingColumnName, ValuesValueColumnName]),
tableAlias,
parameterQueryRootExpression.ElementType,
sqlParameterExpression.TypeMapping,
sqlParameterExpression.IsNullable),

ParameterTranslationMode.Parameter
=> TranslatePrimitiveCollection(sqlParameterExpression, property: null, tableAlias),

_ => throw new UnreachableException()
};
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor : ShapedQue
private readonly bool _threadSafetyChecksEnabled;
private readonly bool _detailedErrorsEnabled;
private readonly bool _useRelationalNulls;
private readonly ParameterizedCollectionMode _parameterizedCollectionMode;
private readonly ParameterTranslationMode _parameterizedCollectionMode;
private readonly bool _isPrecompiling;

private readonly RelationalParameterBasedSqlProcessor _relationalParameterBasedSqlProcessor;
Expand Down Expand Up @@ -751,7 +751,7 @@ Expression<Func<RelationalMaterializerLiftableConstantContext, object>> Generate
_relationalDependenciesRelationalParameterBasedSqlProcessorFactoryProperty),
Constant(queryExpression),
Constant(_useRelationalNulls),
Constant(_parameterizedCollectionMode, typeof(ParameterizedCollectionMode))),
Constant(_parameterizedCollectionMode, typeof(ParameterTranslationMode))),
contextParameter);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
name: queryParameter.Name,
queryParameter.Type,
nullable: false,
queryParameter.ShouldBeConstantized,
queryParameter.ParameterTranslationMode,
typeMapping: null);
}

Expand All @@ -525,7 +525,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
name: queryParameter.Name,
queryParameter.Type,
queryParameter.Type.IsNullableType(),
queryParameter.ShouldBeConstantized,
queryParameter.ParameterTranslationMode,
typeMapping: null);

case StructuralTypeShaperExpression shaper:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class SqlParameterExpression : SqlExpression
/// <param name="type">The <see cref="Type" /> of the expression.</param>
/// <param name="typeMapping">The <see cref="RelationalTypeMapping" /> associated with the expression.</param>
public SqlParameterExpression(string name, Type type, RelationalTypeMapping? typeMapping)
: this(invariantName: name, name: name, type.UnwrapNullableType(), type.IsNullableType(), shouldBeConstantized: false, typeMapping)
: this(invariantName: name, name: name, type.UnwrapNullableType(), type.IsNullableType(), parameterTranslationMode: null, typeMapping)
{
}

Expand All @@ -31,21 +31,21 @@ public SqlParameterExpression(string name, Type type, RelationalTypeMapping? typ
/// </param>
/// <param name="type">The <see cref="Type" /> of the expression.</param>
/// <param name="nullable">Whether this parameter can have null values.</param>
/// <param name="shouldBeConstantized">Whether the user has indicated that this query parameter should be inlined as a constant.</param>
/// <param name="parameterTranslationMode">How the parameter should be handled.</param>
/// <param name="typeMapping">The <see cref="RelationalTypeMapping" /> associated with the expression.</param>
public SqlParameterExpression(
string invariantName,
string name,
Type type,
bool nullable,
bool shouldBeConstantized,
ParameterTranslationMode? parameterTranslationMode,
RelationalTypeMapping? typeMapping)
: base(type.UnwrapNullableType(), typeMapping)
{
InvariantName = invariantName;
Name = name;
IsNullable = nullable;
ShouldBeConstantized = shouldBeConstantized;
ParameterTranslationMode = parameterTranslationMode;
}

/// <summary>
Expand All @@ -65,17 +65,17 @@ public SqlParameterExpression(
public bool IsNullable { get; }

/// <summary>
/// Whether the user has indicated that this query parameter should be inlined as a constant.
/// How the parameter should be handled.
/// </summary>
public bool ShouldBeConstantized { get; }
public ParameterTranslationMode? ParameterTranslationMode { get; }

/// <summary>
/// Applies supplied type mapping to this expression.
/// </summary>
/// <param name="typeMapping">A relational type mapping to apply.</param>
/// <returns>A new expression which has supplied type mapping.</returns>
public SqlExpression ApplyTypeMapping(RelationalTypeMapping? typeMapping)
=> new SqlParameterExpression(InvariantName, Name, Type, IsNullable, ShouldBeConstantized, typeMapping);
=> new SqlParameterExpression(InvariantName, Name, Type, IsNullable, ParameterTranslationMode, typeMapping);

/// <inheritdoc />
protected override Expression VisitChildren(ExpressionVisitor visitor)
Expand Down
38 changes: 11 additions & 27 deletions src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Query;

Expand Down Expand Up @@ -62,7 +61,7 @@ public SqlNullabilityProcessor(
/// <summary>
/// A value indicating what translation mode to use.
/// </summary>
public virtual ParameterizedCollectionMode ParameterizedCollectionMode { get; }
public virtual ParameterTranslationMode ParameterizedCollectionMode { get; }

/// <summary>
/// Dictionary of current parameter values in use.
Expand Down Expand Up @@ -127,10 +126,10 @@ protected override Expression VisitExtension(Expression node)

var processedValues = new List<RowValueExpression>();

switch (ParameterizedCollectionMode)
var parameterMode = valuesParameter.ParameterTranslationMode ?? ParameterizedCollectionMode;
switch (parameterMode)
{
case ParameterizedCollectionMode.MultipleParameters
when !valuesParameter.ShouldBeConstantized:
case ParameterTranslationMode.MultipleParameters:
{
var expandedParameters = _collectionParameterExpansionMap.GetOrAddNew(valuesParameter);
for (var i = 0; i < values.Count; i++)
Expand All @@ -156,11 +155,7 @@ protected override Expression VisitExtension(Expression node)
break;
}

case ParameterizedCollectionMode.Constants:
case ParameterizedCollectionMode.Parameter
when valuesParameter.ShouldBeConstantized:
case ParameterizedCollectionMode.MultipleParameters
when valuesParameter.ShouldBeConstantized:
case ParameterTranslationMode.Constant:
{
foreach (var value in values)
{
Expand Down Expand Up @@ -820,18 +815,7 @@ InExpression ProcessInExpressionValues(

processedValues = [];

var useParameters = ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters
&& !valuesParameter.ShouldBeConstantized;
var useConstants =
ParameterizedCollectionMode is ParameterizedCollectionMode.Constants
||
(ParameterizedCollectionMode is ParameterizedCollectionMode.Parameter
&& valuesParameter.ShouldBeConstantized)
||
(ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters
&& valuesParameter.ShouldBeConstantized);
var useParameter = ParameterizedCollectionMode is ParameterizedCollectionMode.Parameter
&& !valuesParameter.ShouldBeConstantized;
var parameterMode = valuesParameter.ParameterTranslationMode ?? ParameterizedCollectionMode;
var expandedParameters = _collectionParameterExpansionMap.GetOrAddNew(valuesParameter);
var expandedParametersCounter = 0;
for (var i = 0; i < values.Count; i++)
Expand All @@ -842,11 +826,11 @@ ParameterizedCollectionMode is ParameterizedCollectionMode.Constants
continue;
}

switch (useParameters, useConstants, useParameter)
switch (parameterMode)
{
case (true, false, false):
case ParameterTranslationMode.MultipleParameters:
// see #36311 for more info
case (false, false, true):
case ParameterTranslationMode.Parameter:
{
// Create parameter for value if we didn't create it yet,
// otherwise reuse it.
Expand All @@ -864,7 +848,7 @@ ParameterizedCollectionMode is ParameterizedCollectionMode.Constants
break;
}

case (false, true, false):
case ParameterTranslationMode.Constant:
{
processedValues.Add(_sqlExpressionFactory.Constant(values[i], values[i]?.GetType() ?? typeof(object), sensitive: true, elementTypeMapping));

Expand Down Expand Up @@ -1426,7 +1410,7 @@ protected virtual SqlExpression VisitSqlParameter(

nullable = false;

if (sqlParameterExpression.ShouldBeConstantized)
if (sqlParameterExpression.ParameterTranslationMode is ParameterTranslationMode.Constant)
{
var parameters = ParametersFacade.GetParametersAndDisableSqlCaching();

Expand Down
Loading