AutoMapperQueryableExtensions with explicit expansion doesn't work well with filtering
Is there an existing issue for this?
- [X] I have searched the existing issues
Describe the bug
I use ProjectTo from AutoMapperQueryableExtensions with explicit expansion set in AutoMapper and unfortunately it doesn't work when filtering by property not included in selected fields. To resolve this fields selected by filter should also be added to membersToExpand parameter in AutoMapper ProjectTo call.
Steps to reproduce
For example this code:
public class Query
{
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Region> GetRegions(
AppDbContext dbContext,
IResolverContext context
)
{
return dbContext.CodeDefinition
.OfType<RegionCodeDefinition>()
.ProjectTo<RegionCodeDefinition, Region>(context);
}
}
when running following query
query {
regions(
where: {
provinces: { some: { cities: { some: { venues: { any: true } } } } }
}
) {
id
name
code
provinces {
id
name
code
cities {
id
name
code
}
}
}
}
will throw an exception
System.InvalidOperationException: 'The LINQ expression 'DbSet<CityCodeDefinition>()
.Where(c0 => EF.Property<int?>(EntityShaperExpression:
AppDb.Models.CodeDefinitions.ProvinceCodeDefinition
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Code") != null && object.Equals(
objA: (object)EF.Property<int?>(EntityShaperExpression:
AppDb.Models.CodeDefinitions.ProvinceCodeDefinition
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Code"),
objB: (object)EF.Property<int?>(c0, "ParentCode")))
.Any(c0 => new City{
Code = c0.CodeProtocol,
Id = c0.Code,
Name = c0.CodeDescription,
ProvinceCode = c0.ParentCode
}
.Venues
.Any())' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
but it works fine when I add any property from Venue object:
query {
regions(
where: {
provinces: { some: { cities: { some: { venues: { any: true } } } } }
}
) {
id
name
code
provinces {
id
name
code
cities {
id
name
code
venues {
id
}
}
}
}
}
Relevant log output
No response
Additional Context?
No response
Product
Hot Chocolate
Version
12.9.0
what version of automapper are you on?
10.1.1
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I'm facing the same issue using the latest stable release of HotChocolate.Data.AutoMapper (12.15.2) and AutoMapper (12.0.0). Have there been any updates to this yet?
I'm facing the same issue using the latest stable release of HotChocolate.Data.AutoMapper (12.15.2) and AutoMapper (12.0.0). Have there been any updates to this yet?
I've been testing some options and it seems that replacing ProjectTo<T> with .UseAsDataSource().For<T>() using AutoMapper.Extensions.ExpressionMapping works. You can try by installing this package (do note that AutoMapper 11+ currently does not work well with explicit expansion and HotChocolate projections, so you will need to use AutoMapper.Extensions.ExpressionMapping 4.1.5) and using modified extension method:
public static class Extensions
{
public static IQueryable<TResult> ProjectTo<TSource, TResult>(
this IQueryable<TSource> queryable,
IResolverContext context)
where TResult : notnull
{
IMapper mapper = context.Service<IMapper>();
// ensure projections are only applied once
context.LocalContextData = context.LocalContextData.SetItem(QueryableProjectionProvider.SkipProjectionKey, true);
QueryableProjectionContext visitorContext =
new(context, context.ObjectType, context.Selection.Field.Type.UnwrapRuntimeType());
QueryableProjectionVisitor.Default.Visit(visitorContext);
Expression<Func<TResult, object>> projection = visitorContext.Project<TResult, object>();
return queryable
.UseAsDataSource(mapper)
.For(projection);
}
}
Works like a charm. Thank you!