koenbeuk / EntityFrameworkCore.Projectables

Project over properties and functions in your linq queries
MIT License
297 stars 20 forks source link

Method groups inside Projectables #62

Closed cyril265 closed 1 year ago

cyril265 commented 1 year ago

Hey, awesome library. I just replaced LinqKit with it 😃

There might be an issue with method groups inside projectables. If you do the following inside a Projectable it fails because method groups are converted to Func<> instead of Expression<> :

entity.Collection.Select(MapChildren) // Collection is an IEnumerable and MapChildren is another Projectable

Error message:

System.ArgumentException: Expression of type 'System.Func`2[Participant,Substitutes.SubstitutedParticipantRecord]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[Participant,Substitutes.SubstitutedParticipantRecord]]' of method 'System.Linq.IQueryable`1[Substitutes.SubstitutedParticipantRecord] Select[Participant,SubstitutedParticipantRecord](System.Linq.IQueryable`1[Participant], System.Linq.Expressions.Expression`1[System.Func`2[Participant,Substitutes.SubstitutedParticipantRecord]])' (Parameter 'arg1')
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1)

So it's kind of a C#/EF limitation but it might be worth rewritting Projectables that use method groups. Also Rider for example offers a quick fix to replace calls with method group. Which makes it even more confusing/error prone.

koenbeuk commented 1 year ago

For reference: The relevant generated expression tree of using a method group contains a call to CreateDelegate instead of a call to an actual method.

.Call .Constant<System.Reflection.MethodInfo>(Entity Transform(Entity)).CreateDelegate(
    .Constant<System.Type>(System.Func`2[EntityFrameworkCore.Projectables.FunctionalTests.MethodGroupTests+Entity,EntityFrameworkCore.Projectables.FunctionalTests.MethodGroupTests+Entity]),
    .Constant<EntityFrameworkCore.Projectables.FunctionalTests.MethodGroupTests>(EntityFrameworkCore.Projectables.FunctionalTests.MethodGroupTests))

We will have to update ProjectableExpressionReplacer.cs to understand a call to CreateDelegate and replace it with the generated expression.

https://github.com/koenbeuk/EntityFrameworkCore.Projectables/blob/88cebbcabcfb16dd2abb7a205978e2ad14254e1b/src/EntityFrameworkCore.Projectables/Services/ProjectableExpressionReplacer.cs#L42

koenbeuk commented 1 year ago

Closed by #87