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

0
KamilAdam napisał(a):
Aleksander32 napisał(a):

Repozytorium operuje na obiektach domenowych, nie zaś obiektach z ORM, to jest różnica ;)

A widziałeś żeby ktoś przepisywał obiekty z ORM na obiekty domenowe i w drugą strone? No bo jak np przepiszesz w Hibernate (NHibernate pewnie podobnie) to tracisz co najmniej połowę zalet ORMa czyli śledzenie zmian

A to główna zaletą ORM nie jest mapowanie relacji na obiekty? Bo to mapowanie obiektów domenowych na bazodanowe i odwrotnie to widzę praktycznie w każdej aplikacji.

1

Hmm, to co te obiekty domenowe robią, że nie da się ich zmapować np. przez ef core? Tu jest dość złożony przykład imo i działa: https://github.com/kgrzybek/m[...]gs/Domain/Meetings/Meeting.cs

Trzeba się trochę nakonfigurować, ale to chyba i tak lepsze, niż utrzymywanie dwóch warstw obiektów: https://github.com/kgrzybek/m[...]ingEntityTypeConfiguration.cs

1

To czy można zmapowac to jedno, a czy się powinno to drugie. Jak już prześcigamy się w złych pomysłach to już wole generyczne repozytoria.

1

@Saalin: A dlaczego takie mapowanie uważasz za złe?

1

Przecież mapujesz co chcesz i jak chcesz. Po co trzymać jeszcze osobnę libkę z jakimiś POCOsami.
Chyba że komuś płaca za LOC

3

@nobody01: polecam poczytać o clean architecture i Dependency Inversion Principle. Jak masz aplikację biznesowa która nie jest CRUDem to model danych (zapisu) powinien zależeć od encji domenowych i logiki biznesowej a nie odwrotnie! To czy korzystasz z bazy dnaych SQL, NoSQL, Ramu, plików, Resta, kolejek itp nie powinno miec znaczenia dla logiki biznesowej...

A widziałeś żeby ktoś przepisywał obiekty z ORM na obiekty domenowe i w drugą stronę?

Widziałem

1

@Aleksander32: czytałem :) pokaż te encje, której wg ciebie ef core nie da rady zmapowac :) od razu uprzedzam że z prywatnymi polami, niemutowalnymi kolekcjami i value objectami nie będzie problemów. I to bez żadnych atrybutów.

1

@kalimata: tak, tzw generyczne repozytorium ma sens jeśli wiesz co czego może posłużyć, kiedy i jak używać.
To nie powinna być domyślna warstwa DAO ale czasami jest przydatne.
Przymusu używania nie ma. Np. Jak masz dużo małych tabel slownikowych.

2
kalimata napisał(a):

Czy to ma jakiś sens w praktycznym programowaniu? Bo dla mnie to istnieje tu tylko sens akademicki, pomimo ze autorzy kursu twierdzą ze pokazują przykład praktycznego zastosowania w aplikacji.

No praktyczne programowanie polega na tym, że każdy programista musi co najmniej raz w tygodniu napisać bezużyteczny wrapper, i generyczne repozytorium pomaga to osiągnąć. ;)
Ale nie jest to ani pragmatyczne, ani sensowne, no chyba, że nie masz ORMa ani żadnego gotowego dostępu do miejsca przechowywania danych, to wtedy takie bazowe DAO będzie miało sens (aczkolwiek nie ma najmniejszego powodu wyzywać porządnego DAO od repozytoriów).

Jeśli miałbym tego uzywać to sądzę ze byłoby tam co najmniej kilka metod Get z różnyumi parametrami.

No, tylko wtedy nie będzie to już generyczne.

kalimata napisał(a):

i w jakiejś implementacji tego interfejsu

Jak to w "jakiejś"? A będzie więcej niż jedna? Po co w ogóle taki interfejs?
Ten kod jest w moim odczuciu dość nienaturalny, ten Twój serwis to tak naprawdę jakiś wrapper na ORMa. Ja w miejscu, w którym mam logikę aplikacji (czyli serwisie aplikacyjnym bądź mediatorowym handlerze) użył dbContextu i zrobił, co potrzebuję.

KamilAdam napisał(a):

A widziałeś żeby ktoś przepisywał obiekty z ORM na obiekty domenowe i w drugą strone? No bo jak np przepiszesz w Hibernate (NHibernate pewnie podobnie) to tracisz co najmniej połowę zalet ORMa czyli śledzenie zmian

Ale przecież nie trzeba mapować, bo obiekty domenowe mogą być używane przez ORMa. Ale aby to było możliwe, to domena musi spełniać warunek bycia persistence ignorance, a ORM nie może mieć żadnych wymagań co do encji, które mapuje. A nie każdy ORM na to pozwala.
Problem jest wtedy, gdy obiekty z ORMa są używane jako domenowe, czyli de facto model biznesowy operuje na strukturze bazy danych.

