Trochę inna organizacja kodu w ASP.NET MVC

28

ASP MVC 3 jest w dużej części spoko – znajdą się elementy bardzo irytujące, ale ogólnie mogę powiedzieć że jestem z pracy z tym frameworkiem raczej zadowolony. Denerwuje mnie jednak to, że pracując nad jedną daną akcją w jakimś kontrolerze muszę śmigać po kilku plikach:

  • plik kontrolera
  • plik z routingiem
  • plik z modelem parametru akcji
  • plik z modelem zwracanym przez akcję
  • plik z mapowaniami AutoMappera
  • plik widoku .cshtml
  • plik skryptów .js
  • … o czymś zapomniałem?

Jakiś czas temu postanowiłem wypróbować alternatywne podejście do organizacji kodu w swoim projekcie webowym…

[Przyszło mi ono do głowy zimą podczas przeglądania materiałów dotyczących frameworka FubuMVC, ale właściwie momentalnie zarzuciłem zapoznawanie się z tym rozwiązaniem, więc nie jestem w stanie na dzień dzisiejszy powiedzieć, jak poniższe podejście i praktyki Fubu “ze sobą korespondują“;)… co z kolei teraz staram się nadrabiać, eksperymentując ponownie z Fubu]

Najlepiej zobrazuje to przykład, żeby było wiadomo o co mi CHO. Poniżej przedstawiam kawałek kodu odpowiedzialny za akcję “administrator dodaje notatkę do firmy”:

  1:  public partial class CompaniesManagementController
  2:  {
  3:      [HttpPost]
  4:      public virtual ActionResult SaveNote(SaveNoteModel model)
  5:      {
  6:          var company = _session.Load<Company>(model.CompanyId);
  7:  
  8:          Note newNote = model.Map<Note>();
  9:  
 10:          company.AddNote(newNote);
 11:  
 12:          return Json(new SaveNoteResponse()
 13:                          {
 14:                              SavedSuccessfully = true,
 15:                              Message = MessagesContent.CompanyNote_SaveSuccess
 16:                          });
 17:      }
 18:  
 19:      public class SaveNoteModel
 20:      {
 21:          public int CompanyId { get; set; }
 22:  
 23:          [Required]
 24:          [DisplayName("Notatka")]
 25:          public string Content { get; set; }
 26:      }
 27:  
 28:      public class SaveNoteResponse
 29:      {
 30:          public bool SavedSuccessfully { get; set; }
 31:          public string Message { get; set; }
 32:      }
 33:  
 34:      public class Routes : IRouteProvider
 35:      {
 36:          public void RegisterRoutes(RouteCollection routes)
 37:          {
 38:              routes.MapRoute(
 39:                  "Admin_SaveNote",
 40:                  "Admin/ZapiszNotatke",
 41:                  MVC.Administration.CompaniesManagement.SaveNote()
 42:              );
 43:          }
 44:      }
 45:  
 46:      public class Mappings : IMappingProvider
 47:      {
 48:          public void RegisterMap()
 49:          {
 50:              AutoMapper.Mapper.CreateMap<SaveNoteModel, Note>();
 51:          }
 52:      }
 53:  }

Cóż tu widzimy? Jeden plik zawierający prawie wszystko, co jest potrzebne do oprogramowania jednej akcji. Kontroler jest partial – bo w innych plikach (które nazywam CompaniesManagement_SaveNote.cs, CompaniesManagement_DeleteCompany.cs etc…) piszę inne akcje, będące częściami tego samego kontrolera. Modele są zagnieżdżone – więc nigdy nie nastąpi żaden konflikt nazw z innymi mikroskopijnymi klaskami rozsianymi po projekcie. Tak samo definicje routingu oraz mapowań – każda akcja sama sobie definiuje pod jakim URLem chce się znajdować i w jaki sposób skorzystać z AutoMappera.

