Как LINQ откладывает выполнение, когда в операторе using

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

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

EDIT: ПРИМЕЧАНИЕ. Этот код работает, я просто не понимаю, как это сделать.

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

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

Ответ 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
       }
    }
}

Обновление: если вы (комментарии) не хотите откладывать выполнение (т.е. вы не хотите, чтобы вызывающий абонент имел дело с контекстом данных), вам необходимо оценить результаты. Вы можете сделать это, вызвав .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

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

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.