繁体   English   中英

如何维护LINQ延迟执行?

[英]How to maintain LINQ deferred execution?

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

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

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

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

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

编辑:第2部分更多的是试图了解IQueryable<T>.Where(Expression<Func<T, bool>>)IQueryable<T>.Any(Expression<Func<T, bool>>)之间的限制差异IQueryable<T>.Any(Expression<Func<T, bool>>) 在创建要延迟执行的大型查询时,似乎后者并不灵活。 可以附加Where() ,然后可以添加其他构造,然后最终执行。 由于Any()返回标量值,因此它听起来会在构建其余查询之前立即执行。

  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(); 
}

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

在以后的某个时间点,您可以通过调用queryable.Provider.CreateQuery(表达式)重新创建查询,或者直接在提供者的任何内容(在您的情况下是Linq2Sql数据上下文)中重新创建查询。

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

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

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

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

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();

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

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

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;
  } 
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM