[HomeWallet] Pierwszy kod

Zbliża się koniec tygodnia, czas opisać prace nad projektem.


PostgreSQL z .NET Core

Pierwszym krokiem było oczywiście utworzenie nowego projektu. O tym, jak to zrobić, napisałem w pierwszym wpisie NaSzybko. Aby nie musieć zbyt dużo zmieniać w kolejnych wersjach .NET Core, postawiłem na nowe SDK. Projekt utworzyłem poprzez Visual Studio 2017. Po zainstalowaniu nowego SDK na macOS projekt działa na obu platformach. Wszystkie operacje opisywane na blogu będą dotyczyły więc projektów opartych na .csproj.

Kolejnym naturalnym krokiem było dodanie istniejącej już bazy danych do projektu. Aby to zrobić, musiałem dodać do projektu Npgsql – najpopularniejszy Data Provider PostgreSQL dla .NET. Jest on również projektem open-source. Npgsql znajdziemy też w NuGet.

Co zrobić, żeby działało?

Aby skonfigurować nasz projekt z PostgreSQL należy dodać do pliku .csproj dodatkową referencję.

<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="1.1.0" />

 

Bez tej referencji nie jesteśmy w stanie ustawić naszej bazy danych na PostgreSQL. Nie dodaje się ona( a przynajmniej mi się nie dodała) automatycznie. Z tego co pamiętam, przy .NET Framework było inaczej.

W pliku ApplicationDbContext.cs dodajemy metodę

protected override void OnModelCreating(ModelBuilder builder)

 

W jej wnętrzu ustawiamy domyślny katalog bazy na “dbo”.

protected override void OnModelCreating(ModelBuilder builder)

        {

            // PostgreSQL uses the public schema by default - not dbo.

            builder.HasDefaultSchema("dbo");

            base.OnModelCreating(builder);

        }

 

Po zmianach w  ApplicationDbContext przechodzimy do startup.cs i ustawiamy naszą bazę na PostgreSQL jednocześnie wskazując na connection string. Jak zrobić, żeby nie udostępniać naszych danych całemu światu opisuję tutaj: NaSzybko #2. Domyślnie connection string znajduje się w pliku appsettings.json

 

var connectionString = Configuration["ConnectionString"];
services.AddDbContext<ApplicationDbContext>(opts => opts.UseNpgsql(connectionString));

 

Tworząc projekt z autoryzacją, baza danych tworzy nam się automatycznie, jednak jest to SQL Server(w przypadku Visual Studio). W odróżnieniu od .NET Framework, tworzy się również automatycznie migracja. Aby tabele, które powstały dla użytkownika powędrowały do odpowiedniego katalogu w PostgreSQL, musimy usunąć migrację ( poleceniem Remove-Migration), a następnie dodać ją na nowo (Add-Migration). Spowoduje to zmianę lokalizacji tabel i pewność, że wszystko jest dobrze opisane.

Po tych wszystkich operacjach aplikacja powinna bezproblemowo łączyć się w naszą bazą. Czas zacząć kodować!


Dynamiczne ładowanie danych

Moim zamysłem przy wymyślaniu HomeWallet było to, żeby na stronie głównej witały nas nasze paragony posegregowane wg dni. Nie chciałem jednak, aby cała historia ładowała się nam na start, bo nie ma to sensu, a i wydajnościowo nie jest to najlepszy pomysł łagodnie mówiąc. Postanowiłem, więc wykonać przycisk, który powodowałby załadowanie kolejnych dni z historii, przykładowo trzech.

Wygląd strony głównej

Do wykonania tego zadania posłużyłem się Partial View oraz wywoływaniem metod poprzez AJAX, w czym pomaga mi jQuery.

Najpierw poprzez jQuery pobieram najstarszą datę, która jest aktualnie wyświetlana na stronie. Przekazuję ją poprzez AJAX do metody kontrolera.

Pobieranie najstarszej daty i wysłanie jej do kontrolera

