"Właściwy" sposób walidacji parametrów procedury składowanej
Mam procedurę składowaną, która wykonuje walidację parametrów i powinna zawieść i zatrzymać wykonanie, jeśli parametr nie jest poprawny.
Moje pierwsze podejście do sprawdzania błędów wyglądało tak:create proc spBaz
(
@fooInt int = 0,
@fooString varchar(10) = null,
@barInt int = 0,
@barString varchar(10) = null
)
as
begin
if (@fooInt = 0 and (@fooString is null or @fooString = ''))
raiserror('invalid parameter: foo', 18, 0)
if (@barInt = 0 and (@barString is null or @barString = ''))
raiserror('invalid parameter: bar', 18, 0)
print 'validation succeeded'
-- do some work
end
To nie zadziałało, ponieważ dotkliwość 18 nie zatrzymuje wykonania, a "Walidacja powiodła się" jest drukowana wraz z komunikatami o błędach.
Wiem, że mógłbym po prostu dodać zwrot po każdym raiserror, ale to wygląda trochę brzydko dla mnie:
if (@fooInt = 0 and (@fooString is null or @fooString = ''))
begin
raiserror('invalid parameter: foo', 18, 0)
return
end
...
print 'validation succeeded'
-- do some work
Od błędów z dotkliwością 11 i wyższą są złowione w bloku try/catch innym podejściem, które testowałem, było Zamknięcie mojego błędu sprawdzania wewnątrz takiego bloku try / catch. Problem polegał na tym, że błąd został połknięty i w ogóle nie został wysłany do klienta. Więc poszperałem trochę i znalazłem sposób narethrow błąd:
begin try
if (@fooInt = 0 and (@fooString is null or @fooString = ''))
raiserror('invalid parameter: foo', 18, 0)
...
end try
begin catch
exec usp_RethrowError
return
end catch
print 'validation succeeded'
-- do some work
Nadal nie jestem zadowolony z tego podejścia więc pytam:
Jak wygląda Walidacja parametrów? Czy istnieje jakaś "najlepsza praktyka", aby robić tego rodzaju sprawdzania?
5 answers
Nie sądzę, że istnieje jeden "właściwy" sposób, aby to zrobić.
Moje własne preferencje byłyby podobne do twojego drugiego przykładu, ale z oddzielnym krokiem walidacji dla każdego parametru i bardziej wyraźnymi komunikatami o błędach.
Jak mówisz, jest to trochę kłopotliwe i brzydkie, ale intencja kodu jest oczywista dla każdego, kto go czyta, i robi robotę.
IF (ISNULL(@fooInt, 0) = 0)
BEGIN
RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0)
RETURN
END
IF (ISNULL(@fooString, '') = '')
BEGIN
RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0)
RETURN
END
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-19 11:52:09
Zwykle unikamy funkcji raiseerror () i zwracamy wartość wskazującą na błąd, na przykład liczbę ujemną:
if <errorcondition>
return -1
Lub przekazać wynik w dwóch wyjściowych parametrach:
create procedure dbo.TestProc
....
@result int output,
@errormessage varchar(256) output
as
set @result = -99
set @errormessage = null
....
if <errorcondition>
begin
set @result = -1
set @errormessage = 'Condition failed'
return @result
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 12:20:33
Jak widać z historii odpowiedzi podążałem za tym pytaniem i zaakceptowałem odpowiedź, a następnie zacząłem "wymyślać" rozwiązanie, które w zasadzie było takie samo, jak twoje drugie podejście.
Kofeina jest moim głównym źródłem energii, ze względu na fakt, że większość mojego życia półśnie, ponieważ spędzam zbyt dużo czasu na kodowaniu; więc nie zdawałem sobie sprawy z mojego faux-pas, dopóki nie słusznie wskazał go.
Dlatego, dla przypomnienia, wolę twoje drugie podejście: użycie SP do podniesienia aktualny błąd, a następnie za pomocą TRY / CATCH wokół walidacji parametrów.
Zmniejsza zapotrzebowanie na wszystkie bloki IF / BEGIN/END, a tym samym zmniejsza liczbę linii, a także kładzie nacisk na walidację. Podczas czytania kodu dla SP ważne jest, aby być w stanie zobaczyć testy wykonywane na parametrach; wszystkie dodatkowe fluff składniowy, aby zaspokoić parser SQL po prostu dostaje w drodze, moim zdaniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-26 08:57:53
Wolę wrócić jak najszybciej, i nie widzę sensu, aby wszystko wróciło z tego samego punktu na końcu procedury. Przywykłem do tego, robiąc zbiórki, lata temu. Ponadto zawsze zwracam wartość:
RETURN 10
Aplikacja wyświetli błąd krytyczny na liczbach dodatnich i wyświetli komunikat ostrzegawczy użytkownika na wartościach ujemnych.
Zawsze przekazujemy parametr wyjściowy z tekstem Komunikatu o błędzie.
Przykład:
IF ~error~
BEGIN
--if it is possible to be within a transaction, so any error logging is not ROLLBACK later
IF XACT_STATE()!=0
BEGIN
ROLLBACK
END
SET @OutputErrMsg='your message here!!'
INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg)
RETURN 10
END
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 12:32:17
Zawsze używam parametru @ Is_Success bit jako wyjścia. Więc jeśli mam błąd to @Is_success=0. Gdy procedura rodzica sprawdzi, że @Is_Success=0, to wycofuje swoją transakcję (z transakcjami potomnymi) i wysyła komunikat o błędzie z @Error_Message do klienta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 10:53:07