it-swarm.asia

جعل SqlClient افتراضيًا على ARITHABORT ON

أولاً وقبل كل شيء: أستخدم MS SQL Server 2008 مع قاعدة بيانات بمستوى التوافق 80 ، وأتصل بها بـ _Net's System.Data.SqlClient.SqlConnection.

لأسباب تتعلق بالأداء قمت بإنشاء طريقة عرض مفهرسة. ونتيجة لذلك ، يجب إجراء تحديثات للجداول المشار إليها في العرض باستخدام ARITHABORT ON. ومع ذلك ، يظهر المحلل أن SqlClient متصل بـ ARITHABORT OFF ، لذلك فشلت التحديثات على تلك الجداول.

هل هناك إعداد تكوين مركزي لاستخدام برنامج SqlClient ARITHABORT ON؟ أفضل ما تمكنت من العثور عليه هو تنفيذ يدويًا أنه في كل مرة يتم فيها فتح اتصال ، ولكن تحديث قاعدة التعليمات البرمجية الحالية للقيام بذلك سيكون مهمة كبيرة إلى حد ما لذا أنا حريص على إيجاد طريقة أفضل.

32
Peter Taylor

النهج المفضل على ما يبدو

كان لدي انطباع بأن الآخرين قد تم اختبارهم بالفعل بالفعل ، خاصة بناءً على بعض التعليقات. لكن اختباري يُظهر أن هاتين الطريقتين تعملان بالفعل على مستوى قاعدة البيانات ، حتى عند الاتصال عبر .NET SqlClient. تم اختبارها والتحقق منها من قبل الآخرين.

على مستوى الخادم

يمكنك تعيين خيارات المستخدم إعداد تكوين الخادم ليكون أيًا كان حاليًا ORed بتًا مع 64 (قيمة ARITHABORT). إذا كنت لا تستخدم bit-wise 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 ، ولكن لا توجد كلمات رئيسية تسمح بتغيير هذه الإعدادات. يشير مستند آخر إلى أنه يمكن تغييره عبر Client 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);.

يسمح هذا أيضًا بمعالجة الإعداد في مكان واحد بينما لا يتطلب سوى الحد الأدنى من التغييرات البرمجية البسيطة والمبسطة التي يمكن إجراؤها عبر Find & Replace.

الأفضل من ذلك ( Else الجزء 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);.

يمكن تنفيذ كلتا الفكرتين أعلاه في نمط OO من خلال إنشاء فئة تمتد إلى SqlCommand أو SqlConnection.

