На первый взгляд предпочтительный подход
У меня сложилось впечатление, что следующее уже было проверено другими, особенно на основе некоторых комментариев. Но мое тестирование показывает, что эти два метода действительно работают на уровне БД, даже при подключении через .NET SqlClient
. Они были проверены и проверены другими.
Сервера
Вы можете установить настройку конфигурации сервера пользовательских опций так, чтобы она была битовой OR
с 64 (значение для ARITHABORT
). Если вы не используете побитовое ИЛИ ( |
), а вместо этого делаете прямое присваивание ( =
), тогда вы уничтожите любые другие существующие опции, которые уже включены.
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
База данных уровня
Это может быть установлено для каждой базы данных через ALTER DATABASE SET :
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;
Альтернативные подходы
Плохая новость заключается в том, что я много занимался поиском по этой теме, но обнаружил, что в течение многих лет многие другие занимались поиском по этой теме, и нет способа настроить поведение. из SqlClient
. Некоторая документация MSDN подразумевает, что это может быть сделано через ConnectionString, но нет ключевых слов, которые позволили бы изменить эти настройки. Другой документ подразумевает, что его можно изменить с помощью Client Network Configuration / Configuration Manager, но это также не представляется возможным. Следовательно, и, к сожалению, вам нужно будет выполнить SET ARITHABORT ON;
вручную. Вот несколько способов рассмотреть:
Если вы используете Entity Framework 6 (или новее), вы можете попробовать:
Использовать Database.ExecuteSqlCommand : в context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
идеале это должно быть выполнено один раз, после открытия соединения с БД, а не для каждого запроса.
Создайте перехватчик с помощью:
Это позволит вам изменить SQL перед выполнением, в этом случае вы можете просто префикс его: SET ARITHABORT ON;
. Недостатком здесь является то , что он будет в каждом запросе, если вы не хранить локальную переменную для фиксации состояния или не было выполнено и тест для этого каждый раз (который на самом деле не так много дополнительной работы, но с использованием ExecuteSqlCommand
в наверное проще).
Любой из них позволит вам справиться с этим в одном месте без изменения какого-либо существующего кода.
В противном случае , вы можете создать метод-оболочку, который делает это, подобно:
public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec)
{
CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText;
return CommandToExec.ExecuteReader();
}
а затем просто измените текущие _Reader = _Command.ExecuteReader();
ссылки на _Reader = ExecuteReaderWithSetting(_Command);
.
Это также позволяет обрабатывать настройки в одном месте, при этом требуются лишь минимальные и упрощенные изменения кода, которые в основном можно выполнить с помощью функции «Найти и заменить».
Еще лучше ( остальное часть 2), поскольку это настройка уровня соединения, ее не нужно выполнять для каждого вызова SqlCommand.Execute __ (). Таким образом, вместо создания оболочки для ExecuteReader()
, создайте оболочку для 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;
}
А затем просто замените существующие _Connection.Open();
ссылки на OpenAndSetArithAbort(_Connection);
.
Обе вышеприведенные идеи могут быть реализованы в большем количестве ОО-стилей путем создания класса, который расширяет либо SqlCommand, либо SqlConnection.
Или еще лучше ( остальное часть 3), вы можете создать обработчик событий для Connection StateChange и задать ему свойство при изменении соединения Closed
на 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();
}
}
}
Имея это в виду, вам нужно только добавить следующее в каждое место, где вы создаете SqlConnection
экземпляр:
_Connection.StateChange += new StateChangeEventHandler(OnStateChange);
Никаких изменений в существующий код не требуется. Я только что попробовал этот метод в небольшом консольном приложении, тестируя, печатая результат SELECT SESSIONPROPERTY('ARITHABORT');
. Он возвращается 1
, но если я отключаю обработчик событий, он возвращается 0
.
Для полноты картины вот некоторые вещи, которые не работают (либо вообще, либо не так эффективно):
- Триггеры входа в систему : триггеры, даже если они выполняются в том же сеансе и даже если они выполняются в явно запущенной транзакции, все еще являются подпроцессом, и, следовательно, его настройки (
SET
команды, локальные временные таблицы и т. Д.) Являются локальными для него и не сохраняются конец этого подпроцесса.
- Добавление
SET ARITHABORT ON;
в начало каждой хранимой процедуры:
- это требует большой работы для существующих проектов, особенно с увеличением числа хранимых процедур
- это не помогает специальным запросам