Proč jsem se rozhodl napsat Redwood

Tomáš Herceg       5. 12. 2014       C#, ASP.NET WebForms, ASP.NET MVC, ASP.NET/IIS, HTTP/HTML, JavaScript       6887 zobrazení

Na letošním pražském MS Festu jsem měl v Altairově přednášce krátký výstup o mém vlastním ASP.NET frameworku, který jsme s Tomášem Jechou nazvali Redwood. Protože se mě celkem dost lidí ptalo, co tím sleduju a proč nepřejdeme na MVC jako každý slušný .NET vývojář, rozhodl jsem se to sepsat do článku.

WebForms ještě nejsou mrtvé, ale…

Microsoft v novém “vNext stacku” s WebForms nepočítá a chce, aby všichni přešli na MVC anebo na nějaký čistě klientský javascriptový framework a ASP.NET Web API. Neznamená to sice, že WebForms v dohledné době přestanou fungovat; myslím, že WebFormové aplikace budeme potkávat minimálně ještě 10 let, ale Microsoft do nich už nehodlá moc investovat.

V praxi to znamená, že to ještě pár let budou držet třetí strany, které prodávají svoje balíky komponent (např. Telerik, DevExpress apod.), nicméně platforma jako taková se rozvíjet nebude a zrovna WebForms mají uvnitř řadu omezení, která zmenožňují udělat v nich všechno. Navíc WebFormy obsahují spoustu old-fashioned věcí z dob .NET 1.0. I ty slavné nové silně typové bindingy jsou tam spíš doplácané a mají spoustu omezení, mnoho komponent ani nepoužívá generiku, přestože by se to hodilo (např. kolekce Controls u libovolné komponenty není silně typová atd.). Při dnešních aplikacích to dost komplikuje práci a vývoj přestává být pohodlný a rychlý.

Proč nechci MVC

Na toto téma jsem napsal už několik článků a na mém pohledu se v zásadě nic nemění – jako zásadní problém v něm vidím, že kvůli absenci viewstate neumí a nikdy nebude umět snadno udělat plnohodnotné vlastní komponenty, jako to uměly WebForms – prostě komponenty, které by si nějak někde držely vlastní stav, mohly být na jedné stránce umístěny třeba 5x a obsahovaly by vlastní netriviální logiku, která s tím stavem nějak pracuje. Ne, že by se to v MVC nedalo napsat, ale je to dost komplikované.

V aplikacích, které dnes a denně píšu, je stavovost prostě potřeba a framework, který si persistenci stavu řeší sám, má velmi významné plus – šetří to totiž obrovské množství kódu. Kromě toho se mi v MVC nelíbí views, protože HTML helpery se už používají pro úplně každou kravinu a celý ten kód je míchání netriviálních bloků C# kódu a HTML – prostě klasický spaghetti kód. Pokud bych chtěl nějaké UI vygenerovat dynamicky, tak si to taky musím psát sám.

Jak to celé vlastně vzniklo

Před 2 lety jsem si začal hrát s klientskými MVVM frameworky, zejména s Angularem a Knockoutem. Zejména Knockout se mi zalíbil, je krásně minimalistický, řeší sice jen jeden problém, ale řeší ho velmi dobře. V kombinaci s dalšími javascriptovými knihovnami (Kendo UI pro pořádné webové komponenty, Durandal pro implementaci SPA) a ASP.NET Web API jsme v tom napsali pár aplikací a celkový dojem je dobrý, i zákazníkům se to líbilo – aplikace vypadá rychleji, nemusí dělat plné postbacky, při prvním načtení se to stahuje trochu déle, protože JS knihoven je skoro 1MB (z čehož asi 900kB je Kendo UI), ale pak si aplikace se serverem vyměňuje už jen pár JSONů, které mají jednotky kB, což je krásné a rychlé a velmi dobře se to hodí pro informační systémy a podobné aplikace.

Každopádně je zde několik problémů:

  • Celé to řešení je konglomerát asi 20 JS knihoven a .NET backendu, naučit se a zvládnout to všechno není vůbec jednoduché.
  • 99% javascriptového kódu nedělá nic zásadního, jen nějak reprezentuje nebo transformuje data ze serveru.
  • Pokud bych takto chtěl napsat třeba eshop, narazím, protože to vyhledávače nezaindexují. Musím tedy míchat dohromady serverové a klientské renderování.

