Why Linq “where” expression after Select gets evaluated locally when created through a generic method?

六眼飞鱼酱① 提交于 2019-12-03 16:24:00

The problem is similar to The LINQ expression could not be translated for base property and How to use inherited properties in EF Core expressions?, but in this case both the DeclaringType and ReflectedType of the MemberInfo point to ISomeable interface rather than the actual class.

Again this somehow is confusing EF Core in the Select scenario. I've checked the latest EF Core 3.0 preview and it also doesn't work. You might consider posting it to their issue tracker.

The only workaround I could offer so far is to postprocess the expression with custom ExpressionVisitor and bind the member accessors to the actual class. Something like this:

public static partial class ExpressionUtils
{
    public static Expression<T> FixMemberAccess<T>(this Expression<T> source)
    {
        var body = new MemberAccessFixer().Visit(source.Body);
        if (body == source.Body) return source;
        return source.Update(body, source.Parameters);
    }

    class MemberAccessFixer : ExpressionVisitor
    {
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != null && node.Expression.Type != node.Member.DeclaringType)
            {
                var member = node.Expression.Type.GetMember(node.Member.Name).Single();
                if (member.ReflectedType != member.DeclaringType)
                    member = member.DeclaringType.GetMember(member.Name).Single();
                return Expression.MakeMemberAccess(node.Expression, member);
            }
            return base.VisitMember(node);
        }
    }
}

and now

var someCriteria2 = GetCriteria<MySimpleEntity>().FixMemberAccess();

will produce the exact expression as the working compile time someCriteria expression and no client evaluation.

Note: You still need the class constraint in order to avoid the casting issue from your previous question and to make this workaround work.

I think the problem with your code is

GetCriteria<MySimpleEntity>();

linq cannot translate that directly to sql or doesn't have a direct translation. If you want to use it. Execute ToList() then add .Where(someCriteria2).ToList();. In watcher it sees/evaluate it as the same. But in the query itself, generating the sql doesn't seem to work that way.

I have also experienced that in my DateTime extension methods even in converting it to string in my Where i had to execute it outside of my linq query and add it

var dateUtc = DateTime.UtcNow.ExtensionMethod();

...Where(x => x.Date >= dateUtc)

or I execute FirstorDefault, First, ToList() first before my select and/or where

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!