Chcę ponownie zgłosić ten sam wyjątek w SQL Server, który właśnie wystąpił w moim bloku try. Mogę wysłać tę samą wiadomość, ale chcę zgłosić ten sam błąd.
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Tags.tblDomain (DomainName, SubDomainId, DomainCode, Description)
VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
declare @severity int;
declare @state int;
select @severity=error_severity(), @state=error_state();
RAISERROR(@@Error,@ErrorSeverity,@state);
ROLLBACK TRANSACTION
END CATCH
RAISERROR(@@Error, @ErrorSeverity, @state);
Ta linia pokaże błąd, ale chcę mieć taką funkcjonalność. Rodzi to błąd z numerem błędu 50000, ale chcę, aby był wyrzucany numer błędu, który mijam @@error
,
Chcę uchwycić ten błąd nie na frontendzie.
to znaczy
catch (SqlException ex)
{
if ex.number==2627
MessageBox.show("Duplicate value cannot be inserted");
}
Chcę mieć tę funkcjonalność. czego nie można osiągnąć za pomocą raiseerror
. Nie chcę wyświetlać niestandardowego komunikatu o błędzie na zapleczu.
RAISEERROR
Powinien zwrócić poniżej wspomniany błąd, gdy przekażę ErrorNo do wrzucenia do połowu
Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,
Wiersz 14 Naruszenie ograniczenia UNIQUE KEY „UK_DomainCode”. Nie można wstawić zduplikowanego klucza w obiekcie „Tags.tblDomain”. Oświadczenie zostało zakończone.
EDYTOWAĆ:
Jaka może być wada nieużywania bloku try catch, jeśli chcę, aby wyjątek był obsługiwany na interfejsie użytkownika, biorąc pod uwagę, że procedura składowana zawiera wiele zapytań, które należy wykonać?
raiserror
, co różni się od sposobu, w jaki C # kończy działanie po plikuthrow
. Więc dodałemreturn
wewnątrz,catch
ponieważ chciałem dopasować to zachowanie.RAISERROR()
's docs . Poziom istotności ≥11 przeskakuje doCATCH
bloku tylko wtedy, gdy znajduje się wewnątrzTRY
bloku. Więc musisz miećBEGIN TRY…END CATCH
wokół kodu, jeśli chceszRAISERROR()
mieć wpływ na kontrolę przepływu.SQL 2012 wprowadza instrukcję throw:
http://msdn.microsoft.com/en-us/library/ee677615.aspx
BEGIN TRY BEGIN TRANSACTION ... COMMIT TRANSACTION END TRY BEGIN CATCH ROLLBACK TRANSACTION; THROW END CATCH
źródło
ROLLBACK
wierszu jest ważny! Bez tego możesz uzyskaćSQLException: Cannot roll back THROW
.Ponowne wczytywanie wewnątrz bloku CATCH (kod przed SQL2012, użyj instrukcji THROW dla SQL2012 i nowszych):
DECLARE @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(), @ErrorNumber int = ERROR_NUMBER(), @ErrorSeverity int = ERROR_SEVERITY(), @ErrorState int = ERROR_STATE(), @ErrorLine int = ERROR_LINE(), @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-'); SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage; RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)
źródło
Myślę, że masz następujące możliwości:
W pewnym momencie SQL prawdopodobnie wprowadzi polecenie reraise lub możliwość wychwycenia tylko niektórych błędów. Ale na razie użyj obejścia. Przepraszam.
źródło
Nie możesz: tylko silnik może zgłaszać błędy mniejsze niż 50000. Wszystko, co możesz zrobić, to zgłosić wyjątek, który wygląda tak ...
Zobacz moją odpowiedź tutaj, proszę
Pytający tutaj wykorzystał transakcje po stronie klienta, aby zrobić to, czego chciał, co moim zdaniem jest trochę głupie ...
źródło
Ok, to obejście ... :-)
DECLARE @Error_Number INT BEGIN TRANSACTION BEGIN TRY INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') /* Column 'Name' has unique constraint on it*/ END TRY BEGIN CATCH SELECT ERROR_NUMBER() --RAISERROR (@ErrorMessage,@Severity,@State) ROLLBACK TRAN END CATCH
Jeśli zauważysz blok catch, nie powoduje on błędu, ale zwraca rzeczywisty numer błędu (a także wycofa transakcję). Teraz w swoim kodzie .NET, zamiast wychwytywania wyjątku, jeśli używasz ExecuteScalar (), otrzymasz rzeczywisty numer błędu, który chcesz i wyświetlisz odpowiednią liczbę.
Mam nadzieję że to pomoże,
EDYCJA: - Tylko uwaga, jeśli chcesz uzyskać liczbę rekordów, na które ma to wpływ i próbujesz użyć funkcji ExecuteNonQuery, powyższe rozwiązanie może nie działać dla Ciebie. W przeciwnym razie myślę, że będzie pasował do tego, czego potrzebujesz. Daj mi znać.
źródło
Sposobem na zatrzymanie wykonywania w procedurze składowanej po wystąpieniu błędu i przeniesienie błędu z powrotem do programu wywołującego jest wykonanie każdej instrukcji, która może zgłosić błąd z tym kodem:
Sam byłem zaskoczony, gdy dowiedziałem się, że wykonywanie w procedurze składowanej może być kontynuowane po błędzie - nieświadomość tego może prowadzić do trudnych do wyśledzenia błędów.
Ten typ obsługi błędów równoległych (pre .Net) Visual Basic 6. Czekamy na polecenie Throw w SQL Server 2012.
źródło
Biorąc pod uwagę, że nie przeniosłeś się jeszcze do 2012, jednym ze sposobów zaimplementowania propagacji oryginalnego kodu błędu jest użycie części wiadomości tekstowej wyjątku, który (ponownie) wyrzucasz z bloku catch. Pamiętaj, że może zawierać pewną strukturę, na przykład tekst XML dla kodu wywołującego do przeanalizowania w swoim bloku catch.
źródło
Możesz również utworzyć opakowującą procedurę składowaną dla tych scenariuszy, jeśli chcesz, aby instrukcja SQL była wykonywana w ramach transakcji i przekazywała błąd do kodu.
CREATE PROCEDURE usp_Execute_SQL_Within_Transaction ( @SQL nvarchar(max) ) AS SET NOCOUNT ON BEGIN TRY BEGIN TRANSACTION EXEC(@SQL) COMMIT TRANSACTION END TRY BEGIN CATCH DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE() ROLLBACK TRANSACTION RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState) END CATCH GO -- Test it EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2' EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2' EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'
źródło
Z punktu widzenia projektowania, jaki jest sens rzucania wyjątków z oryginalnymi numerami błędów i niestandardowymi komunikatami? Do pewnego stopnia zrywa kontrakt interfejsu między aplikacjami a bazą danych. Jeśli chcesz wyłapać oryginalne błędy i obsłużyć je w wyższym kodzie, nie obsługuj ich w bazie danych. Następnie, gdy złapiesz wyjątek, możesz zmienić wiadomość prezentowaną użytkownikowi na dowolną. Nie zrobiłbym tego jednak, ponieważ powoduje to, że kod twojej bazy danych hmm jest „nieprawidłowy”. Jak powiedzieli inni, powinieneś zdefiniować zestaw własnych kodów błędów (powyżej 50000) i zamiast tego je wyrzucić. Następnie możesz rozwiązać problemy z integralnością („Powielone wartości są niedozwolone”) niezależnie od potencjalnych problemów biznesowych - „Kod pocztowy jest nieprawidłowy”, „Nie znaleziono wierszy spełniających kryteria” itd.
źródło
try { code(); } catch (Exception exc) { log(exc); throw; } finally { cleanup(); }
gdziethrow;
po prostu podniesie oryginalny wyjątek z jego oryginalnym kontekstem.