Posiadanie dwóch klas:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
Przy przypisywaniu ChildId
do Parent
powinienem najpierw sprawdzić, czy istnieje w bazie danych, czy poczekać, aż baza danych zgłosi wyjątek?
Na przykład (przy użyciu Entity Framework Core):
UWAGA: tego rodzaju kontrole są WSZYSTKIE W INTERNECIE, nawet w oficjalnych dokumentach Microsoftu: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using- mvc / handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application # zmodyfikuj kontroler departamentu, ale istnieje dodatkowa obsługa wyjątków dlaSaveChanges
należy również zauważyć, że głównym celem tego sprawdzenia było zwrócenie przyjaznego komunikatu i znanego statusu HTTP użytkownikowi interfejsu API, a nie całkowite zignorowanie wyjątków bazy danych. Jedyny wyjątek od miejsca, który zostanie zgłoszony, to wnętrze SaveChanges
lub SaveChangesAsync
połączenie ... więc nie będzie żadnego wyjątku podczas połączenia FindAsync
lub Any
. Jeśli więc dziecko istnieje, ale zostało usunięte wcześniej, SaveChangesAsync
zostanie zgłoszony wyjątek współbieżności.
Zrobiłem to, ponieważ foreign key violation
wyjątek będzie znacznie trudniejszy do sformatowania w celu wyświetlenia „Nie można znaleźć elementu podrzędnego o identyfikatorze {parent.ChildId}”.
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
// is this code redundant?
// NOTE: its probably better to use Any isntead of FindAsync because FindAsync selects *, and Any selects 1
var child = await _db.Children.FindAsync(parent.ChildId);
if (child == null)
return NotFound($"Child with id {parent.ChildId} could not be found.");
_db.Parents.Add(parent);
await _db.SaveChangesAsync();
return parent;
}
przeciw:
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
_db.Parents.Add(parent);
await _db.SaveChangesAsync(); // handle exception somewhere globally when child with the specified id doesn't exist...
return parent;
}
Drugi przykład w Postgres zgłosi 23503 foreign_key_violation
błąd: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
Wadą obsługi wyjątków w ten sposób w ORM, takich jak EF, jest to, że będzie działać tylko z określonym zapleczem bazy danych. Jeśli kiedykolwiek chciałeś przełączyć się na serwer SQL lub coś innego, nie będzie to już działać, ponieważ kod błędu się zmieni.
Niepoprawne sformatowanie wyjątku dla użytkownika końcowego może ujawnić pewne rzeczy, których nikt poza deweloperami nie powinien widzieć.
Związane z:
https://stackoverflow.com/questions/308905/should-there-be-a-transaction-for-read-queries
źródło
Child with id {parent.ChildId} could not be found.
. A formatowanie „Naruszenie klucza obcego” wydaje mi się gorsze w tym przypadku.Odpowiedzi:
Raczej niejasne pytanie, ale TAK , powinieneś najpierw sprawdzić, a nie tylko obsługiwać wyjątek DB.
Po pierwsze, w twoim przykładzie jesteś w warstwie danych, używając EF bezpośrednio w bazie danych, aby uruchomić SQL. Twój kod jest równoważny z uruchomieniem
Sugerowana przez ciebie alternatywa to:
Używanie wyjątku do wykonywania logiki warunkowej jest powolne i powszechnie się nie podoba.
Masz warunki wyścigu i powinieneś skorzystać z transakcji. Ale można to w pełni zrobić za pomocą kodu.
Najważniejsze jest, aby zadać sobie pytanie:
Jeśli nie, to na pewno włóż i wyrzuć wyjątek. Ale obsługuj wyjątek jak każdy inny błąd, który może wystąpić.
Jeśli spodziewasz się, że to nastąpi, NIE jest to wyjątkowe i powinieneś sprawdzić, czy dziecko istnieje jako pierwsze, odpowiadając odpowiednim przyjaznym komunikatem, jeśli tak nie jest.
Edytuj - Wydaje się, że istnieje wiele kontrowersji. Przed głosowaniem zastanów się:
A. Co jeśli byłyby dwa ograniczenia FK. Czy opowiedziałbyś się za parsowaniem komunikatu wyjątku, aby ustalić, którego obiektu brakuje?
B. W przypadku pominięcia uruchamiana jest tylko jedna instrukcja SQL. To tylko trafienia, które powodują dodatkowy koszt drugiego zapytania.
C. Zwykle Id byłby kluczem zastępczym. Trudno wyobrazić sobie sytuację, w której ją znasz i nie jesteś pewien, czy jest na DB. Sprawdzanie byłoby dziwne. Ale co, jeśli jest to naturalny klucz wpisany przez użytkownika? To może mieć dużą szansę na nieobecność
źródło
Sprawdzanie niepowtarzalności, a następnie ustawienie jest antypatternem; zawsze może się zdarzyć, że identyfikator zostanie wstawiony jednocześnie między czasem sprawdzania a czasem zapisu. Bazy danych są wyposażone w mechanizmy takie jak ograniczenia i transakcje; większość języków programowania nie jest. Dlatego jeśli cenisz spójność danych, pozostaw to ekspertowi (baza danych), tj. Wstaw wstawkę i wyłap wyjątek, jeśli wystąpi.
źródło
Myślę, że to, co nazywacie „szybko zawiedzie”, a to, co nazywam, nie jest takie samo.
Mówienie bazy danych, aby dokonać zmiany i obsługa awarii, to jest szybkie. Twoja droga jest skomplikowana, powolna i niezbyt niezawodna.
Ta twoja technika nie zawodzi szybko, to „preflight”. Są czasem dobre powody, ale nie w przypadku korzystania z bazy danych.
źródło
Zaczęło się od komentarza, ale stało się zbyt duże.
Nie, jak podają inne odpowiedzi, tego wzorca nie należy stosować. *
W przypadku systemów wykorzystujących komponenty asynchroniczne zawsze wystąpi wyścig, w którym baza danych (lub system plików lub inny system asynchroniczny) może zmieniać się między sprawdzaniem a zmianą. Kontrola tego typu po prostu nie jest niezawodnym sposobem zapobiegania typowi błędu, którego nie chcesz obsłużyć.
Co gorsza niż niewystarczająca, na pierwszy rzut oka sprawia wrażenie, że powinna zapobiec błędnemu zapisowi błędu, dając fałszywe poczucie bezpieczeństwa.
W każdym razie potrzebujesz obsługi błędów.
W komentarzach zapytałeś, co jeśli potrzebujesz danych z wielu źródeł.
Nadal nie.
Fundamentalna kwestia nie znika, jeśli to, co chcesz sprawdzić, staje się coraz bardziej skomplikowane.
Mimo to nadal potrzebujesz obsługi błędów.
Nawet jeśli ta kontrola byłaby niezawodnym sposobem uniknięcia określonego błędu, przed którym próbujesz się zabezpieczyć, nadal mogą wystąpić inne błędy. Co się stanie, jeśli stracisz połączenie z bazą danych lub zabraknie miejsca lub?
Prawdopodobnie nadal potrzebujesz innej obsługi błędów związanych z bazą danych. Obsługa tego konkretnego błędu powinna być prawdopodobnie jego niewielką częścią.
Jeśli potrzebujesz danych, aby określić, co zmienić, oczywiście będziesz musiał je skądś zebrać. (w zależności od narzędzi, których używasz, są prawdopodobnie lepsze sposoby niż oddzielne zapytania do ich gromadzenia) Jeśli podczas badania zebranych danych stwierdzisz, że nie musisz wprowadzać zmian, świetnie, nie rób zmiana. To ustalenie jest całkowicie odrębne od problemów związanych z obsługą błędów.
Mimo to nadal potrzebujesz obsługi błędów.
Wiem, że powtarzam się, ale uważam, że ważne jest, aby to wyjaśnić. Uprzątałem ten bałagan wcześniej.
W końcu zawiedzie. Kiedy się nie powiedzie, dotarcie do sedna będzie trudne i czasochłonne. Rozwiązywanie problemów wynikających z warunków wyścigu jest trudne. Nie występują one konsekwentnie, więc odtworzenie ich w izolacji będzie trudne, a nawet niemożliwe. Na początku nie wprowadziłeś prawidłowej obsługi błędów, więc prawdopodobnie nie będziesz miał wiele do zrobienia: może raport użytkownika końcowego o jakimś tajemniczym tekście (czego hej starałeś się przede wszystkim nie zobaczyć). Może ślad stosu, który wskazuje na tę funkcję, która, gdy na nią patrzysz, rażąco zaprzecza, że błąd powinien być możliwy.
* Mogą istnieć uzasadnione powody biznesowe, aby przeprowadzić takie kontrole, na przykład aby zapobiec powielaniu kosztownej pracy aplikacji, ale nie jest to odpowiedni zamiennik dla prawidłowej obsługi błędów.
źródło
Myślę, że warto tutaj zwrócić uwagę - jednym z powodów, dla których chcesz to zrobić, jest to, aby użytkownik mógł sformatować komunikat o błędzie.
Serdecznie polecam:
a) pokaż użytkownikowi końcowemu ten sam ogólny komunikat o błędzie dla każdego wystąpienia błędu.
b) zapisać rzeczywisty wyjątek w miejscu, do którego dostęp mają tylko programiści (jeśli jest na serwerze) lub w innym miejscu, które może zostać wysłane za pomocą narzędzi do zgłaszania błędów (jeśli wdrożony jest klient)
c) nie próbuj formatować szczegółów wyjątków błędów, które rejestrujesz, chyba że możesz dodać więcej przydatnych informacji. Nie chcesz przypadkowo „sformatować” jednej przydatnej informacji, której mógłbyś użyć do wyśledzenia problemu.
Krótko mówiąc - wyjątki są pełne bardzo przydatnych informacji technicznych. Nie powinno to być przeznaczone dla użytkownika końcowego, a użytkownik traci tę informację na własne ryzyko.
źródło