A když jsem na loňském MVP Summitu viděl, že to s WebForms do budoucna nevypadá dobře, napadlo mě, že bych těch 99% otravného javascriptu mohl nějak vygenerovat, protože to bylo pořád na jedno brdo a každý viewmodel jsme dělali podle jednoho zaběhnutého schématu.

Pak jsem zkusil něco napsat, párkrát jsme si na to sedli s Tomem Jechou, napsali jsme kus kódu, pak jsme zjistili, že neumím pořádně vysvětlit, jak konkrétně si to představuju a že bude nejlepší, když napíšu nějaký prototyp a uvidíme, jestli to k něčemu je nebo ne. Do toho do mě začal hudrat Altair, že jestli to nebude umět klasické plnohodnotné postbacky, tak to bude na hovno, že na mizerném připojení je klasický postback nejlepší, načež jsem se naštval a během posledního MVP Summitu jsem prototyp během 3 dnů napsal a plácnul na http://github.com/riganti/redwood. Plné postbacky to neumí, zato to umí část toho, co jsem chtěl.

Jak to funguje a co to umí

Základem je viewmodel, který je napsán v C#. Je to třída, která má vlastnosti a funkce. Viewmodel pro jednoduchou kalkulačku by vypadal asi takhle:

public class CalculatorViewModel 
{
    public int Number1 { get; set; }

    public int Number2 { get; set; }

    public int Result { get; set; }

    public void Evaluate() 
    {
        Result = Number1 + Number2;
    }
}

ViewModel drží kompletní stav stránky a pokud neobsahuje žádné kočičárny, tak lze snadno serializovat. Ve view pak Redwood podporuje vlastní komponenty, které budou moct mít svůj ekvivalent ControlState z WebForms a budou si ho samy přidávat do viewmodelu (nebude v něm potřeba nic deklarovat). A samozřejmě je možné dělat binding do ViewModelu, takže to vypadá třeba takhle:

<rw:TextBox Text="{value: Number1}" />

No a když přijde HTTP request na tuhle stránku, tak Redwood vytvoří strom komponent, vytvoří viewmodel, zavolá postupně funkce Init, Load a PreRender, jako to bylo ve WebForms, a nakonec vyrenderuje výstup. Bindingy přitom převede na Knockoutové data-bind. Při renderování výstupu přidá do stránky odkaz na knihovnu Redwood.js, která obsahuje pár krátkých infrastrukturních funkcí a ještě se do stránky přidá serializovaný viewmodel (to je vlastně viewstate z WebForms).

V okamžiku, kdy člověk klikne na tlačítko, tak se viewmodel vezme, serializuje a pošle AJAXem na klienta (postback), kde framework nějak pozná, kterou funkci má zavolat a s jakými parametry (commandy ve viewmodelu mohou mít libovolné množství parametrů). Na serveru se to zpracuje a nový viewmodel se pošle na klienta.

<rw:Button Click="{command: Evaluate()}" />

A abych vyřešil problém s klientským a serverovým renderováním, tak pokud použiju třeba Repeater, tak mu jde říct, jestli tu ItemTemplate má renderovat na serveru, nebo až na klientovi. Akorát zatím Redwood neumí ekvivalent UpdatePanelu, takže co se při prvním requestu vygeneruje, to už tam zůstane navěky. Asi tam ani UpdatePanel dělat nechci, spíš jen nějakou attached property Render.UpdateOnClient=”true”, která se přidá kamkoliv a nějak se o to postará.

Abych pravdu řekl, líbí se mi to

Řeší to různé neduhy WebForms:

  • Nevím, co mám ve viewstate. V Redwoodu vím, protože viewstate je můj viewmodel, který mám pod kontrolou.
  • Viewmodely jsou na rozdíl od codebehindu testovatelné – nejsou tu závislosti na komponenty.
  • Je to modernější, v bindingu bude plně typová IntelliSense.

Řeší to i ten problém z MVC – jak nepřehledné views plné dlouhých fragmentů C# kódu, tak i ty stavové komponenty jakmile je tam dopíšu.

Má to jednotnou syntaxi pro klientské a serverové šablony a nemusíte psát javascript, pokud nechcete.

