Synchronizacja ObservableCollection z IQueryable (wynik zapytania Entity Framework)

0

Witam

Posiadam TreeView, który jest ładnie podłączony z kolekcją ObservableCollection<MyElement>();

Jeśli dodam coś do tej kolekcji kodem:

MyElement newMyElement= new MyElement()
            {
                Name = "Nazwa"
            };
Folders.Add(newMyElement);

to dodaje natychmiastowo oraz nie zamyka otwartych TreeItemów, ani nie odznacza zaznaczonych. Zachowuje się IDEALNIE.

Problem jest w momencie spięcia z wynikami z bazy.

Tak inicjuje to spięcie:


db = new DbFileContext("plik.baza", true);

var filtered = db.MyElements.Include(p => p.Folders).Where(p => p.ParentId == null);
Folders = new ObservableCollection<MyElement>(filtered);

No i dane się ładują. Problem pojawia się w momencie zinsertowania czegoś do bazy. Automatycznie nic się nie odświeża, a gdy zrobię taką operację:

var filtered = db.MyElements.Include(p => p.Folders).Where(p => p.ParentId == null);
Folders = new ObservableCollection<MyElement>(filtered);

to odświeża TreeView, ale go całego resetuje.

Podejrzewam, ze sytuacja wynika z faktu, że te nowe obiekty które są zwracane przy takim wydaniu zapytania do bazy mimo że są w większości takie same, w sensie obiektu są nowe. Wystarczyłoby napisać jakiś synchronizator. Na zasadzie:

  1. Co nowe to dodaj
  2. Czego nie ma to usuń
  3. Co zmodyfikowane zmodyfikuj pole po polu
  4. Skanuj listę dzieci i zrób powyższe operacje poczynając od pierworodnego potomka skończywszy na najmłodszym dziecku

Jednak czy M$ nie oferuje jakiegoś gotowca? Aż mi się wierzyć nie chce.

Pozdrawiam

0

Obiekty są nowe bo inicjujesz na nowo za pomocą słówka new kolekcję.
Jeśli insertujesz coś do bazy to jest to logiczne, że nie będzie się to pojawiało w aplikacji. Przecież robisz to do bazy a nie aplikacji.
Po prostu jeśli coś dodajesz do bazy to dodawaj od razu do kolekcji Folders. Jeśli coś usuwasz z bazy to usuwaj też z kolekcji itd.

Nie ma sensu za każdym razem wyciągać wszystkiego z bazy. Wręcz przeciwne byłoby to idiotyczne i kompletnie nieefektywne.

0
UnlimitedPL napisał(a):

Obiekty są nowe bo inicjujesz na nowo za pomocą słówka new kolekcję.
Jeśli insertujesz coś do bazy to jest to logiczne, że nie będzie się to pojawiało w aplikacji. Przecież robisz to do bazy a nie aplikacji.
Po prostu jeśli coś dodajesz do bazy to dodawaj od razu do kolekcji Folders. Jeśli coś usuwasz z bazy to usuwaj też z kolekcji itd.

Nie ma sensu za każdym razem wyciągać wszystkiego z bazy. Wręcz przeciwne byłoby to idiotyczne i kompletnie nieefektywne.

Z punktu widzenia wydajności: szczególnie dla dużych drzew rzeczywiście masz rację.

Jesteś jednak pewien, że zawsze tego typu operacje realizuje się poprzez dodawanie najpierw do bazy a potem do kolekcji? Nie ma tu żadnego automatu, w którym dodam tylko raz?

0

Zawsze możesz sobie napisać jakąś klasę i (lub) metodę obsługującą te 2 rzeczy na raz i potem tylko raz wywołasz metodę i nie będzie Cię obchodziło nic bo samo wszystko się zrobi. To Ty masz sobie to zaprogramować, a nie czekać na gotowe rozwiązania :)

0

Ok byłam pewna że musi być jakiś Collection Synchronizator :P

0

Ja bym pozwolił najpierw wprowadzić wszystkie zmiany w kolekcji, a następnie zapisał ją całą do bazy. Będzie wydajnie zarówno po stronie aplikacji, jak i bazy.

0
somekind napisał(a):

Ja bym pozwolił najpierw wprowadzić wszystkie zmiany w kolekcji, a następnie zapisał ją całą do bazy. Będzie wydajnie zarówno po stronie aplikacji, jak i bazy.

