Как LINQ откладывает выполнение в выражении using
-
19-08-2019 - |
Вопрос
Представьте, что у меня есть следующее:
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 (), который, конечно, перечисляет результат. Поведение, помеченное галочкой, является совершенно правильным для фактического ответа на вопрос. Р>
Извините за путаницу.
Решение
Я ожидаю, что это просто не сработает; Select
откладывается, поэтому на данный момент данные не были использованы. Однако, поскольку вы удалили контекст данных (перед тем как покинуть MyFunc
), он никогда не сможет получить данные. Лучший вариант - передать контекст данных в метод, чтобы потребитель мог выбрать время жизни. Кроме того, я бы порекомендовал вернуть IQueryable<T>
, чтобы потребитель мог & Quot; составить & Quot; результат (например, добавьте 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();
}
}
Если вы хотите сохранить его в этом случае отложенным, вам нужно использовать " 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;
}
}
}
Теперь это отложено без передачи контекста данных.
Другие советы
Я только что опубликовал другое решение этой проблемы с отложенным выполнением здесь , включая этот пример кода:
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
блок.