Wrzucanie stronki na serwer produkcyjny, czy inaczej deployment stronki, to rzecz, która znana jest na pewno każdemu Web Developerowi. Postaram się dziś powiedzieć (napisać) słów kilka o tym jak się to robi, gdy chcemy wrzucić stronkę MVC do jej miejsca docelowego. Postaram się także przedstawić rzeczy, na które warto zwrócić uwagę, gdy będziemy wrzucali zawartość aplikacji na serwer Webio.

A więc zaczynamy:

W naszym procesie wytwórczym aplikacji dochodzimy do momentu, gdy warto efekt naszej pracy umieścić na serwerze innym niż deweloperskim. Chcąc to zrobić z aplikacją ASP.NET MVC mamy do dyspozycji 2 możliwości: wrzucić wybrane pliki ręcznie przez FTP lub użyć opcji Publish w samym Visual Studio. Tutaj wyjątkowo polecam opcję Publish, gdyż jest to całkiem szybki i wydajny sposób na wrzucenie stronki (przy serwerach Webio będzie jednakże działała tylko opcja FTP). Należy pamiętać, że zostaną wgrane tylko niezbędne rzeczy do działania naszej aplikacji webowej tylko te, które należą do solucji. Jeśli więc mamy jakieś dodatkowe zasoby, które nie są w solucji, ale są wykorzystywane, to należy je wgrać osobno.

Jeśli będziemy chcieli samemu wgrywać pliki, to musimy wgrać zasadniczo 3 rzeczy:

  • Zawartość statyczną, czyli tzw. content, na który składają się pliki css, obrazki, skrypty JavaScript, itd
  • Kontrolki oraz stronki asp, czyli generalnie wszystkie pliki z rozszerzenie aspx lub ascx, master, itd Musi być przy tym zachowana struktura katalogów
  • Kod wykonywalny w postaci plików dll w katalogu bin naszej stronki.

Nie brzmi to jakoś strasznie, nieprawdaż? :> Przy projektach php mniej było kombinowania – tam w większości przypadków wystarczyło skopiować cały katalog projektowy na ftp, poprawić konfigurację – i już.

Kolejna rzecz, jaką należy zrobić to stworzyć na bazie produkcyjnej strukturę danych, która byłaby zgodna ze schematem na naszej stacji deweloperskiej. Jak to wykonać mając dostęp Sql Server Management Studio do obydwu serwerów? Można łopatologicznie: kliknąć na naszej bazie PPM, wybrać Tasks i Generate Scripts. Bardzo, ale to bardzo bardzo bardzo ważne jest to, że jak wykonamy standardowy zrzut całej bazy, to wykona się wszystko poza zrzutem danych! Czyli wygeneruje się nam tylko schema bazy danych. To na ogół >60KB trochę zagmatwanego kodu SQL, więc trzeba byłoby chwilę poświęcić by upewnić się, że jest lub nie ma tam wpisów uzupełniających dane (chyba, że wiedzielibyśmy, że dane w naszej bazie deweloperskiej ważą kilka mega, to wtedy wielkość 60KB mogłaby wzbudzić nasze podejrzenia ;)). Jest to nawiasem mówiąc zachowanie inne niż w większości baz i managerów dostępnych na rynku. Mysqldump robi zrzut wszystkiego standardowo, MySQL Workbench także, phpmyadmin także (tutaj już nie mam 100% pewności, ale nigdy na to nie narzekałem, więc wydaje mi się, że mam rację:)). Microsoft robi jednak narzędzia wyjątkowe, więc będąc w oknie kreatora Manager Studio, należy kliknąć przycisk Advanced w zakładce Set Scripting Options i poszukać na liście elementu Types of data to script i wybrać odpowiednią dla nas opcję.

Dlaczego o tym mówię w kontekście poprawnego deployu web aplikacji? Dlatego, że jeśli używamy Membership Providera i zdarzyłoby się nam wrzucić do bazy produkcyjnej wszystko poza danymi to napotkamy wielce niepomocny błąd:

*The 'System.Web.Security.SqlMembershipProvider' requires a database schema compatible with schema version '1'.  However, the current database schema is not compatible with this version.  You may need to either install a compatible schema with aspnet_regsql.exe (available in the framework installation directory), or upgrade the provider to a newer version.*

Błąd ten w dość niebezpośredni sposób informuje nas o tym, że nie ma niektórych danych w bazie, które standardowo są generowane skryptem do Membership Providera (o którym pisałem zresztą wcześniej tutaj). Należy więc się upewniać, że baza danych zawiera dane po pełnej migracji :)

Może jest to i trywialna sprawa, ale wgrywając stronę na serwer Webio problem ten zdiagnozowałem dopiero po dłuższej chwili (3h dokładniej). Powód jest cokolwiek trywialny – wprowadzono nowy cudowny system obsługi błędów ELMAH, który najpierw należy włączyć, bo bez niego wysypywany jest tylko Internal Server Error 500 bez informacji o tym co się stało. Po włączeniu ELMAHa według zaleceń Webio nie poprawia się dużo więcej, bo zamiast tego otrzymujemy wspaniały, bezsensowny błąd:

System.Web.HttpException: Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper'. ---> System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.NullReferenceException: Object reference not set to an instance of an object. at ASP.views_shared_topmenu_ascx.__RendertopMenu(HtmlTextWriter __w, Control parameterContainer) at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) *

…………
itd.  

Wynika to z tego, że łapany jest nie bezpośrednio pierwszy wyjątek, tylko ostatni, a ostatni jest tylko wynikiem niemożności skonstruowania widoku do wyświetlenia (przez pierwszy, nas interesujący błąd). Tego błędu nie znajdziemy w tym logu i to jest zasadniczo największy problem z całym logowaniem przez ELMAH. Mam nadzieję, że da się to obejść, ale znowuż trzeba nad tym chwilę posiedzieć i pobuszować w sieci; jako workaround proponuję stare, dobre i sprawdzone rozwiązanie z php: logowanie przez output, czyli gdzieś w kontrolerze wstawiamy blok kodu, który potencjalnie może się wywalać, obejmujemy go blokiem try-catch i wypluwamy do widoku potencjalny wyjątek, na który napotkamy. Trochę średniowieczna metoda – w php jest prosta i funkcjonalna, w asp znacznie utrudniona, a i tak czasem okazuje się niezbędna ;]

Referencje

Determining What Files Need to Be Deployed - ASP.NET

Konfiguracja ELMAH na serwerze Webio