Lambdas捕获变量

时间:2008-12-04 01:38:46

标签: variables c#-3.0 subsonic lambda

考虑以下代码行:

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 + LT;&GT; c__DisplayClass0   到已知的托管提供商本机   类型。

为什么呢?我该如何解决?

1 个答案:

答案 0 :(得分:9)

从技术上讲,解决此问题的正确方法是接受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<TDelegate>。 LINQ到外部数据库(与任何类型的内存中查询相反)使用第二种转换。

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

  1. 语法树由编译器解析 - 这适用于所有代码。
  2. 在考虑变量捕获后重写语法树。捕获变量就像在普通委托或lambda中一样 - 因此创建显示类,并将捕获的本地移动到它们中(这与C#2.0匿名委托中的变量捕获行为相同)。
  3. 新语法树转换为对Expression类的一系列调用,以便在运行时创建一个忠实代表已解析文本的对象树。
  4. LINQ to external data sources应该接受这个表达式树并解释它的语义内容,并将树中的符号表达式解释为引用特定于其上下文的事物(例如DB中的列)或立即值转换。通常,System.Reflection用于查找特定于框架的属性以指导此转换。

    然而,看起来SubSonic没有正确处理它无法找到特定于域的对应关系的符号引用;而不是评估符号引用,它只是踢。因此,这是一个SubSonic问题。