سؤال

أنا فقط ألقي نظرة على بيان الاستخدام، وكنت أعرف دائمًا ما يفعله ولكن حتى الآن لم أحاول استخدامه، لقد توصلت إلى الكود أدناه:

 using (SqlCommand cmd = 
     new SqlCommand(reportDataSource, 
         new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
 {
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
     cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
     cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
     cmd.Connection.Open();

     DataSet dset = new DataSet();
     new SqlDataAdapter(cmd).Fill(dset);
     this.gridDataSource.DataSource = dset.Tables[0];
 }

يبدو أن هذا يعمل ولكن هل هناك أي فائدة في هذا لأنه بقدر ما أستطيع أن أقول أنني سأظل بحاجة إلى تضمين هذا في كتلة محاولة التقاط الأخطاء غير المتوقعة على سبيل المثال.خادم SQL معطل.هل فاتني شيء؟

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

هل كانت مفيدة؟

المحلول

ويجب أن تكون هذه التعليمات البرمجية كما يلي لضمان إغلاق في الوقت المناسب من الاتصال. إغلاق فقط الأمر لا يغلق إتصال:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
         {
             cmd.CommandType = CommandType.StoredProcedure;
             cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
             cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
             cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
             cmd.Connection.Open();

             DataSet dset = new DataSet();
             new SqlDataAdapter(cmd).Fill(dset);
             this.gridDataSource.DataSource = dset.Tables[0];
         }

لإجابة على سؤالك، يمكنك أن تفعل الشيء نفسه في كتلة أخيرا، ولكن هذا نطاقات رمز لطيف ويضمن أن تتذكر لتنظيف.

نصائح أخرى

عند القيام بعمل IO، أقوم بالتشفير إلى يتوقع استثناء.

SqlConnection conn = null;
SqlCommand cmd = null;

try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;

        conn.Open(); //opens connection

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}
finally
{
    if(conn != null)
        conn.Dispose();

        if(cmd != null)
        cmd.Dispose();
}

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

تحرير 2: يمكن للمرء أن يجادل بأن كتلة الاستخدام يمكن أن تؤدي إلى محاولة/التقاط في هذه الحالة، وهذا صحيح تمامًا ومكافئ وظيفيًا.هذا حقا يتلخص في التفضيل.هل تريد تجنب التعشيش الإضافي على حساب التعامل مع التخلص الخاص بك؟أو هل تتحمل التداخل الإضافي للتخلص التلقائي.أشعر أن الأول أنظف لذا أفعل ذلك بهذه الطريقة.ومع ذلك، لا أقوم بإعادة كتابة الأخير إذا وجدته في قاعدة التعليمات البرمجية التي أعمل فيها.

تحرير 3: أتمنى حقًا أن يكون MS قد أنشأ نسخة أكثر وضوحًا من use() مما جعل ما كان يحدث بالفعل أكثر سهولة ومنح المزيد من المرونة في هذه الحالة.خذ بعين الاعتبار الكود التخيلي التالي:

SqlConnection conn = null;
SqlCommand cmd = null;

using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
          cmd = new SqlCommand(reportDataSource, conn)
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}

تقوم عبارة الاستخدام فقط بإنشاء محاولة/أخيرًا باستخدام مكالمات Dispose() في النهاية.لماذا لا نمنح المطور طريقة موحدة للتعامل مع التخلص والاستثناءات؟

وربما لا يكون هناك ميزة لاستخدام بيان using في هذه الحالة إذا كنت تريد الذهاب لديك كتلة try / catch / finally على أي حال. وكما تعلمون، فإن بيان using هو نحوي السكر لtry / finally أن يتصرف في الكائن IDisposable. إذا كنت تريد الذهاب لديك بنفسك try / finally على أي حال، يمكنك أن تفعل بالتأكيد Dispose نفسك.

وهذا لا يتعدى كونه حقا أساسا بانخفاض أسلوب - فريقك قد تكون أكثر راحة مع البيانات using أو بيانات using قد جعل نظافة كود نظرة

.

