Generyczne repozytoria- czy to w ogóle ma jakiś sens?

2

@somekind:

tyle hipsterowania, a wystarczyło przeczytać w dokumentacji, że aby inne encje zostały pobrane, to trzeba joina zrobić :(

var entry = context.Entries.Include(x => x.Tags).Single(x => x.Title == entryTitle);

if (_tags.Count > 3) // Ważna logika biznesowa
throw new Exception("XD");

screenshot-20210220091400.png

w tym drugim przykładzie to jakbyś mógł napisać co dokładnie chcesz osiągnąć

1
nobody01 napisał(a):

Test 1) Dlaczego chcesz, żeby ef core (w sumie nawet nie ef core a sama aplikacja) tam rzucił wyjątek, skoro nie zaciągnąłeś _tags?

Bo powinny zaciągnąć się same? W końcu ten wspaniały ORM miał nie mieć problemu z polami.

Jak _tags są częścią agregatu Entry, to powinny być owned types, żeby ef core je zawsze zaciągał.

Czyli jednak jest problem, i trzeba się naginać do ograniczeń ORMa?

Test 2) Potrzebne była Ci kiedykolwiek taka funkcjonalność? Zazwyczaj chyba używa się instancji db contextu per request (scoped).

To nie jest funkcjonalność, ale dołączenie obiektu spoza kontekstu to dość standardowa potrzeba.

WeiXiao napisał(a):

tyle hipsterowania, a wystarczyło przeczytać w dokumentacji, że aby inne encje zostały pobrane, to trzeba joina zrobić :(

Faktycznie, z tym to działa, ale to jest pole, prywatne pole, miało nie być problemów!

w tym drugim przykładzie to jakbyś mógł napisać co dokładnie chcesz osiągnąć

To proste - mieć działającego ORMa. :D

Żeby nie było - ja widzę postęp, spory postęp. To wspaniałe, że już od trzech miesięcy nie trzeba tworzyć klas pośredniczących dla M:N, a chyba od nieco dłuższego czasu nie trzeba mieć dodatkowego Id, więc już można prawie programować obiektowo. Jeszcze jakieś 10 lat i Microsoft wyprodukuje całkiem sprawnego ORMa. Pytanie tylko, jak popularne będą relacyjne bazy danych za 10 lat. ;]

4

@somekind:

Faktycznie, z tym to działa, ale to jest pole, prywatne pole, miało nie być problemów!

czekaj bo czegoś nie łapie, oczekiwałeś że ORM będzie Ci sam z siebie walił jakieś JOINy do innych tabel?

ciekawy pomysł na ubicie wydajności, w NH tam tak wiele dzieje się "pod spodem"?

Fajnie że ten 🌼 wyszedł, bo to tylko kolejny argument ku temu, aby częściej w dyskusjach pokazywać kod, bo kod najlepiej weryfikuje rzeczywistość :)

1
somekind napisał(a):

dołączenie obiektu spoza kontekstu to dość standardowa potrzeba.

Mógłbyś podać przykład?

Czyli jednak jest problem, i trzeba się naginać do ograniczeń ORMa?

Użycie builder.OwnsMany w klasie konfiguracyjnej to naginanie się do ograniczeń ORMa?

a chyba od nieco dłuższego czasu nie trzeba mieć dodatkowego Id, więc już można prawie programować obiektowo

Shadow properties byly już w 2017: https://stackoverflow.com/a/47650462/13172711
W sumie to chyba od wersji 1.1 z 2016? https://csharp.christiannagel.com/2016/11/07/efcorefields/

0

Nie róbmy proszę wojny pomiedzy EF i NH bo nie o tym był temat :)
Może mi ktoś dać odpowiedz czy pobieranie danych z bazy moze opierać się na serwisie któy ma metody typu:

public async Task<User> GetByName(string name)
{
return await context.Users.FirstOrDefaultAsync(u=>u.Name==name);
}

czy jednak tak jest zle i powinno się robić inaczej (jak?)

3

@kalimata: Złe, bo jest bez sensu, gdy przyjdzie wyszukiwać po mailu, to będzie kolejna metoda w interfejsie. Dlatego najlepiej jest ukryć fakt, że użyty ef i po prostu wystawić

T GetByCriteria<T>(Func<IQueryable<User>, T>) // funktor, żeby łatwiej było zarządzać scopem

