استخدام تقنية ADO.NET
تَستطيع تطبيقات إطار عمل .NET الاتصال بمصادر البيانات (data sources) المُختلفة، مثل خادم SQL، وأوراكل Oracle، وXML، عن طريق تقنية ADO.NET -من مايكروسوفت-، وبالتالي يُمكِنها إدخال وجَلْب وتعديل البيانات الموجودة بمصدر البيانات (data source) بمجرد الاتصال به وِفقًا للصلاحيات المتناسبة.
تُوفِّر ADO.NET بِنْيَة معمارية بدون اتصال (connection-less)، وهو ما يُعدّ أسلوبًا آمنًا للتَعامُل مع قواعد البيانات؛ لأن -عن طريقه- لم يُعدّ من الضروري الإبقاء على الاتصال طوال الجلسة (session).
الممارسات المثلى عند التعامل مع ADO.NET
-
كقاعدة عامة، حَاوِل تَقصِير وقت الاتصال قدر المُسْتَطاع، واغلقه بمجرد انتهاء تَّنْفيذ الإجراء المطلوب إغلاقًا صريحًا، مما يَضمَن عودة الكائن المُستخدَم في الاتصال إلى مَجمع الاتصالات (connection pool)، ويُحسِن من أداء الاتصال الفعلّي بخادم قاعدة البيانات. لاحظ أن أكبر حجم لمَجمع الاتصالات (pool max size) هو 100 بشكل افتراضي. تجمُّع الاتصالات (connection pooling) بخادم SQL
-
أحِط كل الكائنات المُستخدَمة في الاتصال بقاعدة البيانات (database connections) بكتلة
using
، مما يَضمَن إغلاقها (dispose) والتخلص منها حتى في حالة التبلِّيغ عن اعتراض. اِطلع على عبارة using (مرجع c#) لمزيد من المعلومات. -
اِسترجِع سَلاسِل اتصال قواعد البيانات (connection strings) بالاسم من ملف
app.config
أوweb.config
بناءً على نوع التطبيق: -
يَتطلَّب إدراج مَرجِع لمكتبة
System.configuration
. -
اِطلع على سلاسل الاتصال وملفات الإعداد لمزيد من المعلومات عن كيفية تنظيم ملف الإعداد.
-
ضَمِّن أي قيمة دَخْل بمُعامِل (parameter)؛ لأنه:
-
يُجنّبك هجمات الحقن (SQL Injection).
-
يُجنّبك الأخطاء في حالة استخدام نص مُتلاعَب به (malformed)، مثل تَضْمِين علامة اقتباس أحادية بداخل النص، والتي تَستخدِمها قاعدة البيانات SQL كمِحرِف تهريب (escaping) أو لبدء سِلسِلة نصية جديدة.
-
يَسمَح لمُوفِّر قاعدة البيانات (database provider) بإعادة اِستخدَام خطط تَّنْفيذ الاستعلام (query plans) -إن أمكن- مما يُعزز من الكفاءة. (غُير مُدَعَّم من جميع مُوفِّري قواعد البيانات، فقط بعضها).
-
عند التَعامُل مع مُعامِلات قاعدة البيانات:
-
يُعدّ عدم تَوافق نوع وحجم مُعامِلات قاعدة البيانات أحد أهم الأخطاء الشائعة والتي تؤدي إلى فَشَل عمليات الإضافة والتَحْديث والجَلْب.
-
قُم بتسمية مُعامِلات قاعدة البيانات بمُسمَّيات ذات مغزى، بنفس الطريقة التي تُسمِي بها أيّ مُتغيّرات بالشيفرة.
-
حدِّد نوع العمود (column) بقاعدة البيانات، مما يَضمَن عدم اِستخدَام أنواع المُعامِلات الخاطئة وتَجنُّب أيّ نتائج غيْر مُتوقَّعة.
-
تَأكَد من صلاحية (validate) قيم المُعامِلات قبل تمريرها إلى الأوامر
command
(فكما تَعلَم: مُدخلات خاطئة - مُخرجات خاطئة garbage in, garbage out). -
اِستخدِم الأنواع الصحيحة عند إسناد القيم للمُعامِلات. مثلًا إذا كان لديك مُعامِل من النوع
DateTime
، لا تُسْنِد القيمة المطلوبة كسِلسِلة نصية من النوعstring
، ولكن اِسندها بحيث تَكون من النوعDateTime
أي بعد تحليلها (parsing). -
حدِّد خاصية
size
للمُعامِلات من النوعstring
؛ فقد يُعاد اِستخدَام نفس خطة تَّنْفيذ الاستعلام (execution plan) إذا كانت المُعامِلات مُتَوافِقة في النوع والحجم. يُستخدم-1
للإشارة إلىMAX
. -
لا تَستخدِم التابع
AddWithValue
؛ لأنه من السهل جدًا أن تَنسَى تَحديد نوع المُعامِل. اطلع على "هل يمكننا التوقف عن استخدام التابع AddWithValue؟" لمزيد من المعلومات. -
عند التَعامُل مع كائنات الاتصال:
-
اِحرص على تأخير فتح الاتصال قدر المُسْتَطاع، واغلقه بأسرع ما يمكن، بحيث يقتصِر وقت الاتصال على تَّنْفيذ الإجراء المطلوب فقط. يُنصح بذلك عامةً عند التَعامُل مع أي مصدر خارجي.
-
لا تُشارِك الكائنات المُستخدَمة في الاتصال بأي شكل (مثلًا: لا تَستخدِم نمط المفرّدة (singleton pattern) بهدف مشاركة نُسخة وحيدة من النوع
SqlConnection
)، ولكن انشِئ كائن جديد إذا اقتضت الضرورة وتَخلَص (dispose) منه بمجرد انتهائه من تَّنْفيذ المطلوب. وذلك للأسباب التالية:- يَمتلك معظم مُوفِّري قواعد البيانات مَجمع اتصالات (connection pool)، مما يعني أنه ليس من المُكلِف تنشئة كائن اتصال جديد.
- يَلْغي أي احتمالية مُستقبلية لحُدوث أخطاء إذا بدأت الشيفرة في التَعامُل مع أكثر من خيط (thread).
تنفيذ استعلامات SQL بصيغة أمر Command
public void SaveNewEmployee(Employee newEmployee) { // (1) using(SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionName"].ConnectionString)) { using(SqlCommand sc = new SqlCommand("INSERT INTO employee (FirstName, LastName,DateOfBirth /*etc*/) VALUES (@firstName, @lastName, @dateOfBirth /*etc*/)", con)) { // (2) sc.Parameters.Add(new SqlParameter("@firstName", SqlDbType.VarChar, 200){Value = newEmployee.FirstName ?? (object) System.DBNull.Value}); sc.Parameters.Add(new SqlParameter("@lastName", SqlDbType.VarChar, 200){Value = newEmployee.LastName ?? (object) System.DBNull.Value}); sc.Parameters.Add(new SqlParameter("@dateOfBirth", SqlDbType.Date){Value = newEmployee.DateOfBirth}); // (3) con.Open(); sc.ExecuteNonQuery(); } } }
(1): تنشئة كائن الاتصال بقاعدة البيانات ضِمْن كتلة using
(2): تم تَحديد النوع SqlDbType.VarChar
والحجم 200
للمُعامِل firstName
(3): إِرجاء الفَتح الفعلّي للاتصال قَدْرِ الإمكان
ملحوظة 1: اطلع على التعداد SqlDbType
ملحوظة 2: اطلع على التعداد MySqlDbType
استخدام واجهات مشتركة لتجريد الأنواع الخاصة بالمورد
نظرًا لوجود العديد من مصادر البيانات (data sources)، وبالتالي العديد من مُوفِّري قواعد البيانات (database providers)، يُفضَّل الاعتماد على واجهات مُشتَرَكة (common interfaces) لتجريد (abstract) العمليات المُشتَرَكة.
var providerName = "System.Data.SqlClient"; var connectionString = "{your-connection-string}"; var factory = DbProviderFactories.GetFactory(providerName); using(var connection = factory.CreateConnection()) { //IDbConnection connection.ConnectionString = connectionString; connection.Open(); using(var command = connection.CreateCommand()) { //IDbCommand command.CommandText = "{query}"; using(var reader = command.ExecuteReader()) { //IDataReader while(reader.Read()) { ... } } } }
ترجمة -وبتصرف- للفصل ADO.NET من كتاب .NET Framework Notes for Professionals
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.