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

发布于 2024-07-13 05:15:45 字数 725 浏览 8 评论 0原文

想象一下我有以下内容:

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() 扩展方法,它当然会枚举结果。 勾选答案的行为对于所回答的实际问题来说是完全正确的。

抱歉造成任何混乱。

Imagine I have the following:

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

According to the documentation the linq execution will defer till I actual enumerate the result, which occurs in the line at the foreach. However the using statement should force the object to be collected reliably at the end of the call to MyFunct().

What actually happens, when will the disposer run and/or the result run?

Only thing I can think of is the deferred execution is computed at compile time, so the actual call is moved by the compiler to the first line of the foreach, causing the using to perform correctly, but not run until the foreach line?
Is there a guru out there who can help?

EDIT: NOTE: This code does work, I just don't understand how.

I did some reading and I realised in my code that I had called the ToList() extension method which of course enumerates the result. The ticked answer's behaviour is perfectly correct for the actual question answered.

Sorry for any confusion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

毁虫ゝ 2024-07-20 05:15:45

我希望这根本行不通; Select 被延迟,因此此时尚未消耗任何数据。 但是,由于您已经处理了数据上下文(在离开 MyFunc 之前),因此它将永远无法获取数据。 更好的选择是将数据上下文传递到方法中,以便消费者可以选择生命周期。 另外,我建议返回 IQueryable ,以便消费者可以“组合”结果(即添加 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;
      }
   }
}

现在可以延迟而不传递数据上下文。

I would expect that to simply not work; the Select is deferred, so no data has been consumed at this point. However, since you have disposed the data-context (before leaving MyFunc), it will never be able to get data. A better option is to pass the data-context into the method, so that the consumer can choose the lifetime. Also, I would recommend returning IQueryable<T> so that the consumer can "compose" the result (i.e. add OrderBy / Skip / Take / Where etc, and have it impact the final query):

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

Update: if you (comments) don't want to defer execution (i.e. you don't want the caller dealing with the data-context), then you need to evaluate the results. You can do this by calling .ToList() or .ToArray() on the result to buffer the values.

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

If you want to keep it deferred in this case, then you need to use an "iterator block":

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

This is now deferred without passing the data-context around.

失与倦" 2024-07-20 05:15:45

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

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

I just posted another deferred-execution solution to this problem here, including this sample code:

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

The Use() extension method essentially acts like a deferred using block:

public static IEnumerable<T> Use<T>(this T obj) where T : IDisposable
{
    try
    {
        yield return obj;
    }
    finally
    {
        if (obj != null)
            obj.Dispose();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文