【问题标题】:How do I make an anonymous method run in LINQ to Entities?如何使匿名方法在 LINQ to Entities 中运行?
【发布时间】:2011-06-21 14:15:38
【问题描述】:

我正在尝试构建一个通用方法,EF4.1 在数据库和本地内存中查找与特定条件匹配的表中的特定行。

到目前为止,这就是我所拥有的。

这是来电者。

dbEntities.MyTables.LocalAndDb(delegate(MyTable s)
                { return s.Description.Contains("test"); });

这是 LocalAndDb

public static object LocalAndDb<T>(this DbSet<T> myTable, Func<T, bool> function) where T : class
{   
    // look in local 
    var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault()
    // if not exist, look in the database
    if (item == null)
    {
        Expression<Func<T, bool>> predicate = (u) => function(u);
        item = myTable.Where(predicate).FirstOrDefault();
    }
    return item;
}

问题出在这一行。

    item = myTable.Where(predicate).FirstOrDefault();

当它调用数据库时,它会抛出这个错误。

“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'。”

我想这是因为我传入了一个匿名方法,但它不知道如何将其转换为 SQL。我认为将其转换为 Expression 对象可以解决问题,但它仍然不适合我。

我需要做什么才能使匿名方法变成 LINQ 可以转换为 SQL 的东西?

【问题讨论】:

    标签: c# .net entity-framework entity-framework-4.1 expression


    【解决方案1】:

    要完成这项工作,您需要将 lambda 表达式作为表达式树传递给 LocalAndDb(以便 LINQ to Entities 可以分析代码并将其转换为 SQL):

    public static object LocalAndDb<T>(this DbSet<T> myTable, 
        Expression<Func<T, bool>> expr) where T : class {
        // ...
        if (item == null) {
            item = myTable.Where(expr).FirstOrDefault();
        }
        return item;
    }
    

    那么,问题当然是在检查内存数据时无法执行表达式树。解决此问题的一种方法是使用Expression&lt;T&gt;Compile 方法,但这会有点低效(取决于您的场景)。

    另一种选择是将条件作为函数和表达式树传递:

    public static object LocalAndDb<T>(this DbSet<T> myTable, 
        Func<T, boo> function, Expression<Func<T, bool>> expr) where T : class {
        var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault();
        if (item == null) {
            item = myTable.Where(expr).FirstOrDefault();
        }
        return item;
    }
    
    table.LocalAndDb(t => t.Foo > 10, t => t.Foo > 10);
    

    这有点难看,但它不需要在运行时进行低效编译。如果你想要一个稍微复杂一点的解决方案,那么你可以定义自己的类型来保留预编译的函数:

    class Precompiled<T1, T2> {
      public Precompiled(Expression<Func<T1, T2>> expr) {
        this.Expression = expr;
        this.Function = expr.Compile();
      }
      public Expression<Func<T1,T2>> Expression { get; private set; }
      public Func<T1,T2> Function { get; private set; }
    }
    

    【讨论】:

    • 编译成功!谢谢谢谢谢谢!当谈到 EF 时,我是一个完整的新手。但我现在将对性能方面进行一些研究。从您所说的看来,当您“编译”表达式时,无论是在方法中,还是在签名中还是在 Precompiled 构造函数中,这都会影响性能?
    • @stickman - Precompiled 类的想法是,您可以轻松地构造它一次(调用一次Compile),然后在多次调用LocalAndDb 时重复使用结果,因此不会在每次调用期间重新编译表达式。
    猜你喜欢
    • 2011-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-23
    • 1970-01-01
    • 2013-08-01
    相关资源
    最近更新 更多