Dlaczego nie warto korzystać z ViewBag’a w ASP.NET MVC
Jeśli zdarzyło Ci się programować aplikację webową z wykorzystaniem biblioteki ASP.NET MVC to na pewno spotkałeś się również z takim tworem jak ViewBag. Jest to element korzystający z mechanizmu dynamicznych właściwości, które zostały wprowadzone w wersji 4.0 języka C#. ViewBag jest elementem, który poznaje się zwykle na początku przygody z ASP.NET MVC, przynajmniej tak było we wszystkich książkach i kursach dotyczących tej biblioteki jakie widziałem. Umożliwia on proste i wygodne przekazywanie wartości z kontrolera do widoku. Jednak ta prostota użycia jest tylko pozorna i każdy programista prędzej czy później dojdzie do wniosku, że początkowe lenistwo nie opłacało się. Dlatego im wcześniej pozna się wady ViewBag’a tym lepiej, ponieważ odpowiednio szybkie przerzucenie się z niego na chociażby View Modele ograniczy nam w przyszłości ilość pracy. W końcu łatwiej jest coś zmienić mając 10 akcji w kontrolerach i tyle samo widoków niż kiedy będzie ich 100 lub więcej ;)
Pewnie teraz niektórzy młodzi stażem programiści zaczynający przygodę z ASP.NET MVC zapytają, co takiego złego jest w mechanizmie ViewBag? Swoją odpowiedź przedstawię w kilku punktach z uzasadnieniem każdego z nich.
- Brak kontroli typu – do ViewBag możesz wrzucić wartość dowolnego typu, co pewnie uznasz za zaletę. Ale kiedy potem odczytujesz tą wartość np. w widoku nie masz wsparcia ze strony mechanizmu sprawdzania typów. Powoduje to, że zalety statycznego typowania przestają istnieć. Ponieważ pola w ViewBag tworzone są dynamicznie podczas wykonywania się kodu kompilator może jedynie założyć, że wiesz co robisz, nie ma możliwości sprawdzenia tego. Jak wiadomo nigdy nie jest się bezbłędnym więc stosując ViewBag będziesz prawdopodobnie dosyć często widział ekran błędu podczas uruchamiania aplikacji spowodowany przekazywanie np. do pomocniczej metody wartości złego typu.
- Brak kontroli nad poprawnością nazw – czyli coś co będzie jeszcze częstszą przyczyną błędów pojawiających się w trakcie działania niż brak kontroli nad typami danych. Zastanów się ile razy zdarzyło Ci się podczas pisania kodu popełnić literówkę, o której istnieniu szybko informował Cię edytor albo w ostateczności kompilator? No właśnie, prawdopodobnie często, co jest normalne podczas dłuższego i często szybkiego pisania na klawiaturze. W przypadku korzystania z ViewBag’a nie dostajemy podczas edycji lub kompilacji żadnej informacji o tym, czy nazwa użyta w kontrolerze i ta w widoku są takie same. A, że w C# nawet wielkość liter ma znaczenie to o błąd nie trudno, a kiedy się pojawi to zobaczymy „ładny” ekran mówiący, że dana zmienna nie istnieje.
- Brak wsparcia ze strony mechanizmu podpowiadania składni – jeśli korzystasz z Visual Studio, albo innego IDE to na pewno znasz taką cudowną rzecz jak podpowiadanie składni, wystarczy zacząć wpisywać jakąś nazwę, a edytor wyświetla nam listę wszystkich pasujących zmiennych, funkcji i klas. Ale nie dotyczy to niestety ViewBag’a. Ten punkt łączy się niejako z poprzednim. Przez to, że pola w obiekcie ViewBag tworzone są dynamicznie edytor w trakcie pisania kodu nie wie, że one się tam znajdują przez co nie może żadnego z nich podpowiedzieć. Prowadzi to często do błędów, o których wspomniałem w punkcie 2. W dodatku wymusza konieczności ciągłego przełączania się między kodem kontrolera i kodem widoku lub ewentualnie wyświetlanie ich obok siebie, szukanie w jednym pliku odpowiedniego fragmentu gdzie tworzymy pole w ViewBag i porównywanie go z fragmentem gdzie używamy tego pola w drugim pliku. Próby pamiętania jakie zmienne dodało się do „worka” bardzo szybko przestają się sprawdzać, w końcu kto by spamiętał wszystkie utworzone w projekcie zmienne.
- Szczątkowe możliwości debugowania – mam nadzieję, że jesteś osobą umiejącą korzystać z debuggera i doceniającą jego możliwości podczas poszukiwania błędów. Jednak dla tych co nie wiedzą powiem, że debugger jest narzędziem, często wbudowanym w IDE, pozwalającym „podglądać” wartości zmiennych podczas działania programu i umożliwiającym śledzenie wykonywania się kolejnych linii kodu. W takim razie czy można podejrzeć jakie wartości przechowuje ViewBag podczas działania aplikacji? Odpowiedź brzmi tak i nie, bo mamy możliwość wyświetlenia jakie nazwy zmiennych zawiera ViewBag (jest to możliwe bo debugger działa już po uruchomieniu programu, a jak pisałem pola w ViewBagu tworzone są w trakcie działania) i jeśli są one typu prostego (np. int, double, string) to możemy zobaczyć także jakie wartości one przechowują. Problem powstaje kiedy do ViewBag’a wrzucimy np. jakąś kolekcję. Wtedy jedyne co możemy zobaczyć to jakiego typu ona jest, nie ma nawet możliwości sprawdzenia ile elementów zawiera. Tak samo jest z innymi złożonymi obiektami wstawionymi do ViewBag’a, przez co debuggowanie mimo, że możliwe to jednak bardzo ograniczone i pomocne tylko w nielicznych przypadkach.
- Utrudniony refaktoring – w jednym z poprzednich wpisów opisałem ogólnie zagadnienie refaktoringu. Jeśli zagłębiłeś się bardziej w IDE, którego używasz to na pewno znalazłeś funkcje pozwalające m.in. zmieniać nazwę danej zmiennej tak, że zmiana ta zostanie uwzględniona w całym projekcie. Ale nie dotyczy to ViewBag’a. Narzędzia automatyzujące proces refaktoringu, nie tylko te wbudowane w edytor, ale też dostępne w postaci pluginów (np. Resharper) nie radzą sobie z tym elementem przez co w przypadku zmiany nazwy zmiennej trzymanej w ViewBagu musisz sam zadbać o to żeby zedytować wszystkie miejsca gdzie została użyta.
Jak widzisz argumentów przemawiających za nieużywaniem ViewBag’a jest całkiem sporo, a te, które tu podałem nie są zapewne wszystkimi i myślę, że z biegiem czasu sam znajdziesz kolejne. Ale czy w takim razie powinno i da się całkowicie zrezygnować z tego mechanizmu? Moim zdaniem nie. W jakimś celu został on dodany i w prostych przypadkach ułatwia on pracę nie powodując zbyt dużego zamieszania. Przykładowo kiedy potrzebuję przesłać do widoku informację czy dany użytkownik ma np. prawa administratora w celu wyświetlenia dodatkowych pozycji w menu to wystarczy, że wrzucę do ViewBag zmienną IsAdmin, która przyjmie wartość true lub false. Jednak jeśli zaczynasz w ten sposób przerzucać większe zmienne albo całe kolekcje to dobra pora aby zacząć korzystać np. z View Modeli, które poza głównym obiektem, dla którego np. tworzymy formularz będę też przechowywać inne dane potrzebne chociażby do stworzenia rozwijalnej listy wyboru. Da Ci to kontrolę nad poprawnością nazw i typów i gwarantuję, że szybko zrozumiesz, że warto napisać parę linijek kodu więcej żeby później nie musieć naprawiać dużej liczby, często wręcz głupich, błędów.
ViewBag sam w sobie byłby być może dużo lepszym wyjściem gdyby środowisko lepiej radziło sobie z jego obsługą, bo w tym momencie nawet JavaScript jest lepiej obsługiwany jeśli chodzi o sprawdzanie poprawności nazw, a jak niektórzy wiedzą nawet w jego przypadku większy kawałek kodu staje się problematyczny. Zobaczymy co pod tym kątem przyniosą kolejne wersje Visual Studio jednak na razie polecam ograniczone wykorzystanie ViewBag’a jak i ogólnie mechanizmu dynamicznych pól w C#.
Pingback: ViewModele w ASP.NET MVC
„Przykładowo kiedy potrzebuję przesłać do widoku informację czy dany użytkownik ma np. prawa administratora w celu wyświetlenia dodatkowych pozycji w menu to wystarczy, że wrzucę do ViewBag zmienną IsAdmin, która przyjmie wartość true lub false.”
Jeśli admin potrzebuje innego menu niż użytkownik, to kontroler menu powinien zwrócić odpowiednie menu na podstawie Identity zalogowanego użytkownika. Robienie tego przez ViewBag jest bardzo śliskie i niefajne.
Nie tyle potrzebował całkiem innego menu co tylko dodatkowej pozycji w nim, jednak masz rację, robienie tego typu operacji przez ViewBag jest bardzo krzywe. No ale człowiek uczy się przez całe życie i głupi umiera, na razie czasami brakuje mi lepszych rozwiązań, dlatego tym bardziej mnie cieszy, że takie osoby jak Ty również tu zaglądają i dodają swoje uwagi.
„…a jak pisałem pola w ViewBagu tworzone są w trakcie działania) i jeśli są one typu prostego (np. int, double, string)…”
Błąd – string nie jest typem prostym, a referencyjnym.