【问题标题】:How does LINQ defer execution when in a using statement在 using 语句中 LINQ 如何延迟执行
【发布时间】:2009-01-19 06:18:27
【问题描述】:

想象一下我有以下几点:

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 的第一行,导致 using 正确执行,但直到 foreach 行才运行? 有没有大神可以帮忙?

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

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

如有任何混淆,请见谅。

【问题讨论】:

  • 好问题。我被这个炸了.. Using(DataContext) 爆炸了
  • 我得到一个 ObjectDisposedException “DataContext 在 Dispose 之后访问。”当我尝试这个时。您的 MyDataContext 是 LINQ 设计器自动生成的 DataContext 吗?它是否继承自 DataContext?
  • 第三个概念:如果您使用“Using”处理您的数据上下文,如果您稍后尝试使用该数据上下文,您将获得运行时异常。我知道我已经做到了。
  • 向所有人道歉,看看我更新的问题,你是完全正确的。

标签: c# .net linq idisposable using


【解决方案1】:

我希望这根本行不通; 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
       }
    }
}

更新:如果您(cmets)不想推迟执行(即您不希望调用者处理数据上下文),那么您需要评估结果。您可以通过在结果上调用 .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;
      }
   }
}

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

【讨论】:

  • 我试图将数据库操作排除在业务逻辑之外,因此我试图不传递数据上下文。我想要的只是这些函数中的列表、可枚举和标量。
  • 每次都会访问数据库吗?或者它会做查询然后返回每个值?
  • 一旦返回第一个结果,它就会命中数据库。
  • 每次输出都会影响数据库吗?还是点击数据库一次,然后将结果保存在内存中?
  • 每个调用者的“foreach”都会访问数据库一次 - 然后在调用者的foreach期间期间有一个实时连接;迭代器块保证在调用者的 foreach 退出时关闭数据上下文(从而关闭连接)。没有任何缓冲。
【解决方案2】:

我刚刚发布了这个问题的另一个延迟执行解决方案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 块。

【讨论】:

  • 我已经接受了答案,但这是一种非常好的思维方式。 .Use() 扩展方法。对于这样的答案,您应该得到超过 +1 的评价。
  • 为那些喜欢“MyDataContext().Use()”的人准备的附加网站?! lostechies.com/keithdahlby/2009/07/23/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
相关资源
最近更新 更多