Zarządzanie pamięcią modeli z cyklicznymi zależnościami

Zarządzanie pamięcią modeli z cyklicznymi zależnościami
bbhzp
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 92
0

Cześć,
uczę się EfCore i jestem na temacie tworzenia relacji. Przeglądałem dokumentację MS (https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many) i dostosowując trzeci przykład, stworzyłem dwie klasy:

Kopiuj
public class User
{
    ...

    public int Id { get => _id; set => _id = value; }
    public string FirstName { get => _firstName; set => _firstName = value; }
    public string LastName { get => _lastName; set => _lastName = value; }
    public List<Privilege> Privileges { get => _privileges; set => _privileges = value; }
}

public class Privilege
{
    ...

    public int Id { get => _id; set => _id = value; }
    public string Name { get => _name; set => _name = value; }
    public List<User> Users { get => _users; set { _users = value; } }
}

I mam pytanie - jak EF zarządza pamięcią w takim przypadku?

Pobrałem użytkownika za pomocą:

Kopiuj
User u = context.Users
                .Include(x => x.Privileges)
                .First();

Gdy podejrzę go w debuggerze, tworzy się pętla instancji:

screenshot-20240713134746.png
Chodzi mi o to, że użytkownik ma listę uprawnień, a każde uprawnienie ma listę użytkowników. Problem w tym, że nie jest ona pusta, a po rozwinięciu pokazuje się ten sam użytkownik.

Jest to najbardziej widoczne podczas serializacji danych, bo wywołanie:

Kopiuj
JsonSerializer.Serialize(user);

Wyrzuca wyjątek:

Kopiuj
System.Text.Json.JsonException: „A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. Path: $.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privileges.Users.Privi

Pomaga jedynie dodanie atrybutu [JsonIgnore] przed właściwością Privileges.

Czy ma to jakiś wpływ na wydajność aplikacji lub zużycie pamięci?

Z góry dziękuję za wszystkie odpowiedzi

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10230
1

Cześć! 😊 Fajnie że zadałeś pytanie ma forum! 👋

bbhzp napisał(a):

Czy ma to jakiś wpływ na wydajność aplikacji lub zużycie pamięci?

Nie powinno mieć żadnego wpływu, i nie specjalnie ma też związek z EF.

To co widzisz to zależność cykliczna (ang. "circular dependency"). Można to bardzo łatwo zaobrazować:

Kopiuj
class Item {
  public Other other;
}

class Other {
  public Item item;
}

var item = new Item();
var other = new Other();

other.item = item;
item.other = other;

Serializacja to proces reprezentacji obiektów z pamięci w jakieś formie którą można zapisać na dysku lub przesłać, najczęściej tekstowej lub binarnej. Większość formatów nie wspiera zagnieżdżonych referencji, więc dostajemy wtedy błąd serializacji.

Natomiast co do Twojego problemu z serializacją modeli EF - nie jestem do końca pewien czy to dobry pomysł, zamiast serializować modele mógłbyś po prostu przekazać id, lub stworzyć odpowiednie DTO żeby przekazać dane.

Daj znać jeśli jeszcze czegoś potrzebujesz. Pozdrawiam 🤝

bbhzp
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 92
0
Riddle napisał(a):

Natomiast co do Twojego problemu z serializacją modeli EF - nie jestem do końca pewien czy to dobry pomysł, zamiast serializować modele mógłbyś po prostu przekazać id, lub stworzyć odpowiednie DTO żeby przekazać dane.

Dzięki za odpowiedź. Używam serializacji tylko po to, aby uzyskać głęboką kopię instancji (deep copy) : )

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10230
1
bbhzp napisał(a):

Dzięki za odpowiedź. Używam serializacji tylko po to, aby uzyskać głęboką kopię instancji (deep copy) : )

A po co Ci głęboka kopia instancji modelu EF? Wydaje mi się to bardzo złym pomysłem.

EF to abstrakcja na dostęp do bazy danych, nie służy do przesyłu danych ani czego takiego.

bbhzp
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 92
0
Riddle napisał(a):
bbhzp napisał(a):

Dzięki za odpowiedź. Używam serializacji tylko po to, aby uzyskać głęboką kopię instancji (deep copy) : )

A po co Ci głęboka kopia instancji modelu EF?

Dla edycji danych (User bindowany pod widok i UserBefore). W przypadku użytkownika sprawdzam np. czy podany login nie jest już zajęty. Pobieram wtedy count'a wszystkich userów z podanym loginem i jeśli nie jest on równy 0, a nowy login nie jest taki sam jak ten sprzed edycji, to wyrzucam błąd.