أو الأفضل من ذلك ( Else الجزء 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; إلى بداية كل إجراء مخزن:
    • هذا يتطلب الكثير من العمل للمشاريع القائمة ، خاصة مع زيادة عدد الإجراءات المخزنة
    • هذا لا يساعد الاستعلامات المخصصة
29
Solomon Rutzky

الخيار 1

بصرف النظر عن حل Sankar ، فإن تعيين إعداد الإجهاض الحسابي على مستوى الخادم لجميع الاتصالات سيعمل:

EXEC sys.sp_configure N'user options', N'64'
GO
RECONFIGURE WITH OVERRIDE
GO

اعتبارًا من SQL 2014 إنه يوصى بتشغيله لجميع الاتصالات:

يجب عليك دائمًا تعيين ARITHABORT على ON في جلسات تسجيل الدخول. يمكن أن يؤثر تعيين ARITHABORT على OFF سلبًا على تحسين الاستعلام مما يؤدي إلى مشكلات في الأداء.

لذلك يبدو أن هذا هو الحل المثالي.

الخيار 2

إذا لم يكن الخيار 1 قابلاً للتطبيق وكنت تستخدم الإجراءات المخزنة لمعظم مكالمات SQL الخاصة بك (وهو ما يجب عليك فعله ، انظر الإجراءات المخزنة مقابل SQL المضمنة ) ، فقم ببساطة بتمكين الخيار في كل إجراء مخزن ذي صلة:

CREATE PROCEDURE ...
AS 
BEGIN
   SET ARITHABORT ON
   SELECT ...
END
GO

أعتقد أن الحل الأفضل الحقيقي هنا هو ببساطة تعديل التعليمات البرمجية الخاصة بك ، لأنها خاطئة وأي إصلاح آخر هو مجرد حل.

6
LowlyDBA

لست خبيرًا هنا ولكن يمكنك تجربة شيء مثل أدناه.

String sConnectionstring;
sConnectionstring = "Initial Catalog=Pubs;Integrated Security=true;Data Source=DCC2516";

SqlConnection Conn = new SqlConnection(sConnectionstring);

SqlCommand blah = new SqlCommand("SET ARITHABORT ON", Conn);
blah.ExecuteNonQuery();


SqlCommand cmd = new SqlCommand();
// Int32 rowsAffected;

cmd.CommandText = "dbo.xmltext_import";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = Conn;
Conn.Open();
//Console.Write ("Connection is open");
//rowsAffected = 
cmd.ExecuteNonQuery();
Conn.Close();

المرجع: http://social.msdn.Microsoft.com/Forums/en-US/transactsql/thread/d9e3e8ba-4948-4419-bb6b-dd5208bd7547/

4
Sankar Reddy

لا يوجد إعداد لإجبار SqlClient على تشغيل ARITHABORT دائمًا ، يجب عليك تعيين هذا كما تصف.

المثير للاهتمام من وثائق Microsoft لـ SET ARITHABORT : -

يجب عليك دائمًا تعيين ARITHABORT على ON في جلسات تسجيل الدخول. يمكن أن يؤثر تعيين ARITHABORT على OFF سلبًا على تحسين الاستعلام مما يؤدي إلى مشكلات في الأداء.

ومع ذلك ، فإن اتصال .Net تم تشفيره بشكل ثابت لتعيين هذا بشكل افتراضي؟

كنقطة أخرى ، يجب أن تكون حذراً للغاية عند تشخيص مشاكل الأداء في هذا الإعداد. ستؤدي خيارات المجموعة المختلفة إلى خطط استعلام مختلفة للاستعلام نفسه. قد يواجه رمز .Net الخاص بك مشكلة في الأداء (SET ARITHABORT OFF) ومع ذلك عند تشغيل نفس استعلام TSQL في SSMS (SET ARITHABORT ON افتراضيًا) ، قد يكون الأمر جيدًا. هذا لأنه لن يتم إعادة استخدام خطة الاستعلام .Net وإنشاء خطة جديدة. يمكن أن يؤدي ذلك إلى القضاء على مشكلة استنشاق المعلمات على سبيل المثال وتقديم أداء أفضل بكثير.

2
Andy Jones

إذا وفرت لأي شخص بعض الوقت ، في حالتي (Entity Framework Core 2.0.3 ، ASP.Net Core API ، SQL Server 2008 R2):

  1. لا توجد اعتراضات على EF Core 2.0 (أعتقد أنها ستكون متاحة قريبًا في 2.1)
  2. لا تغيير إعداد قاعدة البيانات العمومية ولا تعيين user_options كان مقبولاً بالنسبة لي (إنهم يعملون بالفعل - لقد اختبرت) لكنني لم أستطع المخاطرة بالتأثير على التطبيقات الأخرى.

استعلام مخصص من EF Core مع SET ARITHABORT ON; في الأعلى ، لا يعمل.

أخيرًا ، كان الحل الذي نجح في إجرائه هو: الجمع بين إجراء مخزّن ، يُسمى كاستعلام أولي مع خيار SET قبل EXEC مفصولة بفاصلة منقوطة ، على النحو التالي:

// C# EF Core
int result = _context.Database.ExecuteSqlCommand([email protected]"
SET ARITHABORT ON;
EXEC MyUpdateTableStoredProc
             @Param1 = {value1}
");
2
Chris Amelinckx

البناء على إجابة سليمان رتزي لـ EF6:

using System.Data;
using System.Data.Common;

namespace project.Data.Models
{
    abstract class ProjectDBContextBase: DbContext
    {
        internal ProjectDBContextBase(string nameOrConnectionString) : base(nameOrConnectionString)
        {
            this.Database.Connection.StateChange += new StateChangeEventHandler(OnStateChange);
        }

        protected static void OnStateChange(object sender, StateChangeEventArgs args)
        {
            if (args.OriginalState == ConnectionState.Closed
                && args.CurrentState == ConnectionState.Open)
            {
                using (DbCommand _Command = ((DbConnection)sender).CreateCommand())
                {
                    _Command.CommandType = CommandType.Text;
                    _Command.CommandText = "SET ARITHABORT ON;";
                    _Command.ExecuteNonQuery();
                }
            }
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        ...

يستخدم هذا System.Data.CommonDbCommand بدلاً من SqlCommand ، و DbConnection بدلاً من SqlConnection.

يؤكد تتبع SQL Profiler ، يتم إرسال SET ARITHABORT ON عند فتح الاتصال ، قبل تنفيذ أي أوامر أخرى في المعاملة.

0
CapNCook