看似首选的方法
我的印象是,以下人员已经过测试,尤其是根据某些评论。但是我的测试表明,即使通过.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来完成,但是没有关键字可以更改这些设置。另一个文档暗示可以通过客户端网络配置/配置管理器对其进行更改,但这似乎也不可能。因此,不幸的是,您将需要SET ARITHABORT ON;
手动执行。这里有一些考虑的方法:
如果您使用的是Entity Framework 6(或更高版本),则可以尝试以下任一方法:
使用Database.ExecuteSqlCommand:context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
理想情况下,这将在打开数据库连接后执行一次,而不是针对每个查询执行一次。
通过以下任一方式创建拦截器:
这将允许您在执行SQL之前对其进行修改,在这种情况下,您可以简单地为它加上前缀:SET ARITHABORT ON;
。这里的缺点是,它会按每个查询,除非你存储一个局部变量来捕捉它是否已被执行,每次的状态和测试(这真的不是那么多额外的工作,但使用ExecuteSqlCommand
是可能更容易)。
这些中的任何一个都可以让您一站式处理,而无需更改任何现有代码。
ELSE,您可以创建一个执行此操作的包装方法,类似于:
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的类,可以以更多的OO样式实现上述两个想法。
或者更好的(否则第3部分),您可以创建连接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;
到每个存储过程的开头:
- 对于现有项目,这需要大量工作,尤其是随着存储过程数量的增加
- 这对临时查询没有帮助