为了账号安全,请及时绑定邮箱和手机立即绑定

为什么在通过泛型方法创建时,Select 后的 Linq“where”表达式会在本地求值?

为什么在通过泛型方法创建时,Select 后的 Linq“where”表达式会在本地求值?

C#
慕尼黑8549860 2023-08-13 15:43:48
我正在使用泛型实现规范模式,并尝试动态地将标准应用于映射实体的投影简单(未映射)版本。一般来说,它工作得很好,但是一旦我在表达式之后添加Select并应用,Linq 就会在本地计算表达式Where。如果我将其构建为局部变量并传递给相同的Where.这是简化的相关代码片段:public interface ISomeable{    string Some { get; set; }}public static Expression<Func<T, bool>> GetCriteria<T>() where T : class, ISomeable    {  return e => (e.Some == "Hello"); }...Expression<Func<MySimpleEntity, bool>> someCriteria = e => (e.Some == "Hello");Expression<Func<MySimpleEntity, bool>> someCriteria2 = GetCriteria<MySimpleEntity>();var query = db.Entities       .Select(s => new MySimpleEntity { Id = s.Id, Some = s.Some });// if this Select is removed and MySimpleEntity in both expressions replaced with MyFullEntity, // the issue disappears// this succeedsvar filteredQueryResults = query.Where(someCriteria).ToList();// at this point, someCriteria2 is set to the same e => (e.Some == "Hello");// this fails: why is it evaluated locally and not in SQL? <-----filteredQueryResults = query.Where(someCriteria2).ToList();// results in a warning:                /*                 * 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning:                  * The LINQ expression 'where (new MySimpleEntity() {Id = [s].Id, Some = [s].Some}.Some == "Hello")'                  * could not be translated and will be evaluated locally.'.                  */如何让它生成正确的 SQL 而不是本地评估someCriteria2?我怀疑我需要某种类型的选角,但不确定在哪里。两者在调试器中看起来完全相同,所以我不知道为什么 Linq 会以不同的方式对待它们someCriteria。someCriteria2我创建了一个最小的 .Net Core Console 应用程序来重现该案例。完整的要点在这里:https://gist.github.com/progmars/eeec32a533dbd2e1f85e551db1bc53f8NuGet 依赖项: Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" Microsoft.Extensions.Logging" Version="2.2.0" Microsoft.Extensions.Logging.Console" Version="2.2.0"一些解释:这与同一个查询执行两次这一事实无关。如果我注释掉第一个,query.Where(someCriteria).ToList()第二个调用someCriteria2仍然无法生成有效的 SQL。但是,如果我用第二个查询替换someCriteria2为someCriteria并让它运行,我会在控制台中得到两个完全有效的 SQL 查询。因此,这一切都与泛型someCriteria2和Select投影有关 - 由于某种原因,Linq 不会以相同的方式对待这两个变量,即使编译器(和调试器监视)认为它们是相同的确切类型。
查看完整描述

2 回答

?
慕姐8265434

TA贡献1813条经验 获得超2个赞

问题类似于无法转换基本属性的 LINQ 表达式和如何在 EF Core 表达式中使用继承的属性?,但在这种情况下, 和DeclaringTypeReflectedType指向MemberInfo接口ISomeable而不是实际的类。

这又在某种程度上让场景中的 EF Core 感到困惑Select。我检查了最新的 EF Core 3.0 预览版,它也不起作用。您可以考虑将其发布到他们的问题跟踪器。

到目前为止,我可以提供的唯一解决方法是使用自定义后处理表达式ExpressionVisitor并将成员访问器绑定到实际的类。像这样的东西:

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

        }

    }

}

现在


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

将生成与工作编译时表达式完全相同的表达式someCriteria,并且没有客户端评估。


注意:您仍然需要约束class,以避免上一个问题中的转换问题并使此解决方法发挥作用。


查看完整回答
反对 回复 2023-08-13
?
慕婉清6462132

TA贡献1804条经验 获得超2个赞

我认为你的代码的问题是

GetCriteria<MySimpleEntity>();

linq无法直接翻译sql或没有直接翻译。如果你想使用它。执行ToList()然后添加.Where(someCriteria2).ToList();. 在观察者中,它认为/评估它是相同的。但在查询本身中,生成 sql 似乎并不是这样工作的。

我还经历过,在我的DateTime扩展方法中,即使将其转换为string我的扩展Where方法,我也必须在linq查询之外执行它并添加它

var dateUtc = DateTime.UtcNow.ExtensionMethod();

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

或者我在我的和/或FirstorDefault, First, ToList()之前先执行selectwhere


查看完整回答
反对 回复 2023-08-13
  • 2 回答
  • 0 关注
  • 94 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信