Co je v plánu

Aktuální stav je takový, že je to v praxi skoro nepoužitelné – chybí tam dost zásadní věci. Zde mám takový krásný seznam, co tam chci v nejbližší době doplnit.

  • Plně stavové komponenty a Master Pages – aktuálně nejsou naimplementované
  • Dokončit základní sadu komponent
    Chybí FileUpload, který se bude muset řešit nějak pitomě přes iframe, protože AJAXem soubory nejde posílat
    Dál to chce aspoň GridView a DataPager
    A pak bych rád nějaké wrappery nad bootstrapem, abychom se zbavili toho CSS class hell
  • Upravit serializaci viewmodelů – aby šlo říct, že tuhle property chci při postbacku vynechat, tuhle chci zašifrovat a tuhle nechci šifrovat, ale chci ji digitálně podepsat (aby ji klient viděl, ale nemohl ji sám změnit).
  • Integrace do Visual Studia – mám rozepsaný plugin, který řeší zvýrazňování syntaxe a IntelliSense
  • Validace – chci ji tam přidat nějak neintruzivně, využít na to asi attached properties, které podporujeme, a pochopitelně navázat na Data Annotations v .NETu
  • Lokalizace – ta jediná snad nebude tak těžká, přidáme speciální binding {localize: ResourceFile.ResourceKey}.
  • Dál bych chtěl přes Roslyn překládat jednoduché metody ve viewmodelu do javascriptu, aby se nemusel dělat postback i kvůli akcím, které lze na viewmodelu provést lokálně.

Jak vidíte, práce je na tom dost, ale potenciál vidím obrovský. Serverová část běží na OWINu, nemá závislosti na System.Web, takže bude kompatibilní s novým stackem. Neobsahuje nic nativního, takže teoreticky by přes to šly psát i multiplatformní mobilní a desktopové aplikace – UI bude v HTML a backend v C#. V kombinaci s Apache Cordova by to mohlo dostat další rozměr.

 

P.S. A děkuji panu Stanislavu Lukešovi za první (a užitečný) pull request.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Licencia

Vypada to velmi zaujimavo, pod akou licenciou planujete tento framework distribuovat ?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zatím Apache 2.0.

A do budoucna plánuji, že základní verze bude open source, a pak bude placená, která toho bude umět mnohem víc.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Uvidime...

Jsem zvedavy, jak se bude projekt vyvijet, zatim jsem k tomu spise skepticky, alespon co se tyka psani nejakych netrivialnich aplikaci s bohatym GUI (ne jen par formularovych prvku). Ale je to jen na zakladu par zakladnich informaci, co jsem zatim slysel/cetl, az budu mit chvilku, tak se na to jeste podivam trochu vic...

Mozna proto, ze jsem nikdy poradne nepouzival WebForms (a behem kratkeho pouzivani si vybudoval pomerne velky odpor)... A Redwood ve me vzbuzuje trochu podobny dojem, ze bych dostaval black-box predpripravene komponenty a naplni moji prace by bylo je stale ohybat a pak stejnak dojit do stadia, kdy bych komponentu zahodil a napsal si to cele znovu.

A pak bych mel jeste jednu drobnou pripominku:

"protože AJAXem soubory nejde posílat" - pres AJAX soubory jde odesilat uplne krasne. Jen to nepodporuje IE<10, coz me treba neprijde jako duvod tahat do defaultni controlky nejaky iframe kvuli 4% uzivatelu (nez se Redwood dostane do pouzitelne verze, tak jiste i mene), ale tak zalezi na koho bude cileno. Pokud bych ja jako uzivatel Redwoodu dostal FileUploader komponentu s iframe, tak prvni co bych udelal by bylo to, ze bych si napsal vlastni komponentu. A proste ve me pretrvava pocit, ze bych tohle udelal s alespon polovinou veci, protoze bych je chtel pouzivat proste jinak...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud WebForms nepoužíváte, tak pak moc nerozumím Vašemu příspěvku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak ocekaval jsem, ze Redwood ma ambice pretahnout i nejake MVC (+knockout) vyvojare, kdyz se snazi odstranit "spatne" veci a pridat jine "hezke".

