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