2010-05-17 1 views
7

Обновление - Ответ, очевидно, что DbLinq не реализует Dispose() должным образом. D'о!Почему можно перечислить запрос DbLinq после вызова Dispose() в DataContext?


Ниже все рода заблуждение - Итог: DbLinq (пока), что эквивалентно LinqToSql, так как я предполагал, когда я изначально задал этот вопрос. Используйте его с осторожностью!

Я использую шаблон хранилища с DbLinq. Мои объекты хранилища реализуют IDisposable, а метод Dispose() выполняет только одну вещь - вызывает Dispose() на DataContext. Всякий раз, когда я использую хранилище, я обернуть его в using блоке, как это:

public IEnumerable<Person> SelectPersons() 
{ 
    using (var repository = _repositorySource.GetPersonRepository()) 
    { 
     return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person> 
    } 
} 

Этот метод возвращает IEnumerable<Person>, так что, если я правильно понимаю, нет запросов к базе данных на самом деле не происходит до тех пор, Enumerable<Person> не проходится (например, путем преобразования его в список или массив или используя его в foreach петле), как в этом примере:

var persons = gateway.SelectPersons(); 
// Dispose() is fired here 
var personViewModels = (
    from b in persons 
    select new PersonViewModel 
    { 
     Id = b.Id, 
     Name = b.Name, 
     Age = b.Age, 
     OrdersCount = b.Order.Count() 
    }).ToList(); // executes queries 

в этом примере Dispose() вызывается сразу же после установки persons, который является IEnumerable<Person>, и это единственный раз, когда он вызван.

Итак, три вопроса:

  1. Как это работает? Как можно разместить DataContext запрос к базе данных для результатов после удаления DataContext?
  2. Что делает Dispose() Фактически?
  3. Я слышал, что не нужно (например, см. this question) распоряжаться DataContext, но у меня сложилось впечатление, что это неплохая идея. Есть ли причина не, чтобы избавиться от DbLinq DataContext?
+0

Что делает метод 'repository.GetAll()'? Что он возвращает? –

+0

@ Eclipsed4utoo, хороший вопрос. Я прокомментировал код. – devuxer

ответ

3

1 Как это работает? Как удаленный DataContext по-прежнему запрашивает базу данных для результатов после того, как DataContext был удален?

не работа. Ты что-то не показываешь нам. Я предполагаю, что либо ваш класс репозитория не удаляет DataContext должным образом/в нужное время, либо что вы в полном объеме записываете ToList() в конце каждого запроса, что полностью отрицает преобразование запроса и отложенное выполнение, которое вы обычно получаете.

Попробуйте следующий код в тестовом приложении, я гарантировать, что он будет бросать ObjectDisposedException:

// Bad code; do not use, will throw exception. 
IEnumerable<Person> people; 
using (var context = new TestDataContext()) 
{ 
    people = context.Person; 
} 
foreach (Person p in people) 
{ 
    Console.WriteLine(p.ID); 
} 

Это простейший случай воспроизводимым, и он всегда будет бросать. С другой стороны, если вместо этого вы напишите people = context.Person.ToList(), то результаты запроса уже были перечислены внутри блок using, на который я ставлю, это то, что происходит в вашем случае.

2 Что делает Dispose() на самом деле?

Среди прочего, он устанавливает флаг, указывающий, что DataContext расположена, который проверяется на каждом последующем запросе и вызывает DataContext бросить ObjectDisposedException с сообщением Object name: 'DataContext accessed after Dispose.'.

Он также закрывает соединение, если DataContext открыл его и оставил его открытым.

3 Я слышал, что нет необходимости (например, видеть этот вопрос), чтобы избавиться от DataContext, но у меня сложилось впечатление, что это не плохая идея. Есть ли причина не избавляться от DataContext LinqToSql?

Это является необходимо DisposeDataContext, как это необходимо Dispose каждый IDisposable. Вы можете потенциально протечь соединения, если не удалите DataContext. Вы также можете утечьте память, если какой-либо из объектов, полученных из DataContext, будет сохранен в живых, поскольку контекст поддерживает внутренний кеш-идентификатор для реализуемого им шаблона единицы работы. Но даже если это не так, это не ваше беспокойство, что делает метод Dispose. Предположим, что он делает что-то важное.

