Kategoria: linq

Cannot access a disposed object. Object name: ‚DataContext accessed after Dispose

Czasami podczas wykonywania kodu może się zdarzyć, że nasz kod się wysypie z tym wyjątkiem. Problem ten występuje kiedy próbujemy się odwołać do atrybutów klas LINQ2SQL po usunięciu z pamięci powiązanego z nimi DataContextu, czyli defakto oderwania obiektów od bazy danych. Podstawową przyczyną tego problemu jest niejawna optymalizacja linq2sql zwana lazy loading. Mechanizm ten zakłada że nie ładowane są wszystkie atrybuty i kolejcjie z bazy a jedynie to co jest potrzebne, na podstawie wyrażeń lambda oraz zapytań linq.
Po pewnym czasie to co jest potrzebne do dalszego przetwarzania zostaje załadowane z pamięci, a DataContext jest niszczony. I właśnie tu pojawia się wspomniany wyjątek . Jest on o tyle ‚zdradliwy’, że może pojawić się znienacka. Typowym scenariuszem, jak udało mi się zauważyć jest przypadek kiedy przekazujemy obiekty linq2sql do Widoku w aplikacji ASP.NET MVC. Bierzemy taki obiekt i odwołujemy kilka stopni głębokości w jego strukturę.Przykładowo mamy sobie obiekt klasy user który zawiera kolekcję wszystkich postów użytkownika, a każdy post zawiera z kolei kolekcję komentarzy. Typowa struktura np. dla Bloga. W tym momecie przy odwołaniu typu User.Posts[0].Coments dostajemy nie za wiele mówiący komentarz ‚Cannot access a disposed object Object name: ‚DataContext accessed after Dispose’. Jest to własnie spowodowane tym co wspomniałem wyżej – linq2sql nie załadował kolekcji komentarzy do pamięci bo zwyczajnie nie wiedział że takowa będzie potrzebna. Czy jest na to jakiś sposób? Oczywiście tzw. best practice mówi żeby nie korzystać z obiektów linq2sql w widokach, no ale co jeśli mamy deadline za pięć dwunasta a musimy poprawić bug w tej aplikacji której programista się rozpędził i nie zawracał sobie głowy jakimiś ViewModelami ? Wtedy jedyną możliwością jest jawne przekazanie do linq żeby ładował kolekcje komentarzy dla każdego Posta. Jak to zrobić ? Przyjrzyjmy się temu fragmentowi kodu:

var db = new DataContext();
var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(x=>x.Comments);
db.LoadOptions = dataLoadOptions;

Tworzymy obiekt klasy DataLoadOptions jest to klasa która definiuje jak się ma zachowywać DataConext podczas ładowania obiektów. Mówimy tu jawnie że jeśli musisz załadować Post to załaduj też jego kolekcję Comments. Taki prosty workaround pozwoli nam cieszyć się działającą aplikacji.

P.S Chcecie żebym w następnych postach dał kod przykładowych aplikacji ? Dajcie znać w komentarzach.