Как LINQ откладывает выполнение в выражении using - PullRequest
11 голосов
/ 19 января 2009

Представьте, что у меня есть следующее:

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, что приводит к правильному выполнению использования, но не выполняется до строки foreach ? Есть ли гуру, который может помочь?

РЕДАКТИРОВАТЬ: ПРИМЕЧАНИЕ: этот код работает, я просто не понимаю, как.

Я немного прочитал и понял в своем коде, что я вызвал метод расширения ToList (), который, конечно, перечисляет результат. Поведение, помеченное галочкой, является совершенно правильным для фактического ответа на вопрос.

Извините за путаницу.

Ответы [ 2 ]

12 голосов
/ 19 января 2009

Я ожидаю, что это просто не сработает; 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;
      }
   }
}

Теперь это отложено без передачи контекста данных.

8 голосов
/ 23 июля 2009

Я только что опубликовал другое решение этой проблемы с отложенным выполнением здесь , включая пример кода:

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 блок.

...