Asi jsem to vztahoval moc vuci svym potrebam a zvyklostem, jak a na cem pracuju. Verim, ze nektere vyvojare to zaujme a na nektere projekty to treba i pouziji, ja asi zatim zadny takovy nemam...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

1. Základní komponenty v Redwoodu budou víceméně jednoduché wrappery nad formulářovými prvky z HTML, budou řešit ty samé věci jako základní HtmlHelpery z MVC. Nebude moc důvodů, proč do nich zasahovat, protože TextBox je jen wrapper nad <input type="text" /> a Button je jen wrapper nad <input type="button" />.

2. Redwood patrně bude obsahovat i složitější komponenty jako je DataGrid, ale nikdo vás nebude nutit ho používat - můžete si to napsat sám (a třeba nám i poslat pull request), což byste v MVC nebo ve kterékoliv jiné technologii musel udělat taky, pokud máte nějaké speciální požadavky.

Rád bych tam měl i něco, co bude pomocí Data Annotations generovat formuláře (a nějak podstatně chytřeji, než to dělala svého času Dynamic Data), a to je asi zrovna ta komponenta, kde si ji bude každý chtít naimplementovat sám, protože každý dělá formuláře úplně jinak a liší se to projekt od projektu. Ještě uvidím, jak s tím naložím.

3. Co se týče file uploadu, rozhodně nechci odstřihnout uživatele IE8 a IE9, já jich mezi svými zákazníky mám dost. Proto to bez iframu udělat nejde a někde na pozadí tam patrně bude (asi to napíšu tak, že se budou renderovat dvě verze - jedna pro staré prohlížeče, která tam vrazí iframe, a jedna pro nové prohlížeče, která iframe používat nebude - ještě nevím). Stejně jsme téměř v každé aplikaci nakonec standardní fileupload vyměnili za nějakou flashovou věc, která to uploadovala nějak inteligentně a uměla víc souborů najednou, což opět některé staré browsery nezvládají.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

1. Tak on i textbox dokaze mit spoustu nestandardnich funkci (ktere samozrejme ani vestavene HtmlHelpery nezvladaji)... Zalezi, jak bude mozne jednoduche komponenty upravovat/rozsirovat...

Mozna na to koukam moc z pohledu cloveka co pise dost v knockoutu (kde mam moznosti spoustu) a ne z pohledu Webforms nebo cisteho Razoru (kde nemam moznosti temer zadne), ale prijde mi, ze clovek pak ztrati uplne kontrolu nad interakci mezi prvky na formulari, kterou dokaze v knockoutu napsat uplne jednoduse (napr. jakekoli akce navazane na zmenu hodnot (subscribe) nebo netrivialni spoctene hodnoty na zaklade jinych hodnot (computed/pureComputed)). A nebo pokud to pujde bez psani JS kodu, tak bude potreba postback na server.

Ocekavam, ze i v Redwoodu pujde dopisovat nejaky custom knockout binding a kod, ale to pak podle me povede ke stejnemu spaghetti kodu - komponenty/formulare tvarici se jako vestavene serverove budou obcas doupravene pomoci uplne jineho kodu nekde na klientovi.

2. Asi zalezi na tom, jak moc bude aplikace customizovana, je pravda, ze nekde to usporu casu prinese.

3. Ok, chapu

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Docela by mě zajímal nějaký příklad - určitě bych rád v Redwoodu měl komponenty, které budou snadno rozšiřitelné, tak by bylo super, kdybyste mi mohl hodit nějaké usecases, které třeba v MVC musíte řešit vlastními HtmlHelpery, protože ty standardní to neumí.

Bindingy se už v aktuální verzi překládají do JS, takže jednoduché výrazy jako <span class='{value: Count > 1 ? "visible" : "hidden"}'> fungovat budou.

Počítám s tím, že bychom do javascriptu překládali i funkce z viewmodelu, které nebudou mít závislosti na DB atd. - pokud bude ve viewmodelu readonly property a půjde přeložit, uděláme to. Bohužel není to úplně triviální, takže to rozhodně nebude hned. Tam by se generovaly ty ko.computed.

Určitě plánuju i něco, co umožní snadno přidávat vlastní kusy kódu do vygenerovaného viewmodelu - hodilo by se mi, kdyby začal typescript podporovat partial classes. :-)

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback