代码之家  ›  专栏  ›  技术社区  ›  Spence

LINQ在using语句中如何延迟执行

  •  12
  • Spence  · 技术社区  · 17 年前

    想象一下,我有以下内容:

    private IEnumerable MyFunc(parameter a)
    {
       using(MyDataContext dc = new MyDataContext)
       {
          return dc.tablename.Select(row => row.parameter == a);
       }
    }
    
    private void UsingFunc()
    {
       var result = MyFunc(new a());
    
       foreach(var row in result)
       {
          //Do something
       }
    }
    

    根据文档,linq执行将推迟到我实际枚举结果为止,该结果出现在foreach的行中。但是,using语句应该强制在调用MyFunct()结束时可靠地收集对象。

    实际情况是什么,处理器何时运行和/或结果何时运行?

    我唯一能想到的是,延迟执行是在编译时计算的,因此编译器将实际调用移动到foreach的第一行,使use正确执行,但直到foreach行才运行? 有没有一位大师可以提供帮助?

    编辑: 注意:这段代码确实有效,只是我不明白它是如何工作的。

    我做了一些阅读,在代码中意识到我调用了ToList()扩展方法,该方法当然枚举了结果。勾选答案的行为与实际回答的问题完全正确。

    很抱歉造成任何混淆。

    2 回复  |  直到 17 年前
        1
  •  12
  •   Marc Gravell    17 年前

    我希望这根本行不通;这个 Select 被推迟,因此此时没有数据被消耗。但是,由于您已经处理了数据上下文(在离开之前 MyFunc ),它永远无法获得数据。更好的选择是传递数据上下文 到…里面 这种方法,让消费者可以选择终身。另外,我建议你回来 IQueryable<T> 以便消费者可以“组合”结果(即添加 OrderBy / Skip / Take / Where 并使其影响最终查询):

    // this could also be an instance method on the data-context
    internal static IQueryable<SomeType> MyFunc(
        this MyDataContext dc, parameter a)
    {
       return dc.tablename.Where(row => row.parameter == a);
    }
    
    private void UsingFunc()
    {
        using(MyDataContext dc = new MyDataContext()) {
           var result = dc.MyFunc(new a());
    
           foreach(var row in result)
           {
               //Do something
           }
        }
    }
    

    更新:如果你(注释)不想推迟执行(即你不想让调用者处理数据上下文),那么你需要评估结果。你可以打电话给 .ToList() 或 .ToArray() 对结果进行缓冲。

    private IEnumerable<SomeType> MyFunc(parameter a)
    {
       using(MyDataContext dc = new MyDataContext)
       {
          // or ToList() etc
          return dc.tablename.Where(row => row.parameter == a).ToArray();
       }
    }
    

    如果你想在这种情况下保持延迟,那么你需要使用一个“迭代器块”:

    private IEnumerable<SomeType> MyFunc(parameter a)
    {
       using(MyDataContext dc = new MyDataContext)
       {
          foreach(SomeType row in dc
              .tablename.Where(row => row.parameter == a))
          {
            yield return row;
          }
       }
    }
    

    现在,在不传递数据上下文的情况下,这被推迟了。

        2
  •  8
  •   dahlbyk    17 年前

    我刚刚发布了另一个延迟执行解决方案来解决这个问题 here ,包括以下示例代码:

    IQueryable<MyType> MyFunc(string myValue)
    {
        return from dc in new MyDataContext().Use()
               from row in dc.MyTable
               where row.MyField == myValue
               select row;
    }
    
    void UsingFunc()
    {
        var result = MyFunc("MyValue").OrderBy(row => row.SortOrder);
        foreach(var row in result)
        {
            //Do something
        }
    }
    

    这个 Use() 扩展方法本质上就像一个延迟方法 using 块:

    public static IEnumerable<T> Use<T>(this T obj) where T : IDisposable
    {
        try
        {
            yield return obj;
        }
        finally
        {
            if (obj != null)
                obj.Dispose();
        }
    }
    
    推荐文章