۱۳۸۹/۰۹/۱۸

متغيرهاي استاتيك و برنامه‌هاي ASP.NET


هر متغير استاتيك تنها داراي يك مقدار، در يك AppDomain مشخص است (مگر اينكه با ويژگي ThreadStatic مزين شود). هر برنامه‌ي ASP.NET هم AppDomain جداگانه و منحصر به خود را دارا است. بنابراين تعريف يك متغير استاتيك در يك برنامه‌ي ASP.NET به معناي به اشتراك گذاري آن در بين تمامي درخواست‌هاي رسيده به سرور است. بنابراين عموما استفاده از متغيرهاي استاتيك در برنامه‌هاي چند كاربره ASP.NET يك اشتباه بزرگ است و در صورت استفاده از آن بايد منتظر تخريب اطلاعات يا دريافت نتايج غيرمنتظره‌اي باشيد (مگر اينكه واقعا مي‌دانيد داريد چكار مي‌كنيد، براي مثال كش كردن نگاشت‌هاي NHibernate به اين صورت و استفاده از الگوي singleton يا روش‌هاي مشابه كه بايد بين تمام كاربران به يك صورت و يك شكل به اشتراك گذاشه شود و در حين اجراي برنامه تغييري در آن حاصل نمي‌شود). براي مثال اگر كاربر يك، در صفحه‌ي يك، متغير استاتيكي را مقدار دهي كند، كاربر 2 نيز با مقدار به روز شده‌ي كاربر يك كار خواهد كرد كه به طور قطع اين مورد مد نظر شما نيست (چون به احتمال زياد طراحي شما بر اساس كار كاربر در يك Session است و نه يك مقدار براي تمام سشن‌هاي موجود در سايت) و همچنين بايد دقت داشت كه امنيت سيستم نيز در اين حالت زير سؤال است (زيرا در اين حالت تمامي كاربران، صرفنظر از سطوح دسترسي تعريف شده براي آن‌ها، دسترسي به اطلاعاتي خواهند داشت كه نبايد داشته باشند).
نكته‌ي ديگري را هم كه بايد در مورد ASP.NET به خاطر داشت اين است كه ويژگي ThreadStatic نيز در اينجا كمكي نمي‌كند؛ زيرا مطابق طراحي آن از تردها استفاده‌ي مجدد مي‌گردد.به عبارت ديگر در ASP.NET الزامي ندارد كه آغاز يك درخواست جديد حتما به همراه ايجاد يك ترد جديد باشد.
طول عمر اين نوع متغيرها هم تا زماني است كه وب سرور يا برنامه ري استارت شوند. فقط در اين حالت است كه نمونه‌ي موجود تخريب شده و سپس با اجراي مجدد برنامه، بازسازي خواهند شد.
بنابراين متغيرهاي استاتيك در ASP.NET همانند شيء Application عمل مي‌كنند و از آن سريع‌تر هستند زيرا زمانيكه به آن‌ها ارجاع مي‌شود نيازي به جستجو در يك جدول و يافتن آن‌ها نيست (برخلاف شيء Application) و همچنين در اينجا نيازي هم به عمليات تبديل نوع داده‌اي وجود ندارد (برخلاف نوع شيء Application كه به صورت Object تعريف شده است). وجود اشياء Application در ASP.NET فقط به جهت حفظ سازگاري آن با ASP كلاسيك است و توصيه شده است در ASP.NET به دلايلي كه ذكر شد،‌ اگر و تنها اگر نياز به اشيايي در سطح برنامه داشتيد از متغيرهاي استاتيك استفاده كنيد. شيء Cache نيز در ASP.NET همين كاربرد را دارد با اين تفاوت كه مي‌توان براي آن مدت زمان منقضي شدن تعريف كرد يا اينكه وب سرور بسته به حق تقدم و اهميتي كه براي آن تعريف شده است، مجاز به حذف كردن آن در زماني است كه با كمبود منابع مواجه مي‌شود. همچنين بايد دقت داشت كه تنها مكان ذخيره سازي متغيرهاي استاتيك حافظه‌ است اما امكان دخيره سازي كش بر روي فايل سيستم تا بانك اطلاعاتي و غيره نيز مهيا است.

سؤال: آيا تعريف SqlConnection به صورت استاتيك جزو مواردي است كه "مگر واقعا مي‌دانيد داريد چكار مي‌كنيد؟" ؟
پاسخ: خير. در اينجا هم واقعا اين شخص نمي‌داند كه دارد چكار مي‌كند! يعني در مورد سازوكار دروني ADO.NET اطلاعاتي ندارد. باز كردن يك كانكشن در ADO.NET به معناي مراجعه به استخر (pool) كانكشن‌ها و بازكردن يكي از آن‌ها و در مقابل، بستن يك كانكشن هم به معناي علامتگذاري يك كانكشن به صورت غيرفعال است و آماده سازي آن براي استفاده در درخواست بعدي. به معناي ديگر اين عمليات سربار آنچناني ندارد كه بخواهيد آن‌را استاتيك تعريف كنيد.
همچنين مورد ديگري را هم كه اين برنامه نويس نمي‌داند اين است كه متغيرهاي استاتيك thread safe نيستند. به عبارتي حين استفاده از آن‌ها در يك برنامه‌ي چندكاربره‌ي ASP.NET حتما بايد مكانيزم‌هاي قفل‌گذاري بر روي اين نوع متغيرها و اشياء اعمال شود (كه اين هم خود يك سربار اضافي است در مقياس چند 10 يا چند 100 كاربر همزمان). اين مشكلات همزماني به چه معنا است؟ فرض كنيد كاربر يك، شيء استاتيك SqlConnection ايي را باز كرده است و با آن مشغول كوئري گرفتن است. كاربر 2 نيز همزمان شروع به استفاده از اين كانكشن باز در حال استفاده مي‌كند (SqlConnection استاتيك يعني استفاده‌ي تمام كاربران فقط و فقط از يك كانكشن باز شده)، نتيجه اين خواهد بود كه براي مثال پيغام خطايي را دريافت مي‌كند مانند: فيلد مورد نظر در جدول موجود نيست! چرا؟ چون روي شيء استاتيك SqlConnection تعريف شده قفل گذاري صورت نگرفته است و در حين استفاده از آن هر كاربري در سايت نيز همان را استفاده خواهد كرد يا از آن بدتر ممكن است يك كاربر زودتر از كاربر ديگري آن‌را ببندد! كاربر سوم در وسط كار با پيغام غيرمعتبر بودن كانكشن مواجه مي‌شود، يا اينكه به صورت پيش فرض يك datareader را بيشتر نمي‌توان بر روي يك كانكشن باز شده اعمال كرد. كاربر 4 مشغول خواندن اطلاعات است، كاربر 5 ، پيغام غيرمعتبر بودن كوئري را دريافت مي‌كند.