Dostrzegłem bowiem, że tak naprawdę NIC mi nie daje trzymanie modeli w jednym katalogu – bo nigdy nie potrzebuję dostępu do więcej niż jednego naraz. Tylko powoduje bałagan i zamieszanie podczas nawigacji. To samo z mapowaniami – i tak każda akcja ma własny model (lub dwa), więc i mapowania są wyłącznie dla niej charakterystyczne. A routing… No tutaj wiele już zależy od specyfiki danego systemu, ale generalnie podoba mi się swoboda, jaką daje przedstawione rozwiązanie: hierarchia URLi nie musi wcale pokrywać się z organizacją kontrolerów w “aree” oraz grupowanie akcji w kontrolery.

Wszystkie zależności wymagane przez wszystkie akcje kontrolera znajdują się w jednym konstruktorze, zdefiniowanym w głównym pliku CompaniesManagement.cs, który nie zawiera żadnych akcji. Zadaniem tego pliku jest przyjęcie zależności, dziedziczenie z odpowiedniej klasy bazowej oraz nałożenie atrybutów wspólnych dla wszystkich akcji (jak [Authorize]).

Okazało się, że taka organizacja kodu w projekcie webowym znacznie ułatwia mi pracę. Początkowo podchodziłem do tego bardzo sceptycznie, ale… po jakimś czasie wszystkie nowe funkcjonalności powstawały właśnie w ten sposób.

Zastanawiałem się czy nie pójść o krok dalej i nie “scustomizować” zachowania MVC tak, aby jeszcze bardziej wyeksponować akcję jako główny element rozwiązania webowego. Chodzi o to, aby każda akcja otrzymała osobny katalog, a w nim zarówno przedstawiony wyżej kod C#, jak i widok cshtml. Ale tego jeszcze nie wypróbowałem.

Co o tym myślicie? Tak jak napisałem – w moim przypadku takie coś naprawdę się sprawdza. Pliczki z akcjami są niewielkie i łatwo się po nich poruszać, a mają wszystko co potrzeba.

 

P.S.: Proszę po powyższym kodzie zbytnio nie jechać – ma on służyć jedynie demonstracji mojej koncepcji i nie jest żywcem wzięty z żadnego systemu.

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