Come on, queryable są po to żeby korzystać, ani EF nie jest jedyną implementacją która to potrafi przerobić
(
https://github.com/zhurongbo111/ExpressionToWhereClause,
https://github.com/ryanwatson/Dapper.Extensions.Linq,
https://github.com/gambarra/ExpressionExtensionSQL
)

0

@Pixello: czyli w warstwie dostepu do danych można to zrobic tak jak napisałes, a gdzies tam wyzej np wysatawiajac api dla uzywtkownika moge mu ograniczyc ze szukanie jest tylko po mailu?

0

W sensie? Do obsługi endpointów jest OData + ewentualnie AutoMapper.MappingExpression, żeby można było userowi wystawić DTO zamiast obiektu domenowego.

Tam można w regułach ustawić co może konsument robić z którą właściwością

0

@Pixello

Dlatego najlepiej jest ukryć fakt, że użyty ef i po prostu wystawić

jakby coś, to w tym wątku (pierwsze dwie strony) już był poruszany pomysł na przekazywanie drzewek i generowanie z nich SQLa, ale ludzie uznali że to słabe.

bo jest bez sensu, gdy przyjdzie wyszukiwać po mailu, to będzie kolejna metoda w interfejsie. Dlatego najlepiej jest ukryć fakt, że użyty ef i po prostu wystawić

W ogóle ja bym poszedł krok dalej - po co mi jakaś kolejna abstrakcja nad EF Corem?

2
WeiXiao napisał(a):

czekaj bo czegoś nie łapie, oczekiwałeś że ORM będzie Ci sam z siebie walił jakieś JOINy do innych tabel?

No tak na moje oko to nie łapiesz dwóch rzeczy: SQL (bo żadne joiny nie są potrzebne, wystarczy select z where) i tego, do czego służy ORM.
Tak, oczekuję, że ORM będzie ładował obiekty wtedy, gdy są potrzebne do wykonania danej operacji. Jeśli nie potrzebujemy takiego zachowania, to prawdopodobnie nie potrzebujemy też ORMa.

Fajnie że ten 🌼 wyszedł, bo to tylko kolejny argument ku temu, aby częściej w dyskusjach pokazywać kod, bo kod najlepiej weryfikuje rzeczywistość :)

Jaki znowu kwiatek?! Lazy loading to podstawowy mechanizm i idea stojąca za ORM.

nobody01 napisał(a):

Mógłbyś podać przykład?

Customer ma Address, Address ma Country, a kolekcja Country sobie siedzi skeszowana w słowniku w pamięci.

Użycie builder.OwnsMany w klasie konfiguracyjnej to naginanie się do ograniczeń ORMa?

Tak, skoro ORM nie działa jak ORM, to jest mocno ograniczony.
Zobacz sobie jaki SQL wypluwa EF Core po użyciu OwnsMany, a potem pokaż @WeiXiao, żeby zobaczył jak wyglądają joiny robione pod spodem, żeby następnym razem nie wpadał na dziwne insynuacje pod adresem NH.

Shadow properties byly już w 2017: https://stackoverflow.com/a/47650462/13172711
W sumie to chyba od wersji 1.1 z 2016? https://csharp.christiannagel.com/2016/11/07/efcorefields/

Myślę, że już w 2016 to najbardziej udany dowcip w tym wątku. ;)

kalimata napisał(a):

Nie róbmy proszę wojny pomiedzy EF i NH bo nie o tym był temat :)

Nie wiem czy to do mnie uwaga, ale ja z EF tylko lekko kpię, NH to kto inny tu przywołał.

Może mi ktoś dać odpowiedz czy pobieranie danych z bazy moze opierać się na serwisie któy ma metody typu:

public async Task<User> GetByName(string name)
{
return await context.Users.FirstOrDefaultAsync(u=>u.Name==name);
}

czy jednak tak jest zle i powinno się robić inaczej (jak?)

@kalimata: A co Ci da to, że zmienisz nazwę takiej klasy z DAO czy tam repozytorium na serwis? Absolutnie nic, a nawet mniej niż nic, bo serwis to to nie będzie.