Saalin napisał(a):

A to główna zaletą ORM nie jest mapowanie relacji na obiekty? Bo to mapowanie obiektów domenowych na bazodanowe i odwrotnie to widzę praktycznie w każdej aplikacji.

A w ilu przypadkach wynika to z czegoś innego niż "każda warstwa operacyjna musi mieć swoją warstwę struktur danych" albo "mamy słabego ORMa"? Bo tylko te pozostałe mogą być sensowne. :)

urke napisał(a):

Przecież mapujesz co chcesz i jak chcesz. Po co trzymać jeszcze osobnę libkę z jakimiś POCOsami.
Chyba że komuś płaca za LOC

Tyle, że to nie zawsze są POCO, bo np. technologia składowania wymaga, aby to nie były POCO. To jeden przypadek, dla którego oddzielenie persistence modeli ma sens.
Drugim mogą być znaczące różnice między logiką biznesową, a potrzebami składowania. Jeśli te modele różnią się znacząco, to jest sens mieć oddzielną libkę.

W tej kwestii bardzo unikałbym twierdzenia, że któreś z tych dwóch podejść jest zawsze złe, albo zawsze dobre. To jest mocno zależne od konkretnej sytuacji.

nobody01 napisał(a):

@Aleksander32: czytałem :) pokaż te encje, której wg ciebie ef core nie da rady zmapowac :) od razu uprzedzam że z prywatnymi polami, niemutowalnymi kolekcjami i value objectami nie będzie problemów. I to bez żadnych atrybutów.

Ta nadzieja w to, że Microsoft kiedyś stworzy działającego ORMa przypomina mi te kobiety z podbitymi oczami, które wybaczają, bo "łobuz kocha najbardziej".

using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace EntityFrejmłork
{
    public class Wreszcie
    {
        [Fact]
        public void UmieMapowaćPola()
        {
            string entryTitle = Guid.NewGuid().ToString();

            using (var context = new AppDbContext())
            {
                var entry = new Entry { Title = entryTitle };
                context.Entries.Add(entry);

                entry.AddTag("Microsoft");
                entry.AddTag("nie");
                entry.AddTag("umie");

                context.SaveChanges();
            }

            using (var context = new AppDbContext())
            {
                var entry = context.Entries.Single(x => x.Title == entryTitle);

                entry.AddTag("w");
                entry.AddTag("Ormy");

                context.Invoking(c => c.SaveChanges()).Should().Throw<InvalidOperationException>();
            }
        }

        [Fact]
        public void RadziSobieZOdłączonymObiektem()
        {
            Info detached;
            using (var context = new AppDbContext())
            {
                detached = context.Infos.Find(2);
            }

            using (var context = new AppDbContext())
            {
                var entry = context.Entries.Find(3);
                entry.Info = detached;
                context.Invoking(c => c.SaveChanges()).Should().NotThrow();
            }
        }
    }

    public class AppDbContext : DbContext
    {
        public DbSet<Entry> Entries { get; set; }
        public DbSet<Tag> Tags { get; set; }
        public DbSet<Info> Infos { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=EfSsie;Trusted_Connection=True;MultipleActiveResultSets=true");
        }
    }

    public class Entry
    {
        private readonly ICollection<Tag> _tags = new List<Tag>();
        public virtual IReadOnlyList<Tag> Tags => _tags.ToList();
        public virtual Info Info { get; set; }

        public int Id { get; set; }
        public string Title { get; set; }

        public void AddTag(string name)
        {
            if (_tags.Count > 3) // Ważna logika biznesowa
                throw new InvalidOperationException();

            _tags.Add(new Tag { Name = name, Entry = this }); ;
        }
    }

    public class Tag
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Entry Entry { get; set; }
    }

    public class Info
    {
        public int Id { get; set; }
        public string Content { get; set; }
        public Entry Entry { get; set; }
        public int EntryId { get; set; }    // w TVP mówili, że już niepotrzebne
    }
}

screenshot-20210220034234.png

3

@somekind:
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? Jak _tags są częścią agregatu Entry, to powinny być owned types, żeby ef core je zawsze zaciągał. No chyba, że masz gdzieś jeszcze włączony lazy loading, ale nie widzę go tutaj (samo virtual nie sprawi, że lazy loading zadziała). https://www.learnentityframeworkcore.com/lazy-loading
Test 2) Potrzebne była Ci kiedykolwiek taka funkcjonalność? Zazwyczaj chyba używa się instancji db contextu per request (scoped).

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