IDisposableдоговор, который гласит: «очистка не может быть автоматической, вам нужно избавиться от меня, когда вы закончите». У вас нет гарантий того, есть ли у объекта собственный финализатор, который очистится после вас, если вы забудете Dispose. Реализации могут быть изменены, поэтому не стоит полагаться на наблюдаемое поведение, а не на явные спецификации.

Худшее, что может произойти, если вы распоряжаетесь IDisposable с помощью пустого метода Dispose, заключается в том, что вы тратите несколько циклов процессора. Самое худшее, что может произойти, если вы не смогли удалить IDisposable с помощью нетривиальной реализации . - это утечка ресурсов. Выбор здесь очевиден; если вы видите IDisposable, не забудьте его утилизировать.

+1

Ну, я очень рад, что задал этот вопрос. И вы также навалили меня на немного упрощение моего вопроса. Я технически не использую LinqToSql, я использую DbLinq (который должен быть «точно так же, как LinqToSql») и база данных SQLite, и когда я запускаю ваш код, он не вызывает никаких ошибок. Итак, теперь я считаю, что Dispose() просто не реализован в DbLinq (или не реализован должным образом). В любом случае мне кажется, что мне нужно выполнить все операции, которые понадобятся для конкретного «DataContext» * в * блоке 'using'. – devuxer

+0

@ DanM: Интересно, действительно. Я думаю, что это 'LIKE' Linq для SQL в буквальном смысле этого слова SQL. :-) Тем не менее, точка № 3, вероятно, самая важная и по-прежнему действительна; вызов 'Dispose' никогда не повредит вам (ну, кроме WCF), но * не * часто его вызывает * будет *. Учитывая, что кодовая база DbLinq по-прежнему находится в версии 0.2 (т. Е. Не «стабильная»), я не удивлюсь, если будущие изменения в реализации нарушают здесь неправильный, но рабочий прецедент. – Aaronaught

+0

Ха-ха, второй самый важный момент в том, что я кое-что узнал о правильном способе использования объекта DataContext. Спасибо за вашу помощь. – devuxer

0

«персоны» - это коллекция IEnumerable, DataContext (репозиторий) требуется только для вызова .GetNew.

ключевые слова из/select/etc являются синтаксическим сахаром для методов расширения, добавленных в пространство имен System.Linq. Эти методы расширения добавляют функциональность IEnumerable, которую вы используете в своем запросе, а не DataContext. Фактически, вы можете сделать все это без использования LINQ2SQL вообще, путем программного создания IEnumerable для демонстрации.

Если вы пытаетесь сделать любые последующие вызовы репозитория (DataContext) с использованием этих объектов, то вы получите сообщение об ошибке.

Коллекция IEnumerable будет содержать ВСЕ записи из вашего репозитория, поэтому для запроса запроса DataContext не требуется.

Метода расширения: http://msdn.microsoft.com/en-us/library/bb383977.aspx

метода расширение LINQ: http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx

+0

Я понимаю о методах расширения, и я все время использую LINQ (не только LinqToSql), но, я думаю, я не понимаю, что именно делает DataContext. Тот факт, что я передаю ему объект «Connection», заставляет меня думать, что он должен отвечать за запрос базы данных. Тем не менее, даже если я распоряжаюсь им, запросы, по-видимому, кажутся выполненными. Как это возможно? – devuxer

+0

Контекст данных создан на заводе Visual Studio для создания IEnumerables из вашей базы данных. Было бы намного проще использовать DataContext в вашем запросе, вместо использования GetAll в отдельном методе. – Sprague

+0

Запрос, который вы делаете, не является запросом к базе данных, это запрос против коллекции объектов, которые вы получили из базы данных и ранее хранящихся в памяти (в вашем примере). – Sprague

0

Глубоко внутри API, вы, вероятно, увидеть метод с использованием API, как этот:

http://msdn.microsoft.com/en-us/library/y6wy5a0f(v=VS.100).aspx

Когда команда выполняется, связанный объект Connection закрывается, когда связанный объект DataReader закрыт.