2008-09-25 15 views
11

Zawsze czułem, że oczekiwanie, że wyjątki będą rzucane regularnie i używanie ich jako logiki przepływu było złe. Wyjątki wyglądają tak, jakby powinny być, no, "wyjątek". Jeśli spodziewasz się i planujesz wyjątek, może to oznaczać, że Twój kod powinien zostać refaktoryzowany, przynajmniej w .NET ...
Jednak. Niedawny scenariusz dał mi pauzę. Wysłałem to na msdn jakiś czas temu, ale chciałbym wygenerować więcej dyskusji na ten temat i jest to idealne miejsce!

Załóżmy, że masz tabelę bazy danych, która ma klucz obcy dla kilku innych tabel (w przypadku, w którym pierwotnie pojawiła się debata, wskazywały na to 4 klucze obce). Chcesz zezwolić użytkownikowi na usunięcie, ale tylko wtedy, gdy nie ma żadnych kluczy obcych; NIE chcesz kasować usuwania.
Zwykle robię czek, żeby sprawdzić, czy są jakieś odniesienia, a jeśli tak, to informuję użytkownika, zamiast robić usuwanie. Napisanie tego w LINQ jest bardzo proste i relaksujące, ponieważ powiązane tabele są członkami obiektu, więc Section.Projects i Section.Categories i et cetera jest miło pisać z intellisense i wszystkimi ...
Ale faktem jest, że LINQ następnie musi trafić potencjalnie wszystkie 4 tabele, aby zobaczyć, czy są jakieś wiersze wyników wskazujące na ten rekord, a trafienie do bazy danych jest oczywiście zawsze stosunkowo kosztowną operacją.

Prowadzący na tym projekcie poprosił mnie o zmianę, aby uchwycić wyjątek SqlException z kodem 547 (ograniczenie klucza obcego) i radzić sobie z nim w ten sposób.

Byłem ...
odporny.

Ale w tym przypadku jest to prawdopodobnie o wiele bardziej wydajne przejęcie narzutów związanych z wyjątkami niż połknięcie 4 trafień na stole ... Zwłaszcza, że ​​musimy to zrobić w każdym przypadku, ale oszczędzamy wyjątek w przypadku, gdy nie ma dzieci ...
Dodatkowo baza danych powinna być naprawdę odpowiedzialna za obsługę referencyjnej integralności, to jest jej zadanie i robi to dobrze ...
Więc oni wygrywali i zmieniłem to.

Na pewnym poziomie nadal wydaje mi się jednak nieprawidłowy.

Co sądzisz o oczekiwaniu i celowej obsłudze wyjątków? Czy to jest w porządku, gdy wygląda na to, że będzie skuteczniejsze niż wcześniejsze sprawdzenie? Czy jest to bardziej mylące dla kolejnego programisty patrzącego na Twój kod, czy mniej dezorientującego? Czy jest to bezpieczniejsze, skoro baza danych może wiedzieć o nowych ograniczeniach klucza obcego, które deweloper może nie pomyśleć o dodaniu czeku? Czy jest to kwestia perspektywy, co według Ciebie jest najlepszą praktyką?
Wyłapywanie wyjątków jako oczekiwaną kontrolę wykonania programu?

Odpowiedz

4

Wow,

Po pierwsze, można proszę destylować pytanie nieco w dół, a miło było czytać dobrze przemyślane i wyjaśnić pytanie, które było dość dużo do strawienia.

Krótka odpowiedź brzmi "tak", ale może zależeć.

  • Mamy pewne aplikacje, w których mamy dużo logiki biznesowej związanej z zapytaniami SQL (a nie moim projektem Gov!). Jeśli tak jest skonstruowane, zarządzanie może być trudne do przekonania o tym, że jest inaczej, ponieważ "już działa".
  • W tej sytuacji naprawdę ma to wielki wpływ? Ponieważ to wciąż jedna podróż przez drut i z powrotem. Czy serwer robi dużo, zanim zda sobie sprawę, że nie może kontynuować (np. Jeśli istnieje sekwencja transakcji, które mają miejsce w twoim działaniu, czy w połowie wypadła, marnując czas?).
  • Czy ma sens najpierw sprawdzenie w interfejsie użytkownika? Czy to pomaga w twojej aplikacji? Jeśli zapewnia przyjemniejsze wrażenia użytkownika? (Widziałem przypadki, w których przechodzi się przez kilka kroków w kreatorze, zaczyna się, a następnie przewraca, gdy ma wszystkie informacje, które trzeba przewrócić po kroku 1).
  • Czy współbieżność jest problemem? Czy jest możliwe, że rekord może zostać usunięty/edytowany lub cokolwiek innego przed zatwierdzeniem (tak jak w klasycznym boo-boo File.Exists).

Moim zdaniem:

zrobiłbym zarówno. Jeśli mogę szybko zawieść i zapewnić lepsze wrażenia dla użytkownika, świetnie. Wszelkie oczekiwane wyjątki SQL (lub inne) powinny zostać odpowiednio złapane i zwrócone.

wiem, że to concensus że wyjątki nie powinny być wykorzystywane do celów innych niż wyjątkowych okolicznościach, ale pamiętaj, że jesteśmy tu przekraczania granic aplikacji, oczekiwać niczego. Tak jak powiedziałem, to jest jak File.Exists, nie ma sensu, można go usunąć, zanim uzyskasz dostęp.

