带有捕获变量的 Lambda 表达式

发布于 2024-07-09 22:23:26 字数 613 浏览 5 评论 0原文

考虑以下代码行:

private void DoThis() {
    int i = 5;
    var repo = new ReportsRepository<RptCriteriaHint>();

    // This does NOT work
    var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();      

    // This DOES work
    var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();    
}

因此,当我将实际数字硬连接到 lambda 函数时,它工作得很好。 当我在表达式中使用捕获的变量时,它会返回以下错误:

不存在对象类型的映射 ReportBuilder.Reporter+<>c__DisplayClass0 到已知的本地托管提供商 类型。

为什么? 我该如何修复它?

Consider the following line of code:

private void DoThis() {
    int i = 5;
    var repo = new ReportsRepository<RptCriteriaHint>();

    // This does NOT work
    var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();      

    // This DOES work
    var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();    
}

So when I hardwire an actual number into the lambda function, it works fine. When I use a captured variable into the expression it comes back with the following error:

No mapping exists from object type
ReportBuilder.Reporter+<>c__DisplayClass0
to a known managed provider native
type.

Why? How can I fix it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黯然 2024-07-16 22:23:27

从技术上讲,解决此问题的正确方法是让框架从 lambda 接受表达式树来评估 i 引用; 换句话说,这是某些特定框架的 LINQ 框架限制。 它当前正在尝试做的是将 i 解释为对数据库中已知的某种类型(提供程序)的成员访问。 由于 lambda 变量捕获的工作方式,i 局部变量实际上是隐藏类中的一个字段,该类具有有趣的名称,但提供程序无法识别该字段。

所以,这是一个框架问题。

如果你真的必须勉强度日,你可以手动构建表达式,如下所示:

ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
    Expression.Lambda<Func<RptCriteriaHint,bool>>(
        Expression.Equal(
            Expression.MakeMemberAccess(
                x,
                typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
            Expression.Constant(i)),
        x)).ToList();

...但这只是受虐狂。

您对此条目的评论促使我进一步解释。

Lambda 可转换为以下两种类型之一:具有正确签名的委托,或正确签名的Expression。 外部数据库的 LINQ(与任何类型的内存中查询相反)使用第二种转换。

粗略地说,编译器通过以下方式将 lambda 表达式转换为表达式树:

  1. 语法树由编译器解析 - 所有代码都会发生这种情况。
  2. 在考虑变量捕获后重写语法树。 捕获变量就像在普通委托或 lambda 中一样 - 因此会创建显示类,并将捕获的局部变量移入其中(这与 C# 2.0 匿名委托中的变量捕获行为相同)。
  3. 新的语法树被转换为对Expression 类的一系列调用,以便在运行时创建一个忠实表示已解析文本的对象树。

LINQ to 外部数据源应该采用此表达式树并解释它的语义内容,并将树内的符号表达式解释为引用特定于其上下文的事物(例如数据库中的列)或要转换的立即值。 通常,System.Reflection 用于查找特定于框架的属性来指导此转换。

然而,SubSonic 似乎没有正确处理符号引用,它无法找到特定于域的对应关系; 它不是评估象征性参考,而是只是下注。 因此,这是一个亚音速问题。

Technically, the correct way to fix this is for the framework that is accepting the expression tree from your lambda to evaluate the i reference; in other words, it's a LINQ framework limitation for some specific framework. What it is currently trying to do is interpret the i as a member access on some type known to it (the provider) from the database. Because of the way lambda variable capture works, the i local variable is actually a field on a hidden class, the one with the funny name, that the provider doesn't recognize.

So, it's a framework problem.

If you really must get by, you could construct the expression manually, like this:

ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
    Expression.Lambda<Func<RptCriteriaHint,bool>>(
        Expression.Equal(
            Expression.MakeMemberAccess(
                x,
                typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
            Expression.Constant(i)),
        x)).ToList();

... but that's just masochism.

Your comment on this entry prompts me to explain further.

Lambdas are convertible into one of two types: a delegate with the correct signature, or an Expression<TDelegate> of the correct signature. LINQ to external databases (as opposed to any kind of in-memory query) works using the second kind of conversion.

The compiler converts lambda expressions into expression trees, roughly speaking, by:

  1. The syntax tree is parsed by the compiler - this happens for all code.
  2. The syntax tree is rewritten after taking into account variable capture. Capturing variables is just like in a normal delegate or lambda - so display classes get created, and captured locals get moved into them (this is the same behaviour as variable capture in C# 2.0 anonymous delegates).
  3. The new syntax tree is converted into a series of calls to the Expression class so that, at runtime, an object tree is created that faithfully represents the parsed text.

LINQ to external data sources is supposed to take this expression tree and interpret it for its semantic content, and interpret symbolic expressions inside the tree as either referring to things specific to its context (e.g. columns in the DB), or immediate values to convert. Usually, System.Reflection is used to look for framework-specific attributes to guide this conversion.

However, it looks like SubSonic is not properly treating symbolic references that it cannot find domain-specific correspondences for; rather than evaluating the symbolic references, it's just punting. Thus, it's a SubSonic problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文