Zastanów się nad dwiema rzeczami:

  1. Czy w ogóle potrzebujesz takiej klasy. Każdy wrapper ma jedną zasadniczą wadę - ogranicza możliwości tego, co opakowuje. Czasami to jest potrzebne, ale użyte w złym miejscu powoduje tylko problemy. Alternatywa jest prosta - w kodzie, który implementuje Twój przypadek użycia, używaj ORMa bezpośrednio. Bez dodatkowej warstwy, za to z pełnią możliwości.
  2. A jeśli uważasz, że potrzebujesz takiego wrappera, to niech on coś wnosi. Robienie wrappera na ORMa z metodą, która przyjmuje jakieś Func<cośtam> to jest absurd, przecież to już potrafi ORM. Wrapper niech dostarcza jakiejś abstrakcji i pozwoli jego użytkownikowi nie wiedzieć jak coś ma być zrobione, tylko co ma być zrobione. Dlatego zdecydowanie więcej sensu ma tworzenie metod, tak jak to zaproponowałeś: GetByCośtam.
1
somekind napisał(a):

Jaki znowu kwiatek?! Lazy loading to podstawowy mechanizm i idea stojąca za ORM.

W EF Core trzeba to jawnie włączyć.

0

Wracając do motywu dlaczego chcę mapować sobie model w obie strony to ciężko korzystać z modelu ORM jako domenowego skoro by design może być niepoprawny (niezgodny z rzeczywistością), bo ktoś gdzieś w innym miejscu nie zrobił Include. Czuję się bezpieczniej jak EF nie zarządza moimi obiektami.

3
Saalin napisał(a):

Wracając do motywu dlaczego chcę mapować sobie model w obie strony to ciężko korzystać z modelu ORM jako domenowego skoro by design może być niepoprawny (niezgodny z rzeczywistością), bo ktoś gdzieś w innym miejscu nie zrobił Include. Czuję się bezpieczniej jak EF nie zarządza moimi obiektami.

Dlatego to nie model ORMa ma być modelem domeny, tylko model domeny mapuje się na bazę. Aplikację projektujemy od domeny, nie od bazy.

0
somekind napisał(a):

Customer ma Address, Address ma Country, a kolekcja Country sobie siedzi skeszowana w słowniku w pamięci.

No słabe to, ale jakoś przeboleje, że będę musiał zrobić address.CountryId=... zamiast address.Country=.... W DDD to i tak pewnie druga wersja by nie miała racji bytu.

Zobacz sobie jaki SQL wypluwa EF Core po użyciu OwnsMany, a potem pokaż @WeiXiao, żeby zobaczył jak wyglądają joiny robione pod spodem, żeby następnym razem nie wpadał na dziwne insynuacje pod adresem NH.

Naprawili w EF Core 5? Naprawili. :P

1

@somekind:

Jaki znowu kwiatek?! Lazy loading to podstawowy mechanizm i idea stojąca za ORM.

Dla mnie lazy loading to równie dobrze mógłby zostać wyrzucony z EFa i bym nie zauważył różnicy.

0
nobody01 napisał(a):

No słabe to, ale jakoś przeboleje, że będę musiał zrobić address.CountryId=... zamiast address.Country=.... W DDD to i tak pewnie druga wersja by nie miała racji bytu.

Dlaczego niby używanie obiektów w DDD nie ma racji bytu?

Naprawili w EF Core 5? Naprawili. :P

No najwyraźniej nie. Masz albo zachłanne załadowanie wszystkiego, albo niedziałającą logikę.

WeiXiao napisał(a):

Dla mnie lazy loading to równie dobrze mógłby zostać wyrzucony z EFa i bym nie zauważył różnicy, ja tam wole mięć kontrolę nad wydajnością :D

Raczej ciężko wyrzucić coś, czego nie ma. Ale to nawet logiczne, że skoro nie używasz narzędzi wyposażonych w dane mechanizmy, to ich nie znasz i uważasz za zbędne.

0
somekind napisał(a):

Dlaczego niby używanie obiektów w DDD nie ma racji bytu?

No jakby Country było agregatem, to inne encje miałyby jedynie jego ID. A jeśli byłoby value objectem, to nie masz problemu, bo value objectów nie trzymasz jako DbSety, tylko owned types.

No najwyraźniej nie. Masz albo zachłanne załadowanie wszystkiego, albo niedziałającą logikę.

Faktycznie nie da się mieć lazy loadingu i porządnego modelu domenowego jednocześnie w EF Core, ale czy w NHibernate jest inaczej? A jak oddzielisz domenę od bazy danych, to w ogóle nie będziesz mieć lazy loadingu niezależnie od ORMa.

1
nobody01 napisał(a):

