AutoMapper / AutoMapper.Extensions.ExpressionMapping

MIT License
143 stars 39 forks source link

System.Enum MethodCallExpression (HasFlag) mapped to TDestinationEnum MethodCallExpression #179

Closed engenb closed 6 months ago

engenb commented 6 months ago

Microsoft.AspNetCore.OData 8.2.5 AutoMapper.AspNetCore.OData.EFCore 5.0.0 AutoMapper.Extensions.ExpressionMapping 7.0.0 Microsoft.EntityFrameworkCore.SqlServer 8.0.4

I'll summarize here, but the original issue can be found on StackOverflow. I've also set up a repository demonstrating the issue on GitHub.

I need an OData query endpoint that supports the enum has operator such as $filter=type has 'SomeEnumValue'. The ODataQueryOptions<TModel>.ToFilterExpression() extension in AutoMapper.AspNetCore.OData.EFCore is producing an InstanceMethodCallExpression where the (MethodInfo) Method is (System.Enum).HasFlag(TModelEnum). This seems on track in, in my opinion.

If I map this expression, the resulting mapped InstanceMethodCallExpression has (MethodInfo) Method (TDestinationEnum).HasFlag(TDestinationEnum). I've confirmed this is causing a problem for me downstream when this filter is used later with EFCore. The stack trace can be found in the SO post mentioned above - I won't muck up this issue with a probably unnecessary stack trace. I think this is a result of XpressionMapperVisitor.VisitMethodCall, maybe GetInstanceExpression, found here.

This causes a problem when used with EFCore as the Linq -> SQL conversion doesn't accept the (TDestinationEnum).HasFlag(...) method. It seems to only accept the (System.Enum).HasFlag(...) flavor of this method.

FWIW, I think I can work around this by using my own replacement IQueryable extensions in place of AutoMapper.AspNetCore.OData.EFCore with my own ExpressionVisitor to adjust the mapped InstanceMethodCallExpression back to the (System.Enum).HasFlag(...) one after it has been mapped with this framework.

Would you agree that this may be a "special" enum case that could/should be accounted for in ExpressionMapping, to change the parameter type, but not change the method's declaring/reflected type?

Or, since this is EFCore-specific, is this something AutoMapper.AspNetCore.OData.EFCore needs to handle after the expression has been mapped to ensure the resulting filter is efcore-compatible?

Curious to hear thoughts from the authors of this framework. I'd be happy to help with a PR as well, but I can't do that immediately because my immediate priority is getting a workaround in place. I should be able to circle back to this later in the week though.

BlaiseD commented 6 months ago

I think you're correct. If the initial type System.Enum is not in the configuration it should not be updated. It looks like the MethodInfo for the mapped expression should be created from the declaring type.

BlaiseD commented 6 months ago

@engenb - can you confirm the myget build works?

engenb commented 6 months ago

sorry for the delay @BlaiseD . I was able to confirm this morning that 7.0.1-alpha.0.1 fixes the issue - thanks!