Repozytorium z Dapperem i Entity Frameworkiem

0

Tworzę sobie aplikację pisaną w CQRS. Jednym z użytych wzorców jest Repozytorium. Do pobierania danych (głównie query) chcę używać Dappera, a do command głównie EF. Pomysł wzięty z tego repozytorium: https://github.com/kgrzybek/modular-monolith-with-ddd. Już zacząłem coś tam pisać i wychodzi mi takie coś:

public class AlgorithmRepository : BaseRepository<Algorithm>, IAlgorithmRepository
{
    private IDatabaseConnection _connection;

    public AlgorithmRepository(IThinningDbContext thinningDbContext, IDatabaseConnection connection) 
        : base(thinningDbContext)
    {
        _connection = connection;
    }

    public async Task AddAlgorithmAsync(string algorithmName)
    {
        await _thinningDbContext.Algorithms.AddAsync(new Algorithm { Name = algorithmName });
    }

    public async Task<IEnumerable<Algorithm>> GetAlgorithmsByNameAsync(IEnumerable<string> names)
    {
        var parameters = new DynamicParameters();
        parameters.Add("@names", names);

        var connection = _connection.GetOpenConnection();   
        return await connection.QueryAsync<Algorithm>("GetAlgorithmsByName", parameters, commandType: CommandType.StoredProcedure);
    }
 }

Czy spotkaliście się z podejściem używania np insertów/deletów/updatów w EF, a selectów w Dapperze? Różnica w czasach między EF a procedurami przy insertach jest marginalna, zaś Dapper przy dobrze napisanych procedurach z selectami wygrywa. Czy ładowanie wszystkiego do jednego repozytorium (EF i procedury) wygląda dobrze?

0

Której wersji EF używasz? Na twoim miejscu sprawdziłbym najpierw jak sprawuje się EF z wyłączonym trackerem w porównaniu z Dapperem ponieważ różnica może być o wiele mniejsza niż się wydaje i nie warta komplikowania dostępu do danych

https://medium.com/@engr.mmohsin/entity-framework-core-2-dapper-performance-benchmark-c29e8cce9e1b

0

EF Core 3.1. Dapper to popularne narzędzie i chciałem po prostu podciągnąć trochę SQLa. Zastanawia mnie jak elegancko w repozytorium połączyć te dwa sposoby pracy z bazą. Ja wiem, że w moim projekcie nie poczuje różnicy między EF a Dapperem ale chciałem spróbować tego podejścia (też dla treningu). Podobno przy "słabych bazach" nHibernate, EF sprawuje się bardzo słabo i trzeba strzelać do procedur

1

A czemu po prostu nie wywołać procedury składowanej z EF?

I nie wierz w żadne "podobno" tylko zmierz.

0

Coś innego niż "select gwiazdka". Ten sam EF Core 2 co w linku z medium:

https://exceptionnotfound.net/dapper-vs-entity-framework-core-query-performance-benchmarking-2019/

Ale nie jest to wątek o wyższości jednego nad drugim. Chce sobie popisać sqle i poużywać Dappera. To mój projekt i chce napisać najlepiej jak się da to, co wybrałem za Core aplikacji. Jeśli człowiek z prawie 2k gwiazdkami na githubie porponuje takie rozwiązanie to domyślam się, że ktoś je wykorzystuje. W mojej poprzedniej pracy również (podobno) EF totalnie się nie sprawdził przez złożoność bazy (niby był znacznie, nieakceptowalnie wolniejszy).

Tak więc ponowię pytanie, może ktoś używał w jednym projekcie EF i Dappera. Czy moja klasa repozytorium wygląda w porządku, czy może jest jakieś bardziej fancy rozdzielenie tego?

0

Tu chyba raczej chodzi o to, że agregate rooty nie mogą mieć referencji do siebie, więc czasami jeśli chce się pobrać odpowiednie dane, to trzeba używać Join z EF Core, a to przy trzech tabelkach już wygląda brzydko: https://stackoverflow.com/a/28048939

Do tego są pewne ograniczenia dotyczące owned types, np. You cannot create a DbSet<T> for an owned type, a u Kamila tych owned types jest dużo.

1

