如何使匿名方法在LINQ to Entities中运行

本文关键字:to Entities 运行 LINQ 何使匿 方法 | 更新日期: 2023-09-27 17:59:57

我正在尝试构建一个通用方法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的方法?

如何使匿名方法在LINQ to Entities中运行

要实现这一点,您需要将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<T>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; }
}