+0

Jest to bardzo mała aplikacja; w obecnym środowisku klienta, większość wstawiania będzie wykonywana tylko przez jedną osobę. Na maksimum jest chyba 25 użytkowników. Współbieżność nie jest problemem, wydajność nie jest problemem, to idealny moment na omówienie standardów;) – Grank

+0

Ale nie chodzi tylko o wydajność lub współbieżność. Jest to założenie, że jest problem. Tak jak powiedziałem, wykonaj oba, jeśli zapewnia to lepsze wrażenia dla użytkownika, ale w obu przypadkach należy postępować z wyjątkami. –

7

Twój trop jest absolutnie poprawny. Wyjątki są nie tylko raz w sytuacji niebieskiego księżyca, ale w szczególności w przypadku zgłaszania innych niż oczekiwane rezultatów.

W tym przypadku kontrola klucza obcego nadal będzie miała miejsce, a wyjątki to mechanizm, dzięki któremu można zostać powiadomionym.

Czego nie powinieneś robić, to łapać i tłumić wyjątki za pomocą instrukcji catchall. Wykonywanie precyzyjnych operacji wyjątków polega właśnie na tym, że wyjątki zostały zaprojektowane w pierwszej kolejności.

+0

Cóż, to oczekiwany wynik, to jest problem. Znacznie częściej niż nie, będą dzieci. – Grank

+0

Bardzo zgadzam się z twoją zmianą. Konkretnie tylko SqlException 547 zostaje złapany – Grank

+1

Całkowicie się z tobą zgadzam na tym jednym leviku, ludzie, którzy odpowiadają tym "wyjątkowymi okolicznościami", tracą punkt (zobacz mój post). –

1

Nie ma nic złego w tym, co robisz. Wyjątki nie są "wyjątkowe". Istnieją, aby pozwolić obiektom wywołującym na obsługę błędów drobnoziarnistych w zależności od ich potrzeb.

2

Chyba masz rację, wyjątki powinny być wykorzystywane tylko do obsługi nieoczekiwane rezultaty, tutaj używasz wyjątek do czynienia z prawdopodobnie oczekiwanego rezultatu, należy zająć się tym przypadku wyraźnie, ale nadal złapać wyjątek, aby pokazać możliwy błąd.

Jeśli nie zajmie się tym przypadkiem cały ten kod, chciałbym się z tobą skontaktować. Zagadnienie dotyczące wydajności powinno być zgłaszane tylko wtedy, gdy jest to rzeczywiście problem, tj. Zależałoby od wielkości tych tabel i liczby razy, kiedy ta funkcja jest używana.

1

Złapanie określonego wyjątku SqlException jest właściwe. Jest to mechanizm, za pomocą którego SQL Server komunikuje warunek klucza obcego. Nawet jeśli możesz faworyzować inne użycie mechanizmu wyjątków, tak robi to SQL Server.

Ponadto podczas sprawdzania czterech tabel inny użytkownik może dodać odpowiedni rekord przed zakończeniem sprawdzania, ale po przeczytaniu tej tabeli.

1

Polecam wywołanie procedury składowanej, która sprawdza, czy są jakieś zależności, a następnie usuwa, jeśli nie ma żadnych. W ten sposób sprawdza się integralność.

Oczywiście, prawdopodobnie potrzebowalibyśmy innej procedury składowanej dla pojedynczych usunięć a usuwania partii ... Usunięcie zbiorcze mogłoby wyszukać wiersze potomne i przywrócić zestaw rekordów, które nie kwalifikują się do usunięcia zbiorczego (miał wiersze potomne).

2

Ładne pytanie. Jednak znajduję odpowiedzi ... przerażające!
Wyjątkiem jest rodzaj GOTO.
Nie lubię używać wyjątków w ten sposób, ponieważ prowadzi to do kodu spaghetti.
Proste jak to.

+0

+1 za GOTO! to jest takie prawdziwe –

1

Nie lubię widzieć wyjątków w całym programie, ale w tym przypadku użyłbym mechanizmu wyjątku.

Nawet jeśli testujesz w LINQ, będziesz musiał złapać wyjątek na wypadek, gdyby ktoś wstawił rekord potomny podczas testowania integralności za pomocą LINQ. Ponieważ i tak musisz sprawdzić wyjątek, dlaczego duplikujesz kod?

Inną kwestią jest, że ten rodzaj wyjątku "krótkiego zasięgu" nie powoduje problemów z utrzymaniem, ani nie utrudnia czytania programu. Będziesz musiał spróbować, wywołanie SQL, aby usunąć, i catch, wszystko to w ciągu 10 linii kodu, razem. Intencja jest oczywista.

Nie tak, jak wyrzucanie wyjątku, który ma zostać przechwycony 5 wywołań procedur w górnej części stosu, z problemami upewnienia się, że wszystkie obiekty znajdujące się pomiędzy nimi zostały poprawnie usunięte.

Wyjątki nie zawsze są magiczną odpowiedzią, ale nie byłoby mi źle korzystać z nich w tej sytuacji.

moich dwóch centów,

Yves