Pozornie preferowane podejście
Miałem wrażenie, że następujące zostały już przetestowane przez innych, zwłaszcza na podstawie niektórych komentarzy. Ale moje testy pokazują, że te dwie metody rzeczywiście działają na poziomie bazy danych, nawet podczas łączenia przez .NET SqlClient
. Zostały one przetestowane i zweryfikowane przez innych.
Cały serwer
Możesz ustawić ustawienie konfiguracji serwera opcji użytkownika tak, aby było to, co jest obecnie bitowo edytowane OR
przy pomocy 64 (wartość dla ARITHABORT
). Jeśli nie użyjesz bitowej funkcji OR ( |
), ale zamiast tego wykonasz proste przypisanie ( =
), usuniesz wszystkie inne istniejące już opcje.
DECLARE @Value INT;
SELECT @Value = CONVERT(INT, [value_in_use]) --[config_value] | 64
FROM sys.configurations sc
WHERE sc.[name] = N'user options';
IF ((@Value & 64) <> 64)
BEGIN
PRINT 'Enabling ARITHABORT...';
SET @Value = (@Value | 64);
EXEC sp_configure N'user options', @Value;
RECONFIGURE;
END;
EXEC sp_configure N'user options'; -- verify current state
Na poziomie bazy danych
Można to ustawić dla każdej bazy danych za pomocą ZMIEŃ ZESTAW BAZY DANYCH :
USE [master];
IF (EXISTS(
SELECT *
FROM sys.databases db
WHERE db.[name] = N'{database_name}'
AND db.[is_arithabort_on] = 0
))
BEGIN
PRINT 'Enabling ARITHABORT...';
ALTER DATABASE [{database_name}] SET ARITHABORT ON WITH NO_WAIT;
END;
Alternatywne podejścia
Niezbyt dobra wiadomość jest taka, że dużo szukałem w tym temacie, ale okazało się, że przez lata wiele innych szukało w tym temacie i nie ma sposobu, aby skonfigurować zachowanie z SqlClient
. Niektóre dokumenty MSDN sugerują, że można to zrobić za pomocą ConnectionString, ale nie ma słów kluczowych, które pozwoliłyby na zmianę tych ustawień. Inny dokument sugeruje, że można go zmienić za pomocą Menedżera konfiguracji sieci / Menedżera konfiguracji, ale nie wydaje się to również możliwe. Dlatego i raczej niestety trzeba będzie wykonać SET ARITHABORT ON;
ręcznie. Oto kilka sposobów na rozważenie:
JEŚLI używasz Entity Framework 6 (lub nowszego), możesz spróbować:
Użyj Database.ExecuteSqlCommand : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
Najlepiej byłoby to wykonać raz, po otwarciu połączenia DB, a nie dla każdego zapytania.
Utwórz przechwytywacz za pomocą:
To pozwoli Ci zmodyfikować SQL, zanim zostanie wykonane, w którym to przypadku można po prostu poprzedzić je: SET ARITHABORT ON;
. Minusem jest to, że będzie to za każdym zapytaniu, chyba że przechowywanie zmiennej lokalnej uchwycić stan, czy nie został on wykonany test i że za każdym razem (co naprawdę nie jest dużo dodatkowej pracy, ale przy użyciu ExecuteSqlCommand
jest prawdopodobnie łatwiej).
Każdy z nich pozwoli ci obsłużyć to w jednym miejscu bez zmiany istniejącego kodu.
ELSE można utworzyć metodę opakowania, która to robi, podobnie jak:
public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec)
{
CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText;
return CommandToExec.ExecuteReader();
}
a następnie po prostu zmień bieżące _Reader = _Command.ExecuteReader();
odniesienia na_Reader = ExecuteReaderWithSetting(_Command);
.
Dzięki temu ustawienie może być obsługiwane w jednym miejscu, wymagając jedynie minimalnych i uproszczonych zmian kodu, które można w większości wykonać za pomocą funkcji Znajdź i zamień.
Jeszcze lepiej ( Else Part 2), ponieważ jest to ustawienie poziomu połączenia, nie trzeba go wykonywać dla każdego wywołania SqlCommand.Execute __ (). Zamiast tworzyć opakowanie dla ExecuteReader()
, utwórz opakowanie dla Connection.Open()
:
public static void OpenAndSetArithAbort(SqlConnection MyConnection)
{
using (SqlCommand _Command = MyConnection.CreateCommand())
{
_Command.CommandType = CommandType.Text;
_Command.CommandText = "SET ARITHABORT ON;";
MyConnection.Open();
_Command.ExecuteNonQuery();
}
return;
}
A następnie wystarczy zastąpić istniejące _Connection.Open();
odniesieniaOpenAndSetArithAbort(_Connection);
.
Oba powyższe pomysły można zaimplementować w większym stylu OO, tworząc klasę rozszerzającą SqlCommand lub SqlConnection.
Lub jeszcze lepiej ( Else Part 3), możesz utworzyć moduł obsługi zdarzeń dla Connection StateChange i ustawić właściwość, gdy połączenie zmieni się z Closed
na Open
:
protected static void OnStateChange(object sender, StateChangeEventArgs args)
{
if (args.OriginalState == ConnectionState.Closed
&& args.CurrentState == ConnectionState.Open)
{
using (SqlCommand _Command = ((SqlConnection)sender).CreateCommand())
{
_Command.CommandType = CommandType.Text;
_Command.CommandText = "SET ARITHABORT ON;";
_Command.ExecuteNonQuery();
}
}
}
W tym miejscu wystarczy dodać następujące elementy do każdego miejsca, w którym utworzono SqlConnection
instancję:
_Connection.StateChange += new StateChangeEventHandler(OnStateChange);
Nie są wymagane żadne zmiany w istniejącym kodzie. Właśnie wypróbowałem tę metodę w małej aplikacji na konsolę, testując, drukując wynik SELECT SESSIONPROPERTY('ARITHABORT');
. Zwraca 1
, ale jeśli wyłączę moduł obsługi zdarzeń, zwraca 0
.
Dla kompletności, oto kilka rzeczy, które nie działają (albo wcale, albo nie tak skutecznie):
- Wyzwalacze logowania : Wyzwalacze, nawet jeśli działają w tej samej sesji, a nawet jeśli działają w ramach jawnie rozpoczętej transakcji, są nadal podprocesem, a zatem i jej ustawieniami (
SET
polecenia, lokalne tabele tymczasowe itp.) Są dla niej lokalne i nie przetrwają koniec tego podprocesu.
- Dodawanie
SET ARITHABORT ON;
na początku każdej procedury składowanej:
- wymaga to dużo pracy w przypadku istniejących projektów, zwłaszcza gdy rośnie liczba procedur przechowywanych
- nie pomaga to w zapytaniach ad hoc