Artykuł

freeimages.com freeimages.com
sie 10 2014
0

Myl się rzadziej z szablonami T4 w ASP.NET MVC

Popełnianie błędów jest rzeczą ludzką i nie znam osoby, której nigdy by się to nie zdarzyło - szczególnie w sferze zawodowej. Obojętnie czy pracujemy fizycznie, piszemy maile, umowy, czy programy komputerowe - wystarczy chwila nieuwagi, rozproszenia, czy też zmęczenia (niepotrzebne skreślić) by popełnić błąd. My jako programiści mamy jednak trochę lepiej niż inne grupy zawodowe, ponieważ w nasze ręce wpada sporo narzędzi dodatkowych, które mogą ułatwić naszą pracę i w wymierny sposób zmniejszyć ryzyko popełnienia pomyłki.

Nie bez powodu piszę o programowaniu oraz błędach. Każdy programista wie, jak łatwo jest takowe popełnić w naszym fachu. Dziś jednak chciałbym Wam przedstawić konkretny problem i zarazem zademonstrować jego rozwiązanie. Temat dotyczy platformy ASP.NET MVC.

Problem

Na co dzień pisząc kod MVC, wykorzystuje język C#, który jak wiadomo jest silnie typowany i posiada sporo użytecznych rozwiązań. Kod tworzę w Visual Studio, który jest w stanie mi powiedzieć, gdy robię coś nie tak. Tworząc jednak strony przy użyciu platformy ASP.NET MVC, możemy przygotować rozwiązanie które się kompiluje, ale w praktyce nie ma szans by poprawnie zadziałało. Jak tego dokonać? Wystarczy zrobić choćby literówkę w nazwie widoku, czy też akcji i jest to prostsze niż się Wam wydaje;-)

Nazwy widoków, kontrolerów, czy akcji, podajemy w postaci stringów w wielu różnych miejscach aplikacji ASP.NET MVC. Do najważniejszych należą:

  • Reguły routingu
  • Wszelkiej maści akcji związane z tworzeniem URLi w aplikacji np. Html.ActionLink
  • Nazwy widoków zwracane jako parametr konstruktora klasy View w kontrolerze

Jak widać miejsc w których możemy popełnić błąd jest naprawdę sporo. Niestety żaden z tych błędów nie zostanie wykryty przy kompilacji...

Rozwiązanie

Na pierwszy rzut oka, można by pomyśleć, że przecież można kopiować nazwy kontrolerów, akcji oraz widoków. W praktyce to rozwiązanie jednak się nie sprawdza, bo wciąż choćby przez omsknięcie palca, możemy dodać jakikolwiek przypadkowy znak i go nie zauważyć. Jest jednak lepsze rozwiązanie tego problemu, a jest nim szablon (a raczej szablony) T4 (T4MVC).

Szablony T4 to cudowne rozwiązanie, na które natknąłem się dopiero niedawno. Jest to specjalna biblioteka, która dodaje m.in. wiele silnie typowanych przeciążeń do popularnych helperów oraz dostarcza narzędzie, które generuje pliki dodatkowe wykorzystywane przez całą bibliotekę.

T4MVC można pobrać za pomocą NuGeta.

Po zainstalowaniu paczki, w projekcie pojawi się nowy element o nazwie T4MVC.tt wewnątrz którego będą pojawiały się wcześniej wspomniane pliki specjalne wykorzystywane przez bibliotekę (każdy kontroler posiada swój osobny - dedykowany mu plik). Za pomocą dodatkowych paczek (albo skryptów) możemy włączyć automatyczną aktywację tego narzędzia przy każdej kompilacji projektu. W przeciwnym przypadku po każdej zmianie związanej z utworzeniem nowego kontrolera/akcji/widoku (bądź też ich modyfikacją) będziemy musieli wyzwolić to narzędzie ręcznie (klik PPM na pliku T4MVC.tt oraz wybór opcji Run Custom Tool). Oczywiście czynność tą należy tylko wykonać, w momencie gdy chcemy skorzystać w kodzie z tej funkcjonalności;-)

Praca z T4MVC

Biblioteki T4MVC używa się równie prosto jak się ją instaluje. Nie mniej jednak aby móc ją zainstalować, trzeba najpierw stworzyć jakiś projekt. Aby cały ten proces uprościć, wystarczy pobrać testową solucję napisaną na potrzeby tego tekstu. Znajdziecie ją w dziale download.

W projekcie tym zbudowałem bardzo prostą aplikację, która zasadniczo nie robi nic pożytecznego, ale za to bardzo ładnie ukazuje najważniejsze zalety T4MVC wykorzystując trzy przedstawione przeze mnie wcześniej przypadki użycia. Aby to wszystko miało ręce i nogi, musiałem dodać kilka kontrolerów oraz akcji. Każdej z akcji został przypisany prosty widok. Struktura drzewiasta tego projektu wygląda następująco (kontroler/akcja/widok):

  • Home
    • Index
      • Index
    • Test
      • Test
  • Product
    • List
      • List
    • Details
      • Details

W projekcie znajduje się już biblioteka T4MVC (o jej instalacji pisałem w poprzednim punkcie) oraz zostały wygenerowane wszystkie potrzebne pliki. W kolejnych akapitach opiszę wszystkie problematyczne miejsca, o których wspominałem wcześniej w tym tekście.

