Skip to content
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
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using HotChocolate.Language;
Expand Down Expand Up @@ -114,31 +115,33 @@ public IFilterFieldDescriptor Field<TField>(Expression<Func<T, TField>> property
return arrayLengthFieldDescriptor;
}

switch (propertyOrMember.TryExtractMember())
if (TryExtractDirectMember(propertyOrMember, out var member))
{
case PropertyInfo m:
var fieldDescriptor =
Fields.FirstOrDefault(t => t.Configuration.Member == m);

if (fieldDescriptor is null)
{
fieldDescriptor = FilterFieldDescriptor.New(Context, Configuration.Scope, m);
Fields.Add(fieldDescriptor);
}

return fieldDescriptor;

case MethodInfo:
throw new ArgumentException(
FilterInputTypeDescriptor_Field_OnlyProperties,
nameof(propertyOrMember));

default:
fieldDescriptor = FilterFieldDescriptor
.New(Context, Configuration.Scope, propertyOrMember);
Fields.Add(fieldDescriptor);
return fieldDescriptor;
switch (member)
{
case PropertyInfo m:
var fieldDescriptor =
Fields.FirstOrDefault(t => t.Configuration.Member == m);

if (fieldDescriptor is null)
{
fieldDescriptor = FilterFieldDescriptor.New(Context, Configuration.Scope, m);
Fields.Add(fieldDescriptor);
}

return fieldDescriptor;

case MethodInfo:
throw new ArgumentException(
FilterInputTypeDescriptor_Field_OnlyProperties,
nameof(propertyOrMember));
}
}

var expressionFieldDescriptor = FilterFieldDescriptor
.New(Context, Configuration.Scope, expression: propertyOrMember);
Fields.Add(expressionFieldDescriptor);
return expressionFieldDescriptor;
}

/// <inheritdoc />
Expand Down Expand Up @@ -212,4 +215,29 @@ public IFilterInputTypeDescriptor<T> Ignore(Expression<Func<T, object?>> propert
base.Directive(name, arguments);
return this;
}

private static bool TryExtractDirectMember<TField>(
Expression<Func<T, TField>> propertyOrMember,
[NotNullWhen(true)] out MemberInfo? member)
{
var expression = propertyOrMember.Body;

while (expression is UnaryExpression
{
NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked
} unaryExpression)
{
expression = unaryExpression.Operand;
}

if (expression is MemberExpression { Expression: ParameterExpression }
|| expression is MethodCallExpression { Object: ParameterExpression })
{
member = propertyOrMember.TryExtractMember();
return member is not null;
}

member = null;
return false;
}
}
101 changes: 101 additions & 0 deletions src/HotChocolate/Data/test/Data.Tests/Issue6258ReproTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using HotChocolate.Data.Filters;
using HotChocolate.Execution;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Data;

public class Issue6258ReproTests
{
[Fact]
public async Task Filter_For_Nested_Inherited_Id_Matches_Author_Id()
{
var executor = await CreateExecutorAsync();
var result = await executor.ExecuteAsync(
"""
{
book(where: { authorId: { eq: 20 } }) {
id
author {
id
}
}
}
""");

result.MatchSnapshot();
}

[Fact]
public async Task Filter_For_Nested_Inherited_Id_Does_Not_Match_Book_Id()
{
var executor = await CreateExecutorAsync();
var result = await executor.ExecuteAsync(
"""
{
book(where: { authorId: { eq: 2 } }) {
id
}
}
""");

result.MatchSnapshot();
}

public abstract class Issue6258Entity
{
public long Id { get; set; }
}

public class Issue6258Book : Issue6258Entity
{
public string Title { get; set; } = default!;

public Issue6258Author Author { get; set; } = default!;
}

public class Issue6258Author : Issue6258Entity
{
public string Name { get; set; } = default!;
}

public sealed class Issue6258BookFilterInputType : FilterInputType<Issue6258Book>
{
protected override void Configure(IFilterInputTypeDescriptor<Issue6258Book> descriptor)
{
descriptor.BindFieldsExplicitly();
descriptor.Field(b => b.Author.Id).Name("authorId");
}
}

public class Issue6258Query
{
[UseFiltering<Issue6258BookFilterInputType>]
public IQueryable<Issue6258Book> GetBook()
=> s_data.AsQueryable();
}

private static readonly Issue6258Book[] s_data =
[
new()
{
Id = 1,
Title = "title 1",
Author = new Issue6258Author { Id = 10, Name = "author 1" }
},
new()
{
Id = 2,
Title = "title 2",
Author = new Issue6258Author { Id = 20, Name = "author 2" }
}
];

private static async Task<IRequestExecutor> CreateExecutorAsync()
{
return await new ServiceCollection()
.AddGraphQL()
.AddFiltering()
.AddQueryType<Issue6258Query>()
.BuildRequestExecutorAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"book": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"data": {
"book": [
{
"id": 2,
"author": {
"id": 20
}
}
]
}
}
Loading