ولكن، إذا كان النمطي ان بيان using يكون مختبئا هناك على أي حال، والمضي قدما والتعامل مع الأشياء لنفسك إذا كان هذا هو المفضل لديك.

إذا التعليمات البرمجية يبدو مثل هذا:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

وثم أود أن ريفاكتور لاستخدام محاولة .. الصيد .. أخيرا بدلا من ذلك.

SqlCommand cmd = new SqlCommand(...)
try
{
  /* call stored procedure */
}
catch (SqlException ex)
{
  /* handles the exception and does not ignore it */
}
finally
{
   if (cmd!=null) cmd.Dispose();
}

في هذا السيناريو، وأود أن يكون التعامل مع استثناء لذلك ليس لدي أي خيار سوى أن تضيف في هذا try..catch، وأنا قد وضعت كذلك في بند أخيرا وحفظ نفسي مستوى التداخل آخر. علما بأنني يجب أن نفعل شيئا في كتلة الصيد وليس فقط تجاهل الاستثناء.

وتعليقا على ما قاله كريس بالانس، وC # مواصفات (ECMA-334 الإصدار 4) قسم 15،13 الدول "وترجم العبارة باستخدام إلى ثلاثة أجزاء: اقتناء واستخدام والتخلص ومرفق طيه الاستخدام للموارد ضمنا في المحاولة. بيان يتضمن بندا أخيرا. يتصرف هذا الشرط أخيرا من الموارد. إذا حصلت على الموارد لاغيا، ثم يتم إجراء أية دعوة للتخلص، ويتم طرح أي استثناء ".

ووصف بالقرب 2 صفحات - تستحق القراءة

في تجربتي، ويمكن SqlConnection / SqlCommand إنشاء الأخطاء في نواح كثيرة بحيث تحتاج تقريبا إلى التعامل مع استثناءات القيت أكثر من التعامل مع السلوك المتوقع. أنا لست متأكدا من أنني كنت تريد شرط باستخدام هنا، كما كنت تريد أن تكون قادرة على التعامل مع حالة الموارد فارغة نفسي.

وتستخدم ليست حول اصطياد الاستثناءات. انها التخلص حول صحيح الموارد التي هي خارج نظرا لجمع القمامة.

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

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...

catch (exception)

   ... handle exception ...

}

it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...
   ... open a file or db connection ...

catch (exception)

   ... handle exception ...

finally

   ... close the file or db connection ...

}

لا يزال سوف تكون هناك حاجة لكتابة رمز للتخلص من MyDisposableObj ب / ج انها تريد التعامل معها من قبل using ...

وكيف تفعل مثل ذلك؟

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

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

وهناك الكثير من الإجابات كبيرة هنا، ولكن لا أعتقد أن قيل هذا حتى الان.

وبغض النظر عن ما ... سوف يتم استدعاء الأسلوب "تخلص" على الكائن في "باستخدام" كتلة. إذا وضعت عبارة إرجاع، أو رمي خطأ، "تخلص" سوف يتم استدعاؤها.

مثال:

ولقد تقدمت فئة تسمى "MyDisposable"، وأنها تنفذ IDisposable وببساطة يقوم Console.Write. أنه دائما يكتب إلى وحدة التحكم حتى في كل هذه السيناريوهات:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

ويتم تغيير العبارة باستخدام فعلا في محاولة / منع أخيرا من قبل المترجم الذي يتم التخلص المعلمة من كتلة باستخدام ما دامت تطبق الواجهة IDisposable. وبصرف النظر عن ضمان يتم التخلص كائنات محددة بشكل صحيح عندما يقعون خارج النطاق، هناك حقا أي خطأ اسر المكتسبة باستخدام هذا البناء.

وكما هو مذكور من قبل TheSoftwareJedi أعلاه، سوف تحتاج للتأكد من التخلص من كل من SqlConnection وكائنات SqlCommand منها بشكل صحيح. التراص على حد سواء في استخدام كتلة واحدة غير فوضوي قليلا، وربما لا تفعل ما كنت أعتقد أنه يفعل.

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

