Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed May 3, 2023
1 parent 2edfca8 commit f110fcd
Show file tree
Hide file tree
Showing 23 changed files with 580 additions and 486 deletions.
18 changes: 13 additions & 5 deletions src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@
<data name="CannotChangeWhenOpen" xml:space="preserve">
<value>The instance of DbConnection is currently in use. The connection can only be changed when the existing connection is not being used.</value>
</data>
<data name="CannotTranslateNonConstantNewArrayExpression" xml:space="preserve">
<value>The query contained a new array expression containing non-constant elements, which could not be translated: '{newArrayExpression}'.</value>
</data>
<data name="CannotConfigureTriggerNonRootTphEntity" xml:space="preserve">
<value>Can't configure a trigger on entity type '{entityType}', which is in a TPH hierarchy and isn't the root. Configure the trigger on the TPH root entity type '{rootEntityType}' instead.</value>
</data>
Expand Down Expand Up @@ -346,8 +349,8 @@
<data name="DuplicateSeedDataSensitive" xml:space="preserve">
<value>A seed entity for entity type '{entityType}' has the same key value {keyValue} as another seed entity mapped to the same table '{table}'. Key values should be unique across seed entities.</value>
</data>
<data name="EitherOfTwoValuesMustBeNull" xml:space="preserve">
<value>Either {param1} or {param2} must be null.</value>
<data name="OneOfThreeValuesMustBeSet" xml:space="preserve">
<value>Exactly one of '{param1}', '{param2}' or '{param3}' must be set.</value>
</data>
<data name="EmptyCollectionNotSupportedAsInlineQueryRoot" xml:space="preserve">
<value>Empty collections are not supported as inline query roots.</value>
Expand Down Expand Up @@ -912,7 +915,7 @@
<value>Cannot create a DbCommand for a non-relational query.</value>
</data>
<data name="NonConstantOrParameterAsInExpressionValues" xml:space="preserve">
<value>Expression of type '{type}' isn't supported as the Values of an InExpression; only constants and parameters are supported.</value>
<value>Expression of type '{type}' isn't supported in the Values of an InExpression; only constants and parameters are supported.</value>
</data>
<data name="NoneRelationalTypeMappingOnARelationalTypeMappingSource" xml:space="preserve">
<value>'FindMapping' was called on a 'RelationalTypeMappingSource' with a non-relational 'TypeMappingInfo'.</value>
Expand Down
15 changes: 12 additions & 3 deletions src/EFCore.Relational/Query/ISqlExpressionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,23 +403,32 @@ SqlFunctionExpression NiladicFunction(
/// <returns>An expression representing an EXISTS operation in a SQL tree.</returns>
ExistsExpression Exists(SelectExpression subquery, bool negated);

/// <summary>
/// Creates a new <see cref="InExpression" /> which represents an IN operation in a SQL tree.
/// </summary>
/// <param name="item">An item to look into values.</param>
/// <param name="subquery">A subquery in which item is searched.</param>
/// <param name="negated">A value indicating if the item should be present in the values or absent.</param>
/// <returns>An expression representing an IN operation in a SQL tree.</returns>
InExpression In(SqlExpression item, SelectExpression subquery, bool negated);

/// <summary>
/// Creates a new <see cref="InExpression" /> which represents an IN operation in a SQL tree.
/// </summary>
/// <param name="item">An item to look into values.</param>
/// <param name="values">A list of values in which item is searched.</param>
/// <param name="negated">A value indicating if the item should be present in the values or absent.</param>
/// <returns>An expression representing an IN operation in a SQL tree.</returns>
InExpression In(SqlExpression item, SqlExpression values, bool negated);
InExpression In(SqlExpression item, IReadOnlyList<SqlExpression> values, bool negated);

/// <summary>
/// Creates a new <see cref="InExpression" /> which represents an IN operation in a SQL tree.
/// </summary>
/// <param name="item">An item to look into values.</param>
/// <param name="subquery">A subquery in which item is searched.</param>
/// <param name="valuesParameter">A parameterized list of values in which the item is searched.</param>
/// <param name="negated">A value indicating if the item should be present in the values or absent.</param>
/// <returns>An expression representing an IN operation in a SQL tree.</returns>
InExpression In(SqlExpression item, SelectExpression subquery, bool negated);
InExpression In(SqlExpression item, SqlParameterExpression valuesParameter, bool negated);

/// <summary>
/// Creates a new <see cref="InExpression" /> which represents a LIKE in a SQL tree.
Expand Down
31 changes: 27 additions & 4 deletions src/EFCore.Relational/Query/Internal/ContainsTranslator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Microsoft.EntityFrameworkCore.Query.Internal;
Expand Down Expand Up @@ -38,26 +39,48 @@ public ContainsTranslator(ISqlExpressionFactory sqlExpressionFactory)
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
SqlExpression? itemExpression = null, valuesExpression = null;
// SqlExpression? values = null;
if (method.IsGenericMethod
&& method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains)
&& method.GetGenericMethodDefinition() == EnumerableMethods.Contains
&& ValidateValues(arguments[0]))
{
return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[1]), arguments[0], negated: false);
(itemExpression, valuesExpression) = (RemoveObjectConvert(arguments[1]), arguments[0]);
}

if (arguments.Count == 1
&& method.IsContainsMethod()
&& instance != null
&& ValidateValues(instance))
{
return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[0]), instance, negated: false);
(itemExpression, valuesExpression) = (RemoveObjectConvert(arguments[0]), instance);
}

if (itemExpression is not null && valuesExpression is not null)
{
switch (valuesExpression)
{
case SqlParameterExpression parameter:
return _sqlExpressionFactory.In(itemExpression, parameter, negated: false);

case SqlConstantExpression { Value: IEnumerable values }:
var valuesExpressions = new List<SqlExpression>();

foreach (var value in values)
{
// TODO: Type mapping?
valuesExpressions.Add(_sqlExpressionFactory.Constant(value, itemExpression.TypeMapping));
}

return _sqlExpressionFactory.In(itemExpression, valuesExpressions, negated: false);
}
}

return null;
}

private static bool ValidateValues(SqlExpression values)
=> values is SqlConstantExpression || values is SqlParameterExpression;
=> values is SqlConstantExpression or SqlParameterExpression;

private static SqlExpression RemoveObjectConvert(SqlExpression expression)
=> expression is SqlUnaryExpression sqlUnaryExpression
Expand Down
Loading

0 comments on commit f110fcd

Please sign in to comment.