Jakim kodem można to zrobić? Bo chyba nie ma sensu robić delete all a potem foreach-em insertować po kolei elementy tym bardziej, że docelowo to drzewo będzie powiązane z innymi elementami tablicy (i będą FOREIGN KEY-e).

0
somekind napisał(a):

Ja bym pozwolił najpierw wprowadzić wszystkie zmiany w kolekcji, a następnie zapisał ją całą do bazy. Będzie wydajnie zarówno po stronie aplikacji, jak i bazy.

jesteś starszy stażem więc niepewnie piszę iż chyba lepszym rozwiązaniem jest najpierw spróbować zapisać do bazy i jeśli operacja powiodła się dopiero wtedy aktualizować drzewko.
Dlaczego? Jak zaktualizujemy drzewko a potem bazę (i tu błąd z bazy) to jesteśmy znów zmuszeni zmieniać drzewko drugi raz bo będzie niespójność.

0
UnlimitedPL napisał(a):
somekind napisał(a):

Ja bym pozwolił najpierw wprowadzić wszystkie zmiany w kolekcji, a następnie zapisał ją całą do bazy. Będzie wydajnie zarówno po stronie aplikacji, jak i bazy.

jesteś starszy stażem więc niepewnie piszę iż chyba lepszym rozwiązaniem jest najpierw spróbować zapisać do bazy i jeśli operacja powiodła się dopiero wtedy aktualizować drzewko.
Dlaczego? Jak zaktualizujemy drzewko a potem bazę (i tu błąd z bazy) to jesteśmy znów zmuszeni zmieniać drzewko drugi raz bo będzie niespójność.

Tu 100% zgoda, że najpierw operacja na bazie bo jeśli wystąpi jakiś exception to nie aktualizować drzewka.

Druga sprawa:

W czym lepsze jest aktualizowanie całego drzewa w bazie od aktualizowania całego drzewa w GUI? Aktualizowanie całego drzewa w GUI zostało przez UnlimitedPL nazwane nieefektywnym i idiotycznym. A nie jest idiotycznym aktualizowanie całego drzewa?

Pobieranie całego drzewa z bazy + jego pokazywanie w GUI daje także 100% pewność że użytkownik ma takie drzewo w bazie jakie widzi w GUI. Natomiast synchronizacja w drugą stronę takiej pewności nie daje.

Aha dodam, że pobieranie drzewa można by w przyszłości zmodyfikować o funkcję pobierania i aktualizacji jedynie wybranego fragmentu drzewa. I to rozwiązanie byłoby optymalizacyjne chyba najlepsze.

0
quechua napisał(a):

Jakim kodem można to zrobić? Bo chyba nie ma sensu robić delete all a potem foreach-em insertować po kolei elementy tym bardziej, że docelowo to drzewo będzie powiązane z innymi elementami tablicy (i będą FOREIGN KEY-e).

Owszem, nie ma sensu. Od tego są ORMy. W ramach jednej sesji wszystkie modyfikacje, dodanie nowych elementów i usunięcie starych, po zawołaniu commita do bazy, powinny zostać zamienione na odpowiedni SQL i wykonane w ramach jednego batcha do bazy.

UnlimitedPL napisał(a):

jesteś starszy stażem więc niepewnie piszę iż chyba lepszym rozwiązaniem jest najpierw spróbować zapisać do bazy i jeśli operacja powiodła się dopiero wtedy aktualizować drzewko.
Dlaczego? Jak zaktualizujemy drzewko a potem bazę (i tu błąd z bazy) to jesteśmy znów zmuszeni zmieniać drzewko drugi raz bo będzie niespójność.

Oczywiście masz rację, kwestia tego, co właściwie chcemy osiągnąć:

  1. Czy zmianę najpierw ma zobaczyć użytkownik, a gdy zobaczy, że mu pasuje, to zatwierdza i zmiany dopiero wtedy zostają zapisane do bazy?
  2. Czy inni użytkownicy mogą aktualizować tę tabelę w tym samym czasie?
  3. Czy jest to często zmieniająca się struktura?
  4. Czy te dane są jakoś cachowane po stronie aplikacji?

Generalnie kombinowanie z zapisywaniem do bazy, a następnie dodawaniem do kolekcji pojedynczego elementu, to mikropseudooptymalizacja. Zazwyczaj najlepiej sprawdzają się proste i stabilne rozwiązania, czyli zapis zmian -> odczyt całości -> wyświetlenie na nowo. Optymalizować warto jeśli czas przeładowania jest nieakceptowalny dla użytkowników.

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