@Michał Szewczak: to, jakiego sposobu dostępu do danych używasz wewnątrz repozytorium nie ma generalnie żadnego znaczenia, nie ma żadnych zasad mówiących jak to należy robić, bo to tylko szczegół implementacyjny, a w repozytoriach chodzi o API jakie wystawiają, nie o to, co mają wewnątrz.

Jeśli widzisz jakiś zysk z wymieszania tam EF, Dappera i procedur składowanych to tak zrób. Jak dla mnie pozbycie się ORMa z zapytań w tej konkretnej kwestii ma same wady, bo koszt dodatkowego utrzymania kodu SQL jest znacznie większy niż kodu korzystającego z ORM. Generalnie w praktyce, to wydajność w przypadku repozytoriów nie jest dużym realnym problemem, bo repozytoriów generalnie nie używa się tam, gdzie jest potrzebne wydajne operowanie na danych.

No i nie rozumiem, czemu chcesz tych procedur składowanych używać koniecznie przez Dappera, a nie przez EF.

@nobody01: ale czemu repozytorium miałoby pobierać dane spoza swojego aggregate roota?

0

@somekind Ja mialem na mysli uzywanie bezposrednio dbcontextu do odczytu danych. Do zapisu repozytoria, do odczytu context.

0
Michał Szewczak napisał(a):

Coś innego niż "select gwiazdka". Ten sam EF Core 2 co w linku z medium:

https://exceptionnotfound.net/dapper-vs-entity-framework-core-query-performance-benchmarking-2019/

A przeczytałeś to?

"Indeed, when I ran the test again using .First() instead of .Find(), the results showed that .First() performed markedly better, on the order of 3-5 times quicker than the calls using .Find()."

Ciekawe czy odpalił ten benchmark w trybie release :D

Tak, to nie jest wątek o wyższości jednego nad drugim.
To że ktoś ma 2 tysiące gwiazdek na githubie to nic dla mnie nie znaczy, osobiście to zanim pójdę czyjąś ścieżką to się zastanowię nad tym jakie to ma przełożenie na utrzymanie tego właśnie projektu. Chcesz popisać SQL - spoko, jak najbardziej. Tyle że dokładnie to samo możesz zrobić używając już tego ORM-a którego używasz w innych miejscach. Mieszanie technologii w projekcie prowadzi zazwyczaj do chaosu, ale do tego pewnie sam dojdziesz w przyszłości.

Zakładając że będziesz używać wyłącznie Dappera do odczytu:

  1. dlaczego instancja connection nie ląduje w BaseRepository? Zakładam, że nie tylko ta klasa będzie korzystać z Dappera.
  2. osobiście robiąc taki podział to całkowicie oddzieliłbym zapis od odczytu, nawet na poziomie tych repozytoriów. Tylko i wyłącznie po to, żeby ścieżka do odczytu nie wiedziała zupełnie nic o Dapperze, bo w końcu przyjdzie chwila kiedy znudzony klepaniem procedur i ciągłego dokładania parametrów walniesz sobie _context.Algorithms.FirstOrDefault(x=> ...) i cały zmyślny plan legnie w gruzach.
0

@Aventus: Ciekaw jestem, jak to u Was wyglądało w projekcie. Używaliście Dappera, czy EF Core wystarczył i do zapisu, i do odczytu? Korzystaliście po prostu z Query Types: https://www.learnentityframeworkcore.com/query-types? Pytam, bo jesteś chyba jednym z niewielu użytkowników na forum, którzy używali DDD w pracy.

1

@nobody01: mieliśmy system oparty o event sourcing tak więc Dapper w zupełności wystarczył, jako że do SQL zapisywaliśmy jedynie read modele. Co za tym idzie nie było potrzeby na utrzymywanie relacji między tabelami itp. W pewnym sensie odwrócił bym treść Twojego pytania: Używaliście EF Core, czy Dapper wystarczył i do zapisu, i do odczytu? jako że Dapper nie ułatwia utrzymywania relacji. To scenariusz w którym właśnie bardziej rozbudowane ORMy takie jak EF Core udowadniają swoją przydatność. Poza oczywistym faktem że nie trzeba samemu pisać SQL.

0

Jakby kogoś ciekawiło co zrobiłem, zrezygnowałem z Repozytorium w przypadku EF. Do serwisów będę podawał klasę context i operował na niej. Repozytoria będą służyły tylko do selectów z Dappera. Dzięki za pomoc

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