Gdzie zawrzeć logikę aplikacji

0

Witam ponownie ;)

Uczę się obecnie WPFa i EF i próbuję to ze sobą jakoś połączyć. Póki co jeśli chodzi o EF wykorzystałem DB First.

I teraz pytanie, gdzie zawrzeć "logikę biznesową"? Nie wiem w sumie czy to określenie pasuje, ale może wyjaśnię.

Struktura mojej aplikacji to m.in. folder Views z xamlami, ViewModels, Services, Models gdzie wrzucam model z bazy i jeden dodatkowy z klasami do MVVM. I teraz tak. Mam małe okienko do logowania. Dwa pola login i hasło. W folderze Models mam model edmx wygenerowany na podstawie bazy, gdzie mam tabelę z użytkownikami oraz klasę modelu Users, również wygenerowana z bazy. Coś takiego:

namespace WpfApp6.Models
{
    using System;
    using System.Collections.Generic;
    
    public partial class User
    {
        public int Id { get; set; }
        public string Username { get; set; }
        public string PasswordHash { get; set; }
    }

    
}

Jeśli chodzi o przepływ danych to po kliknięciu przycisku w widoku ViewModel ustawia wartość labelki na podstawie danych.

private void Click()
        {
            if (UserService.LoginUser(new Models.Users(username, password)))
            {
                Message = "Logowanie ok!";
                
            }
            else
            {
                Message = "Logowanie nieudane!";
            }
            
        }

W serwisie mam logikę do sprawdzania danych w bazie. Pomijam bezpieczeństwo i sposób sprawdzania, chciałem tylko sprawdzić jak to wszystko ze sobą działa.

public static class UserService
    {
        public static bool LoginUser(User user)
        {
            using (var company = new CompanyDBEntities1())
            {
                Users userToCheck = company.Users.FirstOrDefault(u => u.Username.Equals(user.Username));
                if (userToCheck == null)
                {
                    return false;
                }
                return userToCheck.PasswordHash.Equals(user.PasswordHash);
            }
        }
    }

Nie wiem czy sposób z klasą statyczną w tym UserService jest ok, ale nie miałem innego pomysłu.

No i teraz przejdę do tego do czego mam pytanie.

Jak widać z kodu powyżej w klasie User potrzebuję konstruktora dwuargumentowego. Problem w tym, że nie powinienem edytować klasy User z modelu bo przy updacie modelu mi ją stworzy na nowo.

Jak to w takich sytuacjach się robi? Ja to obszedłem w ten sposób, że w tym samym namespace w katalogu Models stworzyłem partial class User i tam dopisałem konstruktory. Jednak nie wiem, jaka jest dobra praktyka jeśli chodzi o to... Co do pytania o logikę biznesową to chodzi mi o m.in. metody na klasie modelu User.

4

Byłeś nawet blisko. :-)

  • Zainstaluj sobie w projekcie jakiś kontenerek IoC. Jest tego trochę np. Unity Container od M$ czy Ninject. Pozwoli Ci to automatycznie wstrzykiwać zależności do konstruktorów viewmodeli;
  • Cały model włóż do osobnego projektu w solucji;
  • W modelu utwórz interfejsy, które będą reprezentować API modelu na zewnątrz. Utwórz je w jakichś osobnych namespace'ach (folderach czy osobnym projekcie);
  • Jak już będziesz miał interfejsy reprezentujące Twoje API to utwórz ich implementacje w modelu (serwisy);
  • Jak już będziesz miał implementacje tychże interfejsów to zbinduj jest z nią w konfiguracji kontenera IoC. Zwyczajnie bindujesz interfejs do implementacji w kodzie C#, zazwyczaj w wyższych warstwach;
  • Możesz teraz wstrzykiwać interfejsy w konstruktorach viewmodeli np: public MainViewModel(ICarWash carWash) i korzystać z ich metod (czyli z API modelu) w swoich klasach;
  • DbContext EF (czy jak mu tam) oczywiście ukrywasz tak żeby nie było go widać na zewnątrz modelu. API dostarcza publiczne metody, a wyższe warstwy nie mają prawa wiedzieć jak to działa. One tylko korzystają. Najlepiej pomyśl co gdzie byś rozmieścił gdybyś miał np. zmienić ORM tak, żeby w minimalnym stopniu ruszyć resztę.

I już. Dzięki temu masz ładny i chudziutki viewmodel bez logiki biznesowej, korzystający z interfejsów API Twojego modelu. Aha, do MVVM polecam Prism'a albo MVVM Light z Event aggregatorem w tym pierwszym i Messengerem w tym drugim. Oba te magiczne narzędzia pozwolą Ci jeszcze bardziej rozdrobnić viewmodele i efektywniej korzystać z dobrodziejstw naszego dobrodzieja M$ w postaci UserControl's. Oba robią w sumie to samo, a pisanie apek bez nich to dla mnie już masochizm.

