graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

AutoMapperQueryableExtensions with explicit expansion doesn't work well with filtering

Open Mephistofeles opened this issue 3 years ago • 5 comments

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

Mephistofeles avatar May 18 '22 17:05 Mephistofeles

what version of automapper are you on?

PascalSenn avatar May 18 '22 19:05 PascalSenn

10.1.1

Mephistofeles avatar May 18 '22 20:05 Mephistofeles

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.

stale[bot] avatar Sep 15 '22 21:09 stale[bot]

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?

poernbacher avatar Dec 16 '22 15:12 poernbacher

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);
    }
}

Mephistofeles avatar Dec 16 '22 16:12 Mephistofeles

Works like a charm. Thank you!

poernbacher avatar Dec 19 '22 08:12 poernbacher