8

假设我有一个IQueryable<T>表达式,我想封装它的定义、存储它并重用它,或者稍后将它嵌入到更大的查询中。例如:

IQueryable<Foo> myQuery =
    from foo in blah.Foos
    where foo.Bar == bar
    select foo;

现在我相信我可以保留那个 myQuery 对象并像我描述的那样使用它。但有些事情我不确定:

  1. 如何最好地对其进行参数化?最初我在一个方法中定义了这个,然后返回了IQueryable<T>作为方法的结果。这样我可以将blahand定义bar为方法参数,我猜它IQueryable<T>每次都会创建一个新参数。这是封装逻辑的最佳方式IQueryable<T>吗?还有其他方法吗?

  2. 如果我的查询解析为标量而不是IQueryable怎么办? 例如,如果我希望这个查询与显示的完全一样,但追加.Any()只是让我知道是否有任何匹配的结果?如果我添加(...).Any()然后结果是bool并立即执行,对吗?有没有办法在不立即执行的情况下利用这些Queryable运算符(Any,SindleOrDefault等)?LINQ-to-SQL 如何处理这个问题?

编辑:第 2 部分实际上更多是关于尝试了解IQueryable<T>.Where(Expression<Func<T, bool>>)IQueryable<T>.Any(Expression<Func<T, bool>>). 在创建要延迟执行的较大查询时,后者似乎不那么灵活。Where()可以附加,然后可以稍后附加其他构造,然后最终执行。由于Any()返回一个标量值,听起来它会在构建查询的其余部分之前立即执行。

4

4 回答 4

5
  1. 在使用 DataContext 时,您必须非常小心地传递 IQueryables,因为一旦处理了上下文,您将无法再在该 IQueryable 上执行。如果您不使用上下文,那么您可能没问题,但请注意这一点。

  2. .Any() 和 .FirstOrDefault()不会被延迟。当您调用它们时,它们导致执行发生。但是,这可能不会像您认为的那样。例如,在 LINQ to SQL 中,如果您对 IQueryable 执行 .Any() ,它基本上充当 IF EXISTS( SQL HERE )。

如果您愿意,可以像这样链接 IQueryable:

var firstQuery = from f in context.Foos
                    where f.Bar == bar
                    select f;

var secondQuery = from f in firstQuery
                    where f.Bar == anotherBar
                    orderby f.SomeDate
                    select f;

if (secondQuery.Any())  //immediately executes IF EXISTS( second query in SQL )
{
    //causes execution on second query 
    //and allows you to enumerate through the results
    foreach (var foo in secondQuery)  
    {
        //do something
    }

    //or

    //immediately executes second query in SQL with a TOP 1 
    //or something like that
    var foo = secondQuery.FirstOrDefault(); 
}
于 2009-08-20T19:03:08.317 回答
2

比缓存 IQueryable 对象更好的选择是缓存表达式树。所有 IQueryable 对象都有一个名为 Expression 的属性(我相信),它表示该查询的当前表达式树。

在稍后的某个时间点,您可以通过调用 queryable.Provider.CreateQuery(expression) 或直接在任何提供程序上(在您的情况下为 Linq2Sql 数据上下文)重新创建查询。

然而,参数化这些表达式树稍微困难一些,因为它们使用 ConstantExpressions 来构建一个值。为了参数化这些查询,每次需要不同的参数时都必须重建查询。

于 2009-08-20T19:16:58.443 回答
2

Any()使用这种方式是延迟的。

var q = dc.Customers.Where(c => c.Orders.Any());

Any()使用这种方式不会延迟,但仍会转换为 SQL(整个客户表不会加载到内存中)。

bool result = dc.Customers.Any();

如果你想要一个延迟的 Any(),这样做:

public static class QueryableExtensions
{
  public static Func<bool> DeferredAny<T>(this IQueryable<T> source)
  {
    return () => source.Any();
  }
}

像这样称呼它:

Func<bool> f = dc.Customers.DeferredAny();
bool result = f();

缺点是这种技术不允许子查询。

于 2009-08-20T19:36:38.607 回答
0

在表达式中创建查询的部分应用

Func[Bar,IQueryable[Blah],IQueryable[Foo]] queryMaker = 
(criteria, queryable) => from foo in queryable.Foos
        where foo.Bar == criteria
        select foo;

然后你可以使用它...

IQueryable[Blah] blah = context.Blah;
Bar someCriteria = new Bar();
IQueryable[Foo] someFoosQuery = queryMaker(blah, someCriteria);

如果您想让查询更便携/可重用,可以将查询封装在一个类中。

public class FooBarQuery
{
  public Bar Criteria { get; set; }

  public IQueryable[Foo] GetQuery( IQueryable[Blah] queryable )
  {
     return from foo in queryable.Foos
        where foo.Bar == Criteria
        select foo;
  } 
}
于 2009-09-17T03:29:18.327 回答