I ósmy edit ale cóż: polecam jeszcze Prism Templates do VS :)

0

O, rozumiem, przynajmniej się staram zrozumieć dzięki za odpowiedź ;)

Co do struktury jeśli chodzi o wyrzucenie modelu do oddzielnego projektu. Tak zamierzam to wyrzucić, ale dzisiaj miałem chwilę to tak na szybko chciałem zrobić pobranie z bazy do DataGrida.

Co do kontenerów IoC, frameworków MVVM, ciężko mi to na razie ogarnąć, bo po 1 dopiero zaczynam z WPF i całym MVVM i nie wiem czy to dobry pomysł zaczynać od ułatwień zanim rozgryzę do końca jak to działa. Po 2 interfejsy... Trochę muszę z tym popracować, żeby sobie ułożyć wiedzę jak z nich tak na prawdę korzystać wykorzystując ich możliwości. Szczególnie, że w większości materiałów( czy to na nawet na studiach i kursach video) interfejsy to tylko coś co "każe" klasie go implementującej posiadać konkretne metody. Ale jak to praktycznie wykorzystywać dowiaduję się dopiero po tym jak przysiadłem do WPFa.

Ok, ale wyjaśniłeś mi trochę spróbuję to jakoś wykorzystać, ale nie zmienia to faktu, że chyba czasami przyda mi się skorzystać z obiektu User i chciałbym tam przekazać jakieś wartości przez z konstruktor. Jak to jest? No chyba, że się mylę i nie będę tego potrzebować.

EDIT: Dobra ściągnąłem Prism Template Pack i nugeta Prism.Wpf no faktycznie całkiem przyjemne, póki co muszę rozgryźć ten region w widoku, co się tworzy z template, a jak tam coś wrzucę to wyrzuca wyjątek w klasie bootstraper, ale może ogarnę :)

1

...że chyba czasami przyda mi się skorzystać z obiektu User

Nie. Nie możesz robić tak, że pchasz encje z ORM do wyższych warstw. Model komunikuje się z wyższymi warstwami za pomocą DTO, które np. może być projekcją zapytania uzyskanego przez ORM.

Pchaniem encji do widoku można sobie zrobić krzywdę. :)

W drugą stronę także: jeśli chcesz wstawić coś do bazy to operacja na encjach w modelu jest składana z danych przesłanych za pomocą DTO, np. jako argument metody.

Warstwa ORM to bebechy modelu, nie wypychane na zewnątrz do API.

0

Nie masz może jakiegoś tutoriala, gdzie jest chociaż większość rzeczy o których mówisz? ;) Ciężko mi znaleźć porządny tutorial albo kurs EF + WPF, nie mówiąc o Prism.

Dodatkowo przykład, mam view, gdzie dodaję nowego klienta a tam 20 textboxów. Mam oddzielnie przekazywać 20 stringów zamiast zbindować to do jakiejś klasy? Wybacz, jeśli to głupie pytanie.

2

Odnośnie tego przekazywania stringów za pomocą DTO do modelu: możesz wykorzystać takie narzędzie jak Automapper.
Co do tutoriala to szczerze mówiąc nie wiem co Ci polecić. Jest tego pełno w necie, a gros z tego pokazuje dokładnie jak tego nie robić ;)

Możesz poszukać czegoś o separacji modelu od reszty w takim MVC, bo to bardzo podobne mechanizmy. No i nie wiem czy coś konkretnego po polsku znajdziesz.
Konkretnych rzeczy nie wskażę, bo sam nie wiem :-) Zwyczajnie nie szukam w necie już o tym informacji.

0

Ok, dzięki za odpowiedź. Będę powoli działał, zobaczymy co z tego wyjdzie.

0

Przeniosłem odpowiedź na komentarz tutaj:

Mhm, czyli generalnie chodzi o to, że rejestruję sobie obiekty klas w kontenerze DI, a później mogę sobie te obiekty z kontenera wstrzykiwać do innych klas? Dobrze rozumiem?

Tak z tymże samo wstrzykiwanie dzieje się automatycznie, na podstawie zbindowanych interfejsów. W ogóle samo tworzenie obiektów można przenieść całkowicie na kontener IoC. Wtedy wstrzykiwanie interfejsów zrobi się automagicznie. Zresztą wstrzykiwanie zwykłych obiektów klas również. Starczy zbindować takie coś jako self, czy jakoś tak. :) Fajnie to działa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1