W kontrolerze pobieram trzy daty, oczywiście posegregowane, mniejsze od tej przekazanej do metody. Następnie dla każdej z tej daty pobieram wszystkie paragony i dodaje ViewModelu. Listę tych trzech ViewModeli przekazuję do Partial View, który poprzez AJAX jest przechwytywany i wyświetlany na stronie.

Metoda do pobierania paragonów z trzech wybranych dni

BeginCollectionItem

Większym wyzwaniem było dodawanie paragonu. Jak wiadomo, paragon składa się z produktów. Lista tych produktów powinna tworzyć się dynamicznie. Na pierwszy rzut oka nie jest to wcale takie proste, bo wydawałoby się, że lista w widoku powinna być indeksowana od 0 a kolejne indeksy powinny być inkrementowane o jeden. Pojawia się wtedy problem z usuwaniem obiektów z takiej listy, bo gdy usuniemy obiekt ze środka listy, to wszystkie indeksy będące większymi od tego usuwanego należy na nowo zmienić.

Okazuje się jednak, że indeksy te wcale nie muszą być ani numeryczne, ani mieć określonego porządku. NIESTETY, Microsoft nigdzie się tym faktem tak naprawdę nie chwali. W oficjalnych poradnikach nie ma mowy o dynamicznym tworzeniu listy, a jedynie jej wyświetlaniu. W .NET Framework, przez tych wiele lat, powstało kilka rozwiązań dla problemu. Jednym z nim, którego używałem przy wcześniejszych projektach, był EditorForMany(). W poszukiwaniu innych rozwiązań natknąłem się jednak na BeginCollectionItem. Okazało się, że rozwiązanie to jest już również zaimplementowane dla .NET Core, a nawet dodane jako paczka w NuGet.

Jak działa? Do naszej dyspozycji oddany jest nowy znacznik Html.BeginCollectionItem. Powoduje on wygenerowanie w widoku obiektu listy(wybieranej jako parametr) z unikalnym id. Id to będzie przypisane do każdego elementu obiektu który zawrzemy wewnątrz klamr.

Przykład zastosowania BeginCollectionItem

Powoduje to, że cokolwiek byśmy nie robili później z innymi obiektami, nasza lista w kontrolerze zawsze będzie odczytana prawidłowo ze wszystkimi obiektami zawartymi w widoku.

BeginCollectionItem w kodzie HTML.

 

Jak widać na wcześniejszym zdjęciu, dodawanie paragonu jest na oddzielnej stronie która w domyśle ma przypominać faktyczny paragon. Dodawanie produktu odbywa się poprzez okienko modalne, wykorzystałem do tego jQuery dialog.

Modalne okno do dodawania produktu do paragonu

Widok jest pobierany z Partial View, następnie poprzez AJAX formularz wysyłany jest na serwer a odpowiedź ładowana już do widoku głównego paragonu. Wszystko odbywa się bez przeładowania strony.

Pobranie Partial View do okna modalnego i przesłanie formularza do kontrolera

Na razie to tyle jeśli chodzi o HomeWallet. Cały kod oczywiście możecie znaleźć na moim GitHubie.

Zapraszam do komentowania i do zobaczenia w następnym tygodniu.

Cześć!

2 komentarzy

  1. Cześć. Chciałbym zwrócić uwagę na przeładowane metody w kontrolerach – Robią zdecydowanie za dużo co zmierza do tzw. fat controller.
    Może dodaj warstwę pośrednią, które będzie:
    #1 zwracać `dates` na podstawie lastDate
    #2 zwracać model, które jest użyty w widoku – tak aby pozbyć się tej całej sekcji z pętlą for z kontrolera.

    W ten sposób kontroler będzie cienki – delegował tylko potrzebne operacji do zewnętrznych serwisów. Wydzieloną logikę, będziesz mógł przetestować z pomocą testów jednostkowych. A i kod stanie się czytelniejszy, łatwiejszy w utrzymaniu 🙂
    Powodzenia!

    1. Jak najbardziej ma to sens! 😉 Faktycznie, muszę zacząć oddzielać logikę od kontrolerów, łapię się na tym już jakiś czas. Dzięki wielkie za zwrócenie uwagi 🙂

Dodaj komentarz