ما هي الطريقة الصحيحة لضمان إغلاق اتصال SQL عند طرح استثناء؟

StackOverflow https://stackoverflow.com/questions/141204

  •  02-07-2019
  •  | 
  •  

سؤال

أستخدم نمطًا يبدو مثل هذا كثيرًا.أتساءل عما إذا كان هذا جيدًا أم أن هناك أفضل الممارسات التي لا أطبقها هنا.

على وجه التحديد أنا أتساءل؛في حالة طرح استثناء، هل الكود الموجود في الكتلة الأخيرة كافٍ لضمان إغلاق الاتصال بشكل مناسب؟

public class SomeDataClass : IDisposable
{
    private SqlConnection _conn;

    //constructors and methods

    private DoSomethingWithTheSqlConnection()
    {
        //some code excluded for brevity

        try
        {
            using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection))
            {
                _SqlConnection.Open();
                countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
            }
        }
        finally
        {
            //is this the best way?
            if (_SqlConnection.State == ConnectionState.Closed)
                _SqlConnection.Close();
        }

        //some code excluded for brevity
    }

    public Dispose()
    {
        _conn.Dispose();
    }
}
هل كانت مفيدة؟

المحلول

لف كود معالجة قاعدة البيانات الخاصة بك داخل "استخدام"

using (SqlConnection conn = new SqlConnection (...))
{
    // Whatever happens in here, the connection is 
    // disposed of (closed) at the end.
}

نصائح أخرى

يحتفظ .Net Framework بتجمع اتصال لسبب ما.ثق به!:) ليس عليك كتابة الكثير من التعليمات البرمجية فقط للاتصال بقاعدة البيانات وإصدار الاتصال.

يمكنك فقط استخدام عبارة "استخدام" والتأكد من أن "IDBConnection.Release()" سيغلق الاتصال لك.

تميل "الحلول" المعقدة للغاية إلى إنشاء تعليمات برمجية بها أخطاء.البسيط هو الأفضل.

مستندات MSDN جعل هذا واضحا جدا ...

  • تقوم طريقة الإغلاق باستعادة أي معاملات معلقة.ثم يقوم بتحرير الاتصال بتجمع الاتصال أو إغلاق الاتصال إذا تم تعطيل تجمع الاتصالات.

ربما لم تقم (ولا تريد) بتعطيل تجمع الاتصالات، لذا يقوم التجمع في النهاية بإدارة حالة الاتصال بعد الاتصال بـ "إغلاق".قد يكون هذا مهمًا لأنك قد تشعر بالارتباك عند النظر من جانب خادم قاعدة البيانات إلى جميع الاتصالات المفتوحة.


  • يمكن للتطبيق استدعاء Close أكثر من مرة.لم يتم إنشاء أي استثناء.

فلماذا تهتم باختبار مغلق؟فقط اتصل بـ Close ().


  • الإغلاق والتخلص متساويان وظيفيًا.

ولهذا السبب أ استخدام يؤدي الحظر إلى اتصال مغلق. استخدام مكالمات التخلص لك.


  • لا تتصل بـ Close أو Dispose on a Connection، أو DataReader، أو أي كائن آخر مُدار في طريقة Finalize الخاصة بفصلك.

نصيحة هامة للسلامة.شكرا ايجون.

أعتقد أنه من خلال "_SqlConnection.State == ConnectionState.Closed" كنت تقصد !=.

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

الشيء الوحيد الذي يجب عليك تغييره بالتأكيد هو طريقة Dispose().يجب ألا تشير إلى كائن الاتصال قيد التخلص، لأنه ربما تم الانتهاء منه بالفعل في تلك المرحلة.يجب عليك اتباع نمط التخلص الموصى به بدلاً من ذلك.

نظرًا لأنك تستخدم IDisposables على أي حال.يمكنك استخدام الكلمة الأساسية "استخدام"، والتي تعادل في الأساس استدعاء التخلص من الكتلة النهائية، ولكنها تبدو أفضل.

انظر هذا السؤال لمعرفة الإجابة:

الإغلاق والتخلص - ما الذي يجب الاتصال به؟

إذا كانت مدة اتصالك عبارة عن استدعاء أسلوب واحد، فاستخدم الأمر using ميزة اللغة لضمان التنظيف المناسب للاتصال.بينما أ try/finally الكتلة هي نفسها من الناحية الوظيفية، فهي تتطلب المزيد من التعليمات البرمجية وتكون IMO أقل قابلية للقراءة.ليست هناك حاجة للتحقق من حالة الاتصال، يمكنك الاتصال Dispose بغض النظر عن ذلك، وسوف يتعامل مع تنظيف الاتصال.

إذا كان عمر اتصالك يتوافق مع عمر فئة تحتوي على ذلك، فقم بتنفيذه IDisposable وتنظيف الاتصال في Dispose.

ضع رمز إغلاق الاتصال داخل كتلة "أخيرًا" كما تظهر.أخيرًا، يتم تنفيذ الكتل قبل طرح الاستثناء.يعمل استخدام كتلة "الاستخدام" أيضًا، ولكنني أجد طريقة "أخيرًا" الصريحة أكثر وضوحًا.

يعد استخدام البيانات أمرًا قديمًا بالنسبة للعديد من المطورين، ولكن قد لا يعرف المطورون الأصغر سنًا ذلك عن غير قصد.

لا حاجة للمحاولة..أخيرًا حول "الاستخدام"، الاستخدام يكون محاولة .. أخيرا

هل لي أن أقترح هذا:


    class SqlOpener : IDisposable
    {
        SqlConnection _connection;

        public SqlOpener(SqlConnection connection)
        {
            _connection = connection;
            _connection.Open();

        }

        void IDisposable.Dispose()
        {
            _connection.Close();
        }
    }

    public class SomeDataClass : IDisposable
    {
        private SqlConnection _conn;

        //constructors and methods

        private void DoSomethingWithTheSqlConnection()
        {
            //some code excluded for brevity
            using (SqlCommand cmd = new SqlCommand("some sql query", _conn))
            using(new SqlOpener(_conn))
            {
                int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
            }
            //some code excluded for brevity
        }

        public void Dispose()
        {
            _conn.Dispose();
        }
    }

امل ان يساعد :)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top