Drugim zastosowaniem jest historia zmian - sprawdzam, czy np. imię i nazwisko różni się między dwoma instancjami i jeśli tak, to dopisuję to do tabeli. Tak samo robię z listami (za pomocą Equals()) i do tego potrzebna jest mi głęboka kopia, bo wszystkie kolekcje są bindowane pod widoki w WPF.

Kopiuj
Jan Kowalski -> Zbigniew JSON
Cechy klienta (3) -> Cechy klienta (5)
Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10230
2
bbhzp napisał(a):

Dla edycji danych (User bindowany pod widok i UserBefore). W przypadku użytkownika sprawdzam np. czy podany login nie jest już zajęty. Pobieram wtedy count'a wszystkich userów z podanym loginem i jeśli nie jest on równy 0, a nowy login nie jest taki sam jak ten sprzed edycji, to wyrzucam błąd.

Drugim zastosowaniem jest historia zmian - sprawdzam, czy np. imię i nazwisko różni się między dwoma instancjami i jeśli tak, to dopisuję to do tabeli. Tak samo robię z listami (za pomocą Equals()) i do tego potrzebna jest mi głęboka kopia, bo wszystkie kolekcje są bindowane pod widoki w WPF.

Super że udało Ci się znaleźć rozwiązanie które działa! 🥳

Wziąłbym tylko pod uwagę to, że EFCore to jest narzędzie do obsługi baz danych. WPF to biblioteka do interfejsu użytkownika. Bindując modele EF do widoków WPF dodajesz ciasne powiązanie (ang. "tight-coupling"). Czasami to nie jest problem, ale w rozbudowanych aplikacjach może to sprawić że zmiana czegoś w jednym miejscu, wymusi wiele zmian w innych. Często dobrym pomysłem jest unikać takiego przywiązania.

Gdybym ja robiłbym taką aplikację, nie przekazywałbym modeli EF do widoku WPF, zrobiłbym pod to osobne klasy, które są zbliżone do tego co aplikacja robi, (a nie to w jaki sposób trzyma dane).

bbhzp
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 92
0
Riddle napisał(a):

Bindując modele EF do widoków WPF dodajesz ciasne powiązanie (ang. "tight-coupling"). Czasami to nie jest problem, ale w rozbudowanych aplikacjach może to sprawić że zmiana czegoś w jednym miejscu, wymusi wiele zmian w innych. Często dobrym pomysłem jest unikać takiego przywiązania.

Gdybym ja robiłbym taką aplikację, nie przekazywałbym modeli EF do widoku WPF, zrobiłbym pod to osobne klasy, które są zbliżone do tego co aplikacja robi, (a nie to w jaki sposób trzyma dane).

Dzięki za cenne uwagi : ). Ale w tym przypadku taka klasa Usera pod WPF wyglądałaby tak samo? Czy może nie miałaby pola ID?

... i jaka jest wtedy konwencja nazewnictwa : )?

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10230
1
bbhzp napisał(a):

Dzięki za cenne uwagi : ). Ale w tym przypadku taka klasa Usera pod WPF wyglądałaby tak samo? Czy może nie miałaby pola ID?

... i jaka jest wtedy konwencja nazewnictwa : )?

To zależy od Ciebie. Powinieneś ją zaprojektować tak, żeby używało się jej jak najwygodniej i miała potrzebne pola. Jeśli nic w widoku WPF nie potrzebuje id, to klasa nie musi go mieć, bo i po co.

bbhzp
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 92
0

Ostatnie pytanie : ) Jak nazywają się takie klasy? Bo gdy wpisuję w Google "WPF tight coupling" to pokazują mi się tylko wyniki dotyczące komend, a chciałbym zgłębić temat : )

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10230
1
bbhzp napisał(a):

Ostatnie pytanie : ) Jak nazywają się takie klasy? Bo gdy wpisuję w Google "WPF tight coupling" to pokazują mi się tylko wyniki dotyczące komend, a chciałbym zgłębić temat : )

Dobre wejście możesz zacząć od tego filmu:

youtu.be/o_TH-Y78tt4?si=2dB8qTDTwWoZ6FZ1&t=659

Jeśli chcesz zgłębić temat to google'aj:

  • dependency inversion
  • humble object
  • hexagonal architecture
  • clean architecture
  • onion architecture

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.