Представьте, что у меня есть следующее:
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 (), который, конечно, перечисляет результат. Отмеченный ответ поведение полностью соответствует фактическому ответу на вопрос.
Приносим извинения за путаницу.
Я получаю ObjectDisposedException «DataContext доступен после Dispose». когда я попробую это. Ваш MyDataContext - это автоматически созданный DataContext из конструктора LINQ? Наследуется ли он от DataContext?
Я в-третьих, идея: если вы избавляетесь от своего контекста данных, используя "Использование", вы получите исключение времени выполнения, если попытаетесь использовать контекст данных позже. Я знаю, что сделал это.
Приношу извинения всем, см. Мой обновленный вопрос, вы совершенно правы.





Я ожидал, что это просто не сработает; 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;
}
}
}
Теперь это отложено без передачи контекста данных.
Я пытаюсь исключить операции с базой данных из бизнес-логики, поэтому я стараюсь не передавать текст данных. Все, что мне нужно, это списки, перечисления и скаляры из этих функций.
Будет ли это попадать в базу данных каждый раз? Или он выполнит запрос, а затем вернет каждое значение?
Он попадает в БД, как только возвращается первый результат.
он попадает в дБ каждый год? или ударил по БД один раз, а затем сохранил результаты в памяти?
Он попадает в базу данных один раз за звонящий «foreach» - затем имеет активное соединение в течение для foreach вызывающего абонента; блок итератора гарантирует, что контекст данных закрыт (и, следовательно, соединение закрыто), когда вызывающий foreach завершается. Ничего не буферизуется.
@Marc: Разве SQL не будет выполняться один раз, а затем буферизироваться для каждого оператора yield?
@Cameron - нет, действительно не будет; не все в буфере; на live-data-context / sql-connection ссылается конечный автомат, созданный компилятором. Один из основных приемов LINQ заключается в том, что он полностью отложен: когда вы выполняете «MoveNext ()» (foreach), он считывает данные из входящего потока TDS (SQL).
Даже если DataReader удерживается для повторения, SQL все равно выполняется один раз.
Если я вызываю MyFunc (1) .Take (1) .ToList () и там две строки, не удаляется ли DataContext должным образом?
@David - ортогонален утилизации; бит это не повлияет на это - просто потребляется только одна запись. итераторы все еще расположены должным образом, благодаря (отчасти) тому, как определен "foreach".
Я думаю, что есть пробел в моем понимании использования / finally в этих методах итератора. Спасибо, что помог мне закрыть его.
Я только что опубликовал еще одно решение для этой проблемы здесь с отложенным выполнением, включая этот пример кода:
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/…
хороший вопрос. Я был разбомблен этим .. Использование (DataContext) впечатляюще взрывается