No jakby Country było agregatem, to inne encje miałyby jedynie jego ID. A jeśli byłoby value objectem, to nie masz problemu, bo value objectów nie trzymasz jako DbSety, tylko owned types.

Country jest po prostu encją i tyle.

Faktycznie nie da się mieć lazy loadingu i porządnego modelu domenowego jednocześnie w EF Core, ale czy w NHibernate jest inaczej?

Myślę, że tak.

A jak oddzielisz domenę od bazy danych, to w ogóle nie będziesz mieć lazy loadingu niezależnie od ORMa.

Owszem. Pytanie, czy to ma sens.

0
somekind napisał(a):

Country jest po prostu encją i tyle.

W którym agregacie? :D

Myślę, że tak.

No niby tu wypada dużo lepiej: https://enterprisecraftsmanship.com/posts/ef-core-vs-nhibernate-ddd-perspective/, ale ten artykuł nie uwzględnia zmian w ef core 5.

5

Odpowiadając najprościej i najkrócej na podstawie mojej wiedzy.

Repo nie ma NIC WSPÓLNEGO Z BAZA. Fowler EAA
https://martinfowler.com/eaaCatalog/repository.html
Nie powinno być wiec mylone z baza, to tak dla przypomnienia :)

Z zasady SOC (separetion of concerns) jest prosto powiedziane. Stwórz interfejs który będzie definiował, na jakie operacje biznes pozwala, w celu utwardzenia danych lub ich agregowania do widoku (może być soap, równie dobrze jak mongoDB czy SQL)

Implementacja tego może być przez generyki, w oddzielnym module (np jakieś abstrakcyjne klasy dla modułu, które będą tworzyły na podstawie tego generyki) unikasz takich kwiatków jak pobieranie całej bazy danych czy rzucanie wyjątków, z racji na brak zaimplementowanej metody.

Jak bedziesz miał w interfejsie getMyBrothersSisterByIdMyDogInvoiceAndLast

Pomysl nad rozdziałem czegoś co Evans nazywał BoundedContextem, a Robert Martin, dobrym rysowaniem granic.

0

@somekind:

somekind napisał(a):

@kalimata: A co Ci da to, że zmienisz nazwę takiej klasy z DAO czy tam repozytorium na serwis? Absolutnie nic, a nawet mniej niż nic, bo serwis to to nie będzie.

Czym zatem charakteryzować się powinna klasa której nazwie można nadać przyrostek Service? Bo rozumiem ze jak mam u siebie taki kod to nie moge go nazwac serwisem:

public interface IUserService
{
    Task<User> GetUserByEmail(string email);
    Task<IEnumerable<User>> GetBlockedUsers();

    Task<bool> CreateUser(User user);

    //...
}
public class UserService : IUserService
{
    public async Task<User> GetUserByEmail(string email)
    {
        return await _context.Users.FirstOrDefaultAsynck(x => x.Email == email);
    }

    public async Task<IEnumerable<User>> GetBlockedUsers()
    {
        return await _context.Users.Where(x => x.Blocked == true).ToListAsync();
    }

    public async Task<bool> CreateUser(User user)
    {
        var userExists = await _context.Users.FirstOrDefaultAsync(x => x.Name = user.name && x.Email == user.Email);
        if (userExists != null) return false;

        _context.Users.Add(user);
        var created = _context.SaveChangesAsync();
        return created > 0;
    }

    //...
}
0
kalimata napisał(a):

Czym zatem charakteryzować się powinna klasa której nazwie można nadać przyrostek Service?

Tak szczerze, to najlepiej tej nazwy unikać, bo jest bardzo pojemna i mało precyzyjna (tak jak Manager). Dawniej typowo nazywało się tak klasę, która zbiera implementację różnych przypadków użycia. Teraz czasami klasę, która robi coś na potrzeby innej klasy (albo wielu klas) - jak sama nazwa mówi, ma służyć.

Bo rozumiem ze jak mam u siebie taki kod to nie moge go nazwac serwisem:

No mi to bardziej na jakieś DAO wygląda. Ale to też zależy skąd to jest wołane - jeśli z kontrolera, to może być serwisem, jeśli z innego serwisu, to raczej DAO.

0

@somekind: a UserDataProvider nie byłoby tu lepsze?

0

Może by było, gdyby służyło tylko do odczytu danych, a nie do ich tworzenia.

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