هل هناك أي عيوب لاستخدام نوع الإرجاع ienumerable لبيانات SQL؟

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

  •  05-07-2019
  •  | 
  •  

سؤال

سؤالي يتعلق بحالة اتصال SQL ، التحميل ، وما إلى ذلك بناءً على الكود التالي:

public IEnumberable<MyType> GetMyTypeObjects()
{
  string cmdTxt = "select * from MyObjectTable";

  using(SqlConnection conn = new SqlConnection(connString))
  {
    using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
    {
      conn.Open();
      using(SqlDataReader reader = cmd.ExecuteReader())
      {
         while(reader.Read())
         {
            yield return Mapper.MapTo<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

أستطيع أن أرى أن هذه مشكلة ربما إذا كانت هناك العديد من العمليات التي تعمل على تشغيل رمز مماثل مع أوقات تنفيذ طويلة بين تكرار الكائن غير المقصود ، لأن الاتصالات ستكون مفتوحة لفترة أطول ، وما إلى ذلك. ومع ذلك ، يبدو من المعقول أيضًا أن هذا سيقلل من استخدام وحدة المعالجة المركزية على خادم SQL لأنه يرجع البيانات فقط عند استخدام الكائن Ienumerable. كما أنه يقلل من استخدام الذاكرة على العميل لأنه يتعين على العميل فقط تحميل مثيل واحد من MyType أثناء عمله بدلاً من تحميل جميع حوادث MyType (عن طريق التكرار من خلال DataReader بأكمله وإعادة قائمة أو شيء ما).

  • هل هناك أي حالات يمكنك التفكير فيها في المكان الذي لا ترغب في استخدامه غير قابلة للتطبيق بهذه الطريقة ، أو أي حالات تعتقد أنها تناسبها تمامًا؟

  • ما نوع الحمل الذي وضعه هذا على خادم SQL؟

  • هل هذا شيء ستستخدمه في الكود الخاص بك (باستثناء أي ذكر لل nhibernate ، دون الصوت ، إلخ)؟

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

المحلول

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

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

حتى الامتدادات المدمجة للمكانة قد تتسبب في ذلك حتى لو كنت مخاطًا تستخدمها بشكل صحيح:

foreach (MyType item in GetMyTypeObjects().Take(10)) {
   ...
}

نصائح أخرى

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

أوصي ضد التحليل المسبق. في العديد من المواقف ، سيتم تجميع الاتصالات.

لا أتوقع أيضًا أي اختلاف في الحمل على SQL Server - سيتم تجميع الاستعلام بالفعل ، وسيتم تشغيله.

سيكون قلقي هو أنك تضع نفسك تمامًا تحت رحمة رمز العميل.

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

طالما يستخدم رمز العميل foreach, using وما إلى ذلك أو يستدعي صراحة العداد Dispose الطريقة التي أنت بخير ، ولكن لا يوجد شيء لمنعه من فعل شيء مثل هذا:

var e = GetMyTypeObjects().GetEnumerator();
e.MoveNext();    // open the connection etc

// forget about the enumerator and go away and do something else
// now the reader, command and connection won't be closed/disposed
// until the GC kicks in and calls their finalisers
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top