首先要做的事情是:我正在使用MS SQL Server 2008和兼容级别为80的数据库,并使用.Net的System.Data.SqlClient.SqlConnection连接到它.
出于性能原因,我创建了一个索引视图.因此,需要使用ARITHABORT ON对视图中引用的表进行更新.但是,分析器显示SqlClient与ARITHABORT OFF连接,因此对这些表的更新失败.
是否有中央配置设置使SqlClient使用ARITHABORT ON?我能找到的最好的方法是每次打开连接时手动执行,但更新现有的代码库来执行此操作将是一项相当大的任务,所以我渴望找到更好的方法.
解决方法
看似首选的方法
我的印象是,其他人已经测试了以下内容,特别是基于一些评论.但我的测试表明,这两种方法确实可以在数据库级别工作,即使通过.NET SqlClient连接也是如此.这些已经过其他人的测试和验证.
服务器范围
您可以将user options服务器配置设置设置为当前按位与64进行ORING(ARITHABORT的值).如果您不使用按位OR(|)而是直接分配(=),那么您将清除已启用的任何其他现有选项.
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;”);
理想情况下,这将在打开数据库连接后执行一次,而不是按每个查询执行.
>通过以下任一方式创建interceptor:
> DbConfiguration.AddInterceptor
> DbInterception.Add
这将允许您在执行之前修改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);.
这样做还允许在单个位置处理设置,同时仅需要极少且简单的代码更改,这些更改通常可通过Find& amp;更换.
更好的是(其他第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的Class,可以以更多OO样式实现上述两个想法.
或者更好(另外第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.
为了完整起见,以下是一些不起作用的事情(根本不是或者没有效果):
> Logon Triggers:触发器,即使在同一会话中运行,即使在显式启动的事务中运行,仍然是一个子进程,因此其设置(SET命令,本地临时表等)是本地的,不是在该子流程结束时幸存下来.
>添加SET ARITHABORT ON;到每个存储过程的开头:
>这需要对现有项目进行大量工作,尤其是随着存储过程数量的增加>这无助于即席查询