ولمعلوماتك، في هذا المثال معينة، لأنك كنت تستخدم اتصال ADO.net وجوه القيادة، أن ندرك أن العبارة باستخدام فقط ينفذ Command.Dispose، وConnection.Dispose () الذي لا يغلق في الواقع اتصال، ولكن ببساطة تصدر مرة أخرى في تجمع ADO.net اتصال ليتم استخدامها من قبل connection.open القادم ... وهو أمر جيد، والشيء الصحيح تماما القيام به، قبل الميلاد إذا لم تقم بذلك، الاتصال ستبقى unuseable حتى جمع القمامة النشرات مرة أخرى إلى التجمع، والتي قد لا تكون حتى العديد من طلبات الاتصال الأخرى، والتي لولاها سيضطر لإنشاء اتصالات جديدة على الرغم من هناك واحد غير المستخدمة في انتظار أن يتم تجميع البيانات المهملة.

وأود أن أتخذ قراري بشأن موعد وعند عدم استخدام العبارة باستخدام تعتمد على الموارد أتعامل معها. في حالة وجود مورد محدود، مثل اتصال ODBC I تفضل استخدام T / C / F حتى أتمكن من تسجيل الأخطاء ذات معنى عند نقطة وقوعها. السماح أخطاء برنامج تشغيل قاعدة البيانات فقاعة مرة أخرى إلى العميل، وربما تضيع في أعلى التفاف استثناء مستوى هو الأمثل الفرعي.

وT / C / F يمنحك راحة البال التي يجري التعامل مع الموارد بالطريقة التي ترغب فيها. كما سبق ان ذكرت بعض، العبارة باستخدام لا توفر استثناء التعامل مع أنه يضمن فقط ودمر المورد. معالجة الاستثناء هو بنية اللغة underuitilised والتقليل من شأنها أن غالبا ما يكون الفرق بين النجاح والفشل في التوصل إلى حل.

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

وانها تسمح لك لوضع التعليمات البرمجية معالجة الاستثناء في حدود طبقة / التجمع ويساعد على منع وظائف أخرى يصبح تشوش أيضا.

وبطبيعة الحال، انها حقا يتوقف على أنواع من الاستثناءات التي ألقاها التعليمات البرمجية. أحيانا يجب عليك استخدام محاولة اللحاق-أخيرا وليس باستخدام عبارة. عادتي هي أن تبدأ دائما مع العبارة باستخدام لIDisposables (أو لديك الطبقات التي تحتوي على IDisposables أيضا تطبيق واجهة) وإضافة محاولة تجميع مياه الأمطار أخيرا حسب الحاجة.

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

وتصحيح طفيفة على سبيل المثال: يحتاج SqlDataAdapter أيضا أن يكون مثيل في بيان using:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

أولا، ينبغي أن يكون مثال التعليمة البرمجية الخاصة بك:

using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}

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

إذا كنت بحاجة إلى معالجة الاستثناءات في البناء من الاتصال والقيادة (وكذلك عند استخدامها)، نعم، لديك لالتفاف الأمر برمته في محاولة / صيد:

try
{
    using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
    using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
        cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
        cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Connection.Open();

        DataSet dset = new DataSet();
        new SqlDataAdapter(cmd).Fill(dset);
        this.gridDataSource.DataSource = dset.Tables[0];
    }
}
catch (RelevantException ex)
{
    // ...handling...
}

ولكن لا تحتاج للتعامل مع تنظيف conn أو cmd. تم بالفعل القيام بذلك نيابة عنك.

وعلى النقيض مع نفس الشيء دون using:

SqlConnection conn = null;
SqlCommand cmd = null;
try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
    // ...handling...
}
finally
{
    if (cmd != null)
    {
        try
        {
            cmd.Dispose();
        }
        catch { }
        cmd = null;
    }
    if (conn != null)
    {
        try
        {
            conn.Dispose();
        }
        catch { }
        conn = null;
    }
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless

وأنا أعرف وأنا سوف بدلا من الكتابة. : -)

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