未验证 提交 36376107 编写于 作者: S Shay Rojansky 提交者: GitHub

Translate ValueTuple.Create() (#2371)

As a nicer alternative to new ValueTuple<T1, T2>(...)
上级 4de9b541
...@@ -37,7 +37,7 @@ public class NpgsqlMethodCallTranslatorProvider : RelationalMethodCallTranslator ...@@ -37,7 +37,7 @@ public class NpgsqlMethodCallTranslatorProvider : RelationalMethodCallTranslator
new NpgsqlRandomTranslator(sqlExpressionFactory), new NpgsqlRandomTranslator(sqlExpressionFactory),
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model), new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlRegexIsMatchTranslator(sqlExpressionFactory), new NpgsqlRegexIsMatchTranslator(sqlExpressionFactory),
new NpgsqlRowValueComparisonTranslator(sqlExpressionFactory), new NpgsqlRowValueTranslator(sqlExpressionFactory),
new NpgsqlStringMethodTranslator(typeMappingSource, sqlExpressionFactory, model), new NpgsqlStringMethodTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlTrigramsMethodTranslator(typeMappingSource, sqlExpressionFactory, model), new NpgsqlTrigramsMethodTranslator(typeMappingSource, sqlExpressionFactory, model),
}); });
......
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Npgsql.EntityFrameworkCore.PostgreSQL.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal; namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
public class NpgsqlRowValueComparisonTranslator : IMethodCallTranslator public class NpgsqlRowValueTranslator : IMethodCallTranslator
{ {
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory; private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
...@@ -28,7 +29,7 @@ public class NpgsqlRowValueComparisonTranslator : IMethodCallTranslator ...@@ -28,7 +29,7 @@ public class NpgsqlRowValueComparisonTranslator : IMethodCallTranslator
typeof(NpgsqlDbFunctionsExtensions).GetMethods() typeof(NpgsqlDbFunctionsExtensions).GetMethods()
.Single(m => m.Name == nameof(NpgsqlDbFunctionsExtensions.LessThanOrEqual)); .Single(m => m.Name == nameof(NpgsqlDbFunctionsExtensions.LessThanOrEqual));
private static readonly Dictionary<MethodInfo, ExpressionType> Methods = new() private static readonly Dictionary<MethodInfo, ExpressionType> ComparisonMethods = new()
{ {
{ GreaterThan, ExpressionType.GreaterThan }, { GreaterThan, ExpressionType.GreaterThan },
{ LessThan, ExpressionType.LessThan }, { LessThan, ExpressionType.LessThan },
...@@ -37,19 +38,27 @@ public class NpgsqlRowValueComparisonTranslator : IMethodCallTranslator ...@@ -37,19 +38,27 @@ public class NpgsqlRowValueComparisonTranslator : IMethodCallTranslator
}; };
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="NpgsqlRowValueComparisonTranslator"/> class. /// Initializes a new instance of the <see cref="NpgsqlRowValueTranslator"/> class.
/// </summary> /// </summary>
public NpgsqlRowValueComparisonTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory) public NpgsqlRowValueTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory)
=> _sqlExpressionFactory = sqlExpressionFactory; => _sqlExpressionFactory = sqlExpressionFactory;
/// <inheritdoc /> /// <inheritdoc />
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ValueType))] // For ValueTuple.Create
public virtual SqlExpression? Translate( public virtual SqlExpression? Translate(
SqlExpression? instance, SqlExpression? instance,
MethodInfo method, MethodInfo method,
IReadOnlyList<SqlExpression> arguments, IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger) IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{ {
if (method.DeclaringType != typeof(NpgsqlDbFunctionsExtensions) || !Methods.TryGetValue(method, out var expressionType)) // Translate ValueTuple.Create
if (method.DeclaringType == typeof(ValueTuple) && method.IsStatic && method.Name == nameof(ValueTuple.Create))
{
return new PostgresRowValueExpression(arguments, method.ReturnType);
}
// Translate EF.Functions.GreaterThan and other comparisons
if (method.DeclaringType != typeof(NpgsqlDbFunctionsExtensions) || !ComparisonMethods.TryGetValue(method, out var expressionType))
{ {
return null; return null;
} }
......
...@@ -23,15 +23,18 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model ...@@ -23,15 +23,18 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model
{ {
case MethodCallExpression methodCallExpression: case MethodCallExpression methodCallExpression:
var declaringType = methodCallExpression.Method.DeclaringType; var declaringType = methodCallExpression.Method.DeclaringType;
var method = methodCallExpression.Method;
if (methodCallExpression.Method == TsQueryParse if (method == TsQueryParse
|| methodCallExpression.Method == TsVectorParse || method == TsVectorParse
|| declaringType == typeof(NpgsqlDbFunctionsExtensions) || declaringType == typeof(NpgsqlDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlFullTextSearchDbFunctionsExtensions) || declaringType == typeof(NpgsqlFullTextSearchDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlFullTextSearchLinqExtensions) || declaringType == typeof(NpgsqlFullTextSearchLinqExtensions)
|| declaringType == typeof(NpgsqlNetworkDbFunctionsExtensions) || declaringType == typeof(NpgsqlNetworkDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlJsonDbFunctionsExtensions) || declaringType == typeof(NpgsqlJsonDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlRangeDbFunctionsExtensions)) || declaringType == typeof(NpgsqlRangeDbFunctionsExtensions)
// Prevent evaluation of ValueTuple.Create, see NewExpression of ITuple below
|| declaringType == typeof(ValueTuple) && method.Name == nameof(ValueTuple.Create))
{ {
return false; return false;
} }
......
...@@ -245,8 +245,8 @@ public async Task Row_value_GreaterThan() ...@@ -245,8 +245,8 @@ public async Task Row_value_GreaterThan()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => EF.Functions.GreaterThan( .Where(c => EF.Functions.GreaterThan(
new ValueTuple<string, string>(c.City, c.CustomerID), ValueTuple.Create(c.City, c.CustomerID),
new ValueTuple<string, string>("Buenos Aires", "OCEAN"))) ValueTuple.Create("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -262,8 +262,8 @@ public async Task Row_value_GreaterThan_with_differing_types() ...@@ -262,8 +262,8 @@ public async Task Row_value_GreaterThan_with_differing_types()
_ = await ctx.Orders _ = await ctx.Orders
.Where(o => EF.Functions.GreaterThan( .Where(o => EF.Functions.GreaterThan(
new ValueTuple<string, int>(o.CustomerID, o.OrderID), ValueTuple.Create(o.CustomerID, o.OrderID),
new ValueTuple<string, int>("ALFKI", 10702))) ValueTuple.Create("ALFKI", 10702)))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -281,8 +281,8 @@ public async Task Row_value_GreaterThan_with_parameter() ...@@ -281,8 +281,8 @@ public async Task Row_value_GreaterThan_with_parameter()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => EF.Functions.GreaterThan( .Where(c => EF.Functions.GreaterThan(
new ValueTuple<string, string>(c.City, c.CustomerID), ValueTuple.Create(c.City, c.CustomerID),
new ValueTuple<string, string>(city1, "OCEAN"))) ValueTuple.Create(city1, "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -300,8 +300,8 @@ public async Task Row_value_LessThan() ...@@ -300,8 +300,8 @@ public async Task Row_value_LessThan()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => EF.Functions.LessThan( .Where(c => EF.Functions.LessThan(
new ValueTuple<string, string>(c.City, c.CustomerID), ValueTuple.Create(c.City, c.CustomerID),
new ValueTuple<string, string>("Buenos Aires", "OCEAN"))) ValueTuple.Create("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -317,8 +317,8 @@ public async Task Row_value_GreaterThanOrEqual() ...@@ -317,8 +317,8 @@ public async Task Row_value_GreaterThanOrEqual()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => EF.Functions.GreaterThanOrEqual( .Where(c => EF.Functions.GreaterThanOrEqual(
new ValueTuple<string, string>(c.City, c.CustomerID), ValueTuple.Create(c.City, c.CustomerID),
new ValueTuple<string, string>("Buenos Aires", "OCEAN"))) ValueTuple.Create("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -334,6 +334,23 @@ public async Task Row_value_LessThanOrEqual() ...@@ -334,6 +334,23 @@ public async Task Row_value_LessThanOrEqual()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => EF.Functions.LessThanOrEqual( .Where(c => EF.Functions.LessThanOrEqual(
ValueTuple.Create(c.City, c.CustomerID),
ValueTuple.Create("Buenos Aires", "OCEAN")))
.CountAsync();
AssertSql(
@"SELECT COUNT(*)::INT
FROM ""Customers"" AS c
WHERE (c.""City"", c.""CustomerID"") <= ('Buenos Aires', 'OCEAN')");
}
[ConditionalFact]
public async Task Row_value_with_ValueTuple_constructor()
{
await using var ctx = CreateContext();
_ = await ctx.Customers
.Where(c => EF.Functions.GreaterThan(
new ValueTuple<string, string>(c.City, c.CustomerID), new ValueTuple<string, string>(c.City, c.CustomerID),
new ValueTuple<string, string>("Buenos Aires", "OCEAN"))) new ValueTuple<string, string>("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
...@@ -341,7 +358,7 @@ public async Task Row_value_LessThanOrEqual() ...@@ -341,7 +358,7 @@ public async Task Row_value_LessThanOrEqual()
AssertSql( AssertSql(
@"SELECT COUNT(*)::INT @"SELECT COUNT(*)::INT
FROM ""Customers"" AS c FROM ""Customers"" AS c
WHERE (c.""City"", c.""CustomerID"") <= ('Buenos Aires', 'OCEAN')"); WHERE (c.""City"", c.""CustomerID"") > ('Buenos Aires', 'OCEAN')");
} }
[ConditionalFact] [ConditionalFact]
...@@ -352,8 +369,8 @@ public async Task Row_value_parameter_count_mismatch() ...@@ -352,8 +369,8 @@ public async Task Row_value_parameter_count_mismatch()
var exception = await Assert.ThrowsAsync<ArgumentException>( var exception = await Assert.ThrowsAsync<ArgumentException>(
() => ctx.Customers () => ctx.Customers
.Where(c => EF.Functions.LessThanOrEqual( .Where(c => EF.Functions.LessThanOrEqual(
new ValueTuple<string, string>(c.City, c.CustomerID), ValueTuple.Create(c.City, c.CustomerID),
new ValueTuple<string, string, string>("Buenos Aires", "OCEAN", "foo"))) ValueTuple.Create("Buenos Aires", "OCEAN", "foo")))
.CountAsync()); .CountAsync());
Assert.Equal(NpgsqlStrings.RowValueComparisonRequiresTuplesOfSameLength, exception.Message); Assert.Equal(NpgsqlStrings.RowValueComparisonRequiresTuplesOfSameLength, exception.Message);
...@@ -366,8 +383,8 @@ public async Task Row_value_equals() ...@@ -366,8 +383,8 @@ public async Task Row_value_equals()
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => .Where(c =>
new ValueTuple<string, string>(c.City, c.CustomerID).Equals( ValueTuple.Create(c.City, c.CustomerID).Equals(
new ValueTuple<string, string>("Buenos Aires", "OCEAN"))) ValueTuple.Create("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
...@@ -382,9 +399,7 @@ public async Task Row_value_not_equals() ...@@ -382,9 +399,7 @@ public async Task Row_value_not_equals()
await using var ctx = CreateContext(); await using var ctx = CreateContext();
_ = await ctx.Customers _ = await ctx.Customers
.Where(c => .Where(c => !ValueTuple.Create(c.City, c.CustomerID).Equals(ValueTuple.Create("Buenos Aires", "OCEAN")))
!new ValueTuple<string, string>(c.City, c.CustomerID).Equals(
new ValueTuple<string, string>("Buenos Aires", "OCEAN")))
.CountAsync(); .CountAsync();
AssertSql( AssertSql(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册