28 Comments

  1. Uwaga, Gutek właśnie zwrócił mi uwagę, że klasa Mappings w powyższym kodzie nie ma metody – i faktycznie, to nie jest żadna magiczna sztuczka, tylko po prostu zapomniałem:). Tak jak pisałem, kod nie jest skopiowany z żadnego projektu, a dostosowany w Notatniku na potrzeby posta aby przedstawić ideę/koncepcję.
    Posta poprawiam, ale do RSSów poszła głupota. Dzięki Gutek za uwagę!

  2. organizacja calkiem spoko, jednak ja troche inaczej do tego podchodze:

    controllers
    Name
    ViewModels
    ClassInput.css -> ClassInput { p FileInput F {get;set;} class FileInput {} }
    ClassDisplay.css
    Mappings
    … -> other folders and classes req by controller
    NameController.cs
    views
    ControllerName
    View.cshtlm
    GrouppedByControllerType
    ControllerName
    View.cshtml
    public
    css
    js
    app.js -> app.views.controllerName(); all views in one file

    ale pomysl by przezucic jeszcze view jest calkiem niezly

    rozbicia zas na akcje bym pewnie nie robil, raczej bym pomyslal o czyms takim http://lostechies.com/jimmybogard/2011/06/22/cleaning-up-posts-in-asp-net-mvc/

  3. Zawsze mnie to zastanawia i w końcu muszę gdzieś o to zapytać. Load w NHibernate pobiera cały rekord w bazy po identyfikatorze. Rozumiem, jeżeli akurat w danej tabeli jest tylko kilka pól, np id, nazwa, to nie ma problemu. Co jednak w sytuacji, gdy w takiej tabeli jest pól przykładowo 30, a my potrzebujemy przerzucić do widoku tylko kilka wybranych, bo szczegóły w danej chwili nie są nam potrzebne? Używać get/load i później mapować to na na własne view modele? Czy w takich sytuacjach korzystać właśnie z Projections, którą udostępnia NHib? Wydaje mi się, że w celach wydajnościowych oczywiście należałoby używać projekcji, jednak wszędzie, gdzie nie spojrze, nawet w projektach z codeplexa, wszyscy jadą to właśnie w taki sposób, get/load i później dopiero mapowanie.

  4. @ja

    load tworzy proxy tak samo jak get, roznica polega na tym:
    1) get-> jezeli nie ma w cache i 2nd level cache entity, robi hit do bazy, jak nie ma entity to zwraca null, lub proxy
    2) load-> tworzy proxy i nie uderza do bazy, jednak jak robi sie transaction commit i entity o danym ID nie istnieje w bazie danych zwracany jest wyjatek

    ogolnie powinno sie uzywac Get i Load jak chce sie miec proxy ale to czy get czy load zalezy od sytuacji. napewno nie powinno sie robic Criteria po ID – w sensie, nie powinno sie robic by zwrocic tylko proxy po to by moc je przypisac gdzie indziej. zas oplaca sie robic QueryOver, LINQ whatever jezeli chce sie pominac SELECT N+1.

    a wiec mapowanie na viewmodels, jest uzaleznione od tego czy istnieje mozliwosci select n+1 -> na przyklad blog post i comments do blog postu, chca pobrac to wszystko na raz, lepiej zrobic QueryOver etc. niz get. Ale jezeli nie ma szans na select n+1 to nalezy sie zastanowic jak chce sie to obsluzyc po stronie controllera -> get nie zwroci exception, load zwroci.

    moze ktos jeszcze cos doda do tego :)

  5. Podoba mi się takie rozłożenie kodu. Jednak gdy zapragniesz dodać kolejną akcję w innym pliku, to się posypie
    na klasach do routngu i mappingu, bo nazwy te same. Interesuje mnie jeszcze, jak wywołujesz dodanie tych routingów i mappingów.
    Gdyby to były tylko dwie klasy, ktore zapisałeś, to można z konstruktora. ale w takim przypadku? Chyba że w kolejnych akcjach jakoś można dodać kolejne mappingi/routingi do już tych istniejących klas. Prosiłbym o rozwinięcie tematu w tym kierunku.

  6. ja,
    To zależy od kontekstu, od systemu, od przewidywanego obciążenia, od ilości danych… Tutaj skupiłem się na całkowicie innej kwestii.
    1) w takim scenriuszu użyłbym Get() a nie Load(), ale w kontekście posta nie ma to znaczenia
    2) nic nie stoi na przeszkodzie aby mieć więcej niż jedną klasę mapowaną na tabelę Company – może być Company zawierająca właściwości dla wszystkich kolumn, a może być np CompanyWithNotes zawierająca tylko id, nazwę i referencję do notatek
    Z tym że nie ma sensu bawić się w takie rozróżnienia w systemie, dla którego nie zrobi to żadnej różnicy.
    Z kolei przy pobieraniu więcej niż jednego rekordu często mapuję już na poziomie samego zapytania, żądając tylko tych danych które muszę wyświetlic w danym widoku (czyli pokrywające się np z kolumnami grida na ekranie).

  7. eric,
    Masz rację w takim przypadku klasy mapowań i routingu zrobiłbym partial albo tworzył o unikalnych nazwach, np RoutingForSaveNote, MappingsForSaveNote etc.. Ale pewnie stanęłoby na partialach.

    Rejestrację mapowań i routingu robię w jakimś app_start czy innym bootstraperze – wszystko automatycznie rejestruje mi się w Autofac, więc gdzieś przy starcie aplikacji mam coś takiego:
    foreach (var m in container.Resolve<IEnumerable<IMappingProvider>>())
    {
    m.RegisterMap();
    }

  8. Awesome:) Bardzo mi się podoba. Chociaż ja osobiście nie wrzucałbym tego w partial classę opakowującą, tylko wrzucił jako osobne klasy do jednego katalogu. Ale idea jest cudowna:)

  9. Szymon,
    Od 2 dni zagłębiam się bardziej w Fubu (w sumie napisanie tego posta mnie skłoniło do przyjrzenia się Fubu bliżej:) ) i tam wszystko opiera się na podejściu: coś takiego jak kontroler w ogóle nie jest potrzebne, ważne sa tylko AKCJE. I ta idea, jak i implementacja, BARDZO mi się podobają, polecam rzucenie okiem (pewnie coś o tym skrobnę za jakiś czas).

  10. "Zastanawiałem się czy nie pójść o krok dalej i nie "scustomizować" zachowania MVC tak, aby jeszcze bardziej wyeksponować akcję jako główny element rozwiązania webowego. Chodzi o to, aby każda akcja otrzymała osobny katalog, a w nim zarówno przedstawiony wyżej kod C#, jak i widok cshtml. Ale tego jeszcze nie wypróbowałem."

    To bardzo ciekawe podejście a ASP.NET MVC, coś podobnego jest u konkurencji chyba w Zend Framework, gdzie można dowolnie ustawiać sobie strukturę katalogów. Takie rozwiązanie które zaproponowałeś to zajebiście czytelna i skompresowana struktura projektu. Niby MapAreas ma poprawiać czytelność ale nadal jak widać tu http://haacked.com/archive/2008/11/04/areas-in-aspnetmvc.aspx na zrzucie struktura jest to nadal rozwleczona.

  11. Łukasz,
    Areas to moim zdaniem trochę pomyłka. Co prawda korzystam z nich, ale… one po prostu dodają kolejny poziom organizacyjny, nie zajmując się źródłem problemu. A źródłem problemu jest zbyt chaotyczna i skomplikowana struktura. Po wprowadzeniu Areas nadal mam burdel – tyle że każdy burdel zamknięty jest we własnym folderze. Kilka odseparowanych od siebie burdeli może i jest lepsze niż jeden mega-burdel, ale im dłużej się nad tym zastanawiam tym mniej to do mnie przemawia.

  12. Hej, w moim projekcie też się zastanawialiśmy nad organizacją katalogów tak, by wszystko dotyczące jednego "feature" było w jednym miejscu.

    Wygrał układ, który dokładniej opisałem tutaj: http://dotnet.uni.lodz.pl/whut/post/2011/07/03/Better-ASPNET-MVC-folder-structure.aspx

    Pokrotce wygląda on tak:
    Areas\ // wolałbym nazwę "Features", ale Areas jest wymagane przez ASP.NET MVC
    AREA#1\
    Views\ // ten katalog też istnieje tylko po to by zadowolić ASP.NET MVC
    CONTROLLER#1\ // bezpośrednio w tym katalogu są widoki
    Code\ // tu jest controler, modele, mapowania, itp.
    Content\ // tu js i css
    Images\
    CONTROLLER#2\
    Code\
    Content\
    Images\

    Shared\ // zwykle tu nic nie ma
    Code\
    Content\
    Images\
    AREA#2\

    Views\
    CONTROLLER#3\

    Shared\ // layout page, error page, itp
    Code\ // wspólny kod, np action filters
    Content\ // css od layout page, wspólne javascripty
    Images\

    Zalety:
    – gdy pracujesz na jednym kontrolerem, wszystko jest w jednym katalogu: Areas\AREANAME\Views\CONTROLLERNAME
    – taki układ wyraźnie oddziela modele/js/css jednego kontrolera od drugiego, współdzielenie ich jest niezalecane
    Wady
    – głęboka struktura katalogów, zatem nawigowanie pomiędzy kontrolerami jest trudniejsze

    Co sądzicie?

  13. BTW. twoja koncepcja przypomina mi MVVM: partial klasa z jedna akcją kontrolera i modelami do niej to odpowiednik jednego ViewModelu.

  14. Jeszcze jeden komentarz: rozbicie akcji kontrolera na oddzielne pliki jest fajny: zwiększa czytelność i mniej kodu jest do przejrzenia, by zrozumieć co się dzieje. Ale IMO prostszym rozwiązaniem jest rozbicie kontrolera na oddzielne klasy, czyli np. zamiast PersonController z metodami SaveNew/Edit dwa kontrolery: SavePersonController i EditPersonController.

    Ten opisany przeze mnie wyżej układ katalogów jest pod takie podejście do kontrolerów: byłby tam area Persons z kontrolerami SaveController i EditController

  15. Whut,
    Czy jeden kontroler partial, czy osobne kontrolery – nie ma to dla mnie znaczenia. Kontroler jest tylko paczką z akcjami.
    A Twoja koncepcja mi pasuje, chociaż wydaje mi się że nie idzie o wiele dalej niż to co napisałem. Ale faktycznie akie wykorzystanie "Area" jest bardziej wygodne niż "standardowe", zalecane przez MS.
    A nawigacja w kodzie nie jest problemem – Resharper to załatwia. Bez Resharpera to nie wiem czy bym w ogóle został przy VS:).

  16. Fakt, jedyne praktyczna różnica to to, że zamiast partial klas używam areas:)

    "Kontroler jest tylko paczką z akcjami" – muszę spojrzeć na FuBu żeby to poczuć

  17. Witam,

    u nas podobnie jak u WHUT’a.

    Areas/
    Nazwa Featura (zazwyczaj opcji w menu)/
    Views/
    Nazwa Controllera/
    Code, Content, pliki cshtml

    Projekt z testami odpowiada powyższemu tylko do 2 poziomu.
    Areas/
    Nazwa Feature/
    pliki .cs z testami

    Natomiast w projekcie odpowiadającym warstwie logiki.
    Domain/
    główne klasy + mapowiania nHibernate

    Features/
    Nazwa Featura (która niestety nie odpowiada Nazwie Featura z apliacji MVC)/
    głównie pliki z handlerami agathy

    W sumie tyle. Staramy się dzielić kod w stylu: jeśli masz jakiś Feature w aplikacji
    to jest to osobny folder.

  18. Właśnie się spotkałem z pawelkiem, okazało się, że siedzi biurko obok:)

    Pomysł na podział jest z bloga Ayende:)

  19. PrzypadkowyObserwator on

    [quote]
    wolałbym nazwę "Features", ale Areas jest wymagane przez ASP.NET MVC
    [/qoute]
    [quote]
    // ten katalog też istnieje tylko po to by zadowolić ASP.NET MVC
    [/quote]
    Można w prosty sposób pozbyć się tych ograniczeń, dziedzicząc ViewEngine i podstawiając mu swoje ścieżki. Taką technikę stosuje np. SharpArchitecture (https://github.com/sharparchitecture/Sharp-Architecture/blob/1.9.6.0/src/SharpArch/SharpArch.Web/Areas/AreaViewEngine.cs)
    Pozdrawiam
    H.

  20. PrzypadkowyObserwator on

    Jeszcze odnosząc się do zaprezentowanego pomysłu – podoba mi się, ale mam kilka uwag :)
    1. Chyba w każdym większym projekcie najwięcej czasu poświęca się na nawigację pomiędzy plikami. Jestem użytkownikiem SharpArchitecture, gdzie wraz z dobrodziejstwem inwentarza przychodzi oddzielenie widoków od kontrolerów (osobne biblioteki), więc gonitwy mam dwa razy więcej :)
    2. Naprawdę piszecie osobne modele dla wejścia i wyjścia każdej metody? Matko ile wy musicie mieć klas w projektach… A potem aktualizacja tego to musi być masakra…
    Przy klasach na których ‘cykl życia’ polega jedynie na istnieniu, kontroler ogranicza się do CRUD, a modele mam zwykle dwa – jeden dla listy i drugi do tworzenia i edycji – ten funkcjonuje zarówno dla wejścia jak i wyjścia. Obydwa dziedziczą po klasach bazowych, których świadomy jest kontroler. W dodatku operacja mapowania i "odmapowania" odbywa się w samym modelu a nie w kontrolerze. Dzięki temu sam kontroler często jest "anemiczny" bo po prostu dziedziczy po klasie bazowej a poszczególne metody są przeciążane jedynie w celu nadania uprawnień poprzez dekorację atrybutami.
    3. Podoba mi się pomysł z przerzuceniem routingu w to miejsce. Ale znowu – dla większości zastosowań z którymi mam styczność, wystarczają mi 2-3 modele routingu które załatwiają sprawę. Znowu klasa bazowa i anemiczni potomkowie załatwiają problem
    Ale… Może ja się nie znam? Chętnie podyskutuję jakby co :)

    H.

  21. PrzypadkowyObserwator,
    Ano z ViewEngine racja, spoko tip.

    1. Co do nawigacji – resharper to the rescue, ale i tak otwieranie kilku malutkich plików jednoczesnie (nawet jesli samo ich znalezienie jest banalne z R#) to dla mnie osobiscie pewien dyskomfort.
    2. Klas mogę mieć i milion, co za różnica? Tekst to tekst. A problemu z aktualizacją nie do końca rozumiem. Aktualizacja czego musi być masakrą? Przecież nie trzeba nigdy zmieniać wszystkich klas naraz, jednocześniej, tylko każda z nich odpowiada za jeden malutki wycinek systemu i jest niezależna od reszty.
    No i te klasy są zwykle po prostu zbiorem get/set i niczym więcej, zwykłe DTO.
    3. Akurat w ostatnim projekcie nadawałem ręcznie URL dla prawie każdej akcji osobno. Po 1) URLe musiały być po polsku, a po 2) – nie były zgodne ze strukturą kontrolerów. Więc tych definicji miałem masę.

  22. Przypadkowy Obserwator on

    ad1 ahhh więc do tego używa się resharpera ;) Robiłem do niego kilka podejść i nigdy się nie przekonałem. Ale może trzeba jeszcze raz…
    ad2 Co do ilości klas – sama w sobie nic nie zmienia, choć ma wpływ np. na kompilację. Dlaczego masakrą – dlatego że jak dodajesz jakieś pole do klasy biznesowej to musisz je później propagować na co najmniej 2 miejsca. A potem jeszcze reguły walidacji, testowanie itp…
    Chyba że masz jakąś sztuczkę która to automatyzuje?
    ad3 Jak już pisałem – pomysł mi się podoba, a w szczególności jeśli wymagania narzucają tak rozbudowaną strukturę routingu.
    pozdrawiam
    H.

  23. Przypadkowy Obserwator,
    1. Przedstawianie R# jako "narzędzia do nawigacji" jest dla niego krzywdzące, ALE gdyby faktycznie tak było (tzn gdyby R# oferował wyłącznie nawigację w kodzie) to i tak byłby warty dzisiejszej ceny. A że przy okazji dostaje się milion razy więcej to tym lepiej:).
    2. Wpływ liczby klas/plików na kompilację… no nie wiem czy powinniśmy w ogóle się takimi rzeczami przejmować, to dla mnie trochę przesada. A właśnie mnogość tych klas powoduje, że modyfikacja klasy biznesowej NIE WYMUSZA zmian w X innych miejsc – a tylko w tych, gdzie jest to konieczne, czyli dla ekranów które faktycznie tych nowych informacji potrzebują. Dla mnie łatwiej tak, niż mieć 10x więcej informacji w jednym wspólnym modelu.

  24. PrzypadkowyObserwator on

    1. Co zabawne, licencję na R# mam – wygrałem na jakiejś konferencji, ale poważnie nie mogę się przekonać. Jestem przyzwyczajony do skrótów i własnych szablonów w VS, a R# zawsze mi wszystko rozwala. Pewnie gdyby go skonfigurować to dałby radę ale jakoś zawsze brakuje mi cierpliwości. A może strzelisz jakąś serię artykułów "Resharper dla starych wyjadaczy VS"?

    2. Odnośnie liczby plików/klas – nie chciałbym żebyś mnie źle zrozumiał. Doskonale wiem, że każdy projekt ma inne wymagania, które mogą prowadzić do różnych ciekawych rozwiązań – podobnie jak przy dyskutowanym wcześniej routingu.
    Oczywiście nie jestem zwolennikiem jednego wielkiego modelu, wyczerpującego wszystkie przypadki – we wszystkim należy zachować umiar. Nadal uważam, że największym problemem jest utrzymanie spójności pomiędzy klasą biznesową a modelami.
    Clue problemu jest "Czy masz na to jakiś patent". Ja na przykład zacząłem eksperymentować z generatorami kodu i ta technika wydaje się być bardzo obiecująca. Niestety przygotowanie dobrego generatora jest czasochłonne, a przygotowanie generatora który będzie obejmował 100% przypadków – praktycznie niemożliwe. Ale to dyskusja na inny artykuł :)

    A jeszcze odnośnie kompilacji – nie wiem jak Ty, ale ja kompiluję projekt bardzo często – czasem nawet co minutę, bo taki mam styl pracy. Coś zmieniam i kompiluję żeby się upewnić że jest ok. Jeśli kompilacja wydłuża się, automatycznie spada mi produktywność :)

    Oraz – last but not least – im więcej klas tym dotfuskator bardziej głupieje :)

  25. 1) Taką serię kiedyś puściłem (w 3 odcinkach), co prawda było to dość dawno i tyczy wersji chyba 3.x, ale wszystko co tam jest pokazane nie straciło na aktualności, polecam http://www.maciejaniserowicz.com/?tag=/c%23+via+r%23

    2) Patentu jako takiego nie mam, ale na pewno nie będę szedł w kierunku generacji kodu. Po prostu widzę szkic UI i dane jakie mają być na nim pokazane -> i tworzę dla niego model. Nie szukam czy taki lub podobny już gdzieś istnieje. Po prostu każdy ekran/widok ma swoją własną klasę.

    A co do kompilacji… znów się R# kłania:). Nie muszę kompilować co chwilę projektu bo resharper robi to za mnie w tle i pokazuje błędy jakie popełniłem. Na bieżąco.

    Na temat dotfuscatora sie nie wypowiem bo nie uzywam.

  26. Fakt zmiana biznesowa, pociąga za soba zmiany w DTO oraz modelu… Ale nie widzę innej możliwości.
    Fajny tip z tym ViewEngine, ale jak mówił WHUT (przed chwila rozmawialiśmy) to jak zaglądał do kodu to słówko Views widział zahardkodowane w wielu miejscach. Sam nie patrzyłem więc to tylko taki assume :)

    Natomiast refactoring tak wielu klas pociąga za sobą masakryczna robotę i spodziewać się można że czasem drobna zmiana przebiegająca przez cały projekt (jak wczorajsza zmiana typu ze string na AccountNumber) to kilka godzin roboty.

    Pozdrawiam
    P.

  27. Właśnie sprawdziłem drugi raz, nie wiem, gdzie ja widziałem to "Views" czy "Areas" zahardcodowane w wielu miejscach. Teraz oprócz ViewEngine widzę je teraz tylko raz i to dodatkowo w klasie, która zdaje się, że jest unsupported.

    A do komentarza wyżej: jak jest wiele modeli to jest łatwiej, IMO, tak jak pisał wyżej procent. My w naszej aplikacji mamy "masakryczną robotę" od czasu do czasu, bo oprócz wielu modeli mamy też wiele DTOsów, (to wynika z specyfiki projektu).