Reguły routingu

Tworząc reguły routingu, często podajemy nazwę konkretnego kontrolera, czy akcji po prostu podając ją w stringu. Podawanie tego rodzaju danych w postaci łańcucha tekstowego zawsze rodzi ryzyko błędu - wystarczy choćby zmienić nazwę metodę w kontrolerze i zapomnieć zaktualizować rzeczonego stringa w regułach routingu. Normalnie Visual Studio nam przecież o tym nie podpowie... Na szczęście łatwo można to naprawić.

Przyjmijmy dla testów, że chcemy się pozbyć stringów z domyślnej reguły routingu:

routes.MapRoute(
	name: "Default",
	url: "{controller}/{action}/{id}",
	defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Z T4MVC jest to bardzo proste. Wystarczy tylko wykorzystać możliwości dostarczone przez dynamicznie tworzoną klasę MVC:

routes.MapRoute(
	name: "Default",
	url: "{controller}/{action}/{id}",
	defaults: new { controller = MVC.Home.Name, 
		action = MVC.Home.ActionNames.Index, 
		id = UrlParameter.Optional 
	}
);

Dynamicznie tworzona klasa zawiera wszystkie nasze kontrolery, metody oraz widoki i co naturalne, jest wspierana przez IntelliSense w Visual Studio. Do reguł routingu możemy dodać również nowe reguły. Załóżmy że chcemy mieć szybki link w postaci /Produkty, który będzie prowadził do kontrolera Product oraz akcji List. Normalnie zrobilibyśmy to mniej więcej tak:

routes.MapRoute(
	name: "ProductsList",
	url: "Produkty",
	defaults: new
	{
		controller = "Product",
		action = "List"
	} 
);

Natomiast z T4MVC możemy to zrobić jeszcze szybciej i bezpieczniej:

routes.MapRoute(
	name: "ProductsList",
	url: "Produkty",
	result: MVC.Product.Actions.List()
);

Również w tym przypadku pojawiła się pewna nowość, ponieważ do właściwości result, przypisujemy obiekt typu ActionResult (typ domyślnie zwracany przez metody kontrolera w MVC). Nie musimy więc tutaj definiować domyślnego kontrolera oraz akcji, zamiast tego zwracamy tutaj niejako jej wynik. Podobną konstrukcję wykorzystuje się w helperze Html.ActionLink.

Html.ActionLink

Html.ActionLink to bardzo przydatny helper, który umożliwia dynamiczne tworzenie linków w oparciu o zadane parametry i reguły routingu. Generalnie jest to bardzo użyteczne rozwiązanie, ale bardzo mocno cierpi w wyniku zastosowania stringów jako parametrów. Na szczęście T4MVC również rozwiązuje ten problem. Spójrzcie jak to robiło się dotychczas:

@Html.ActionLink("Link do akcji testowej", "Test") |
@Html.ActionLink("Lista produktów", "List", "Product") |
@Html.ActionLink("Szczegóły produktu", "Details", "Product", new { id = 5 }, null)

Powyższe linki przekierowują kolejno do:

  • Akcji Test w tym samym kontrolerze
  • Akcji List w kontrolerze Product - warto przypomnieć, że na bazie reguły routingu nie zostanie wygenerowany tutaj adres /Product/List tylko /Produkty
  • Akcji Details w kontrolerze Product z parametrem id równym 5

Cały ten kod możemy zastąpić bezpieczniejszą jego wersję opartą o T4MVC i znane już wcześniej konstrukcje z ActionResult:

@Html.ActionLink("Link do akcji testowej", MVC.Home.Test()) |
@Html.ActionLink("Lista produktów", MVC.Product.List()) |
@Html.ActionLink("Szczegóły produktu", MVC.Product.Details(5))

Jak nie trudno się domyślić, efekt działania obu tych akcji jest dokładnie taki sam.

Zwracanie widoków

Ostatnią rzeczą którą chciałbym Wam zaprezentować, jest opcja zwracania nazw widoków w konstruktorze klasy View. Metody kontrolera domyślnie zwracają widoki o nazwie takiej samej, jak nazwa akcji kontrolera. Można to jednak zmienić - wystarczy podać nazwę widoku jako parametr konstruktora:

public virtual ActionResult Index()
{
	return View("Index");
}

Oczywiście my nie chcemy podawać stringów, które nic nie mówią samemu kompilatorowi, dlatego też z pomocą T4MVC, zrobimy to w następujący sposób:

public virtual ActionResult Index()
{
	return View(MVC.Home.Views.Index);
}

Jeśli ta konstrukcja wydaje się Wam być zbyt długa, to w tym przypadku możecie się również pokusić o formę skróconą działającą w obrębie wybranego kontrolera:

public virtual ActionResult Index()
{
	return View(Views.Index);
}

Teraz już żaden błąd nie pozostanie nieodkryty;-)

Podsumowanie

T4MVC to bardzo fajna i przydatna biblioteka, która zmniejsza ryzyko popełnienia błędów i czyni cały kod bardziej obiektowym. W tekście przedstawiłem trzy kluczowe funkcjonalności, ale sama biblioteka oferuje znacznie więcej. Szczegółowe informacje znajdziecie na stronie projektu.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus