لدي الكثير من الطموح في المشاركة من أجل الإفادة والاستفادة من خلال عمل مشاريع برمجية مفتوحة المصدر يشارك في برمجتها فريق عمل متكامل، وذلك بدلا عن النقاشات الجدلية التي لا تحتوي على سطر برمجي واحد، إلا أن ذلك من المتعذر عمليا نظرا لكثير من الأسباب التي تم الاسهاب في تناولها والتي تعيق عمل مثل هذه المشاريع الكبيرة، ولذلك ارتأيت المساهمة بمجموعة من الأفكار والحلول البرمجية التي يمكن الاستفادة منها، وبنفس الوقت أتوخى أن أستفيد من من مناقشة الآخرين لهذه الأفكار والحلول بصورة عملية، علماً بأن المشروع الذي سوف أتطرق إليه هو ما أستخدمه فعلاً في مشاريع التطوير التي أقوم بها وأحاول تطويره بشكل مستمر.
مقدمة
الكثير منا يعرف بأن هناك معمارية لبناء التطبيقات تدعى N-Tier Architecture وفكرتها تقوم على مبدأ فصل أجزاء المشروع البرمجي إلى عدة طبقات ويتفاوت عدد هذه الطبقات حسب نوع التطبيق وحجمه ولكن في الأعم الأغلب هناك ثلاثة طبقات أساسية هي: Data Access Layer و Business Layer و Presentation Layer.
فكرة المشروع
فكرة المشروع هي بناء القاعدة الأساسية المساعدة لطبقة الوصول للبيانات ، وللتوضيح أكثر تحتوي طبقة الوصول للبيانات Data Access Layer على جزئين أساسيين:
أولاً: جزء يتعلق بالتعامل مع مختلف أنواع قواعد البيانات ومزوديها في بيئة الدوت نت MS .Net Framework ووظيفته الأساسية هي تنظيم عملية الاتصال بقاعدة البيانات وتنفيذ الاستعلامات.
ثانياً: الجزء الآخر يقوم باستخدام الجزء الأول الذي تحدثنا عنه عبر تمرير الاستعلامات المطلوبة Queries واستلام نتائج هذه الاستعلامات، وهو يختلف باختلاف نوع التطبيق وقاعدة البيانات المرتبطة به والجداول التي تحتويها قاعدة البيانات.
ما نود عمله هو الجزء الأول وهو أساس فكرة المشروع الذي سنسميه قاعدة الوصول للبيانات Data Access Base ، وحاليا المشروع يعتمد على قواعد بيانات SQL Server 2005 وبالإمكان تطوير ليشمل قواعد البيانات الأخرى.
المزايا العامة للمشروع:
1) إعادة الاستخدام Reusability: يمكن استخدام هذه القاعدة عند البدء بأي مشروع، بدون إعادة كتابة الأكواد وتكرارها.
2) سهولة الاستخدام Ease of Use: الكود المستخدم سهل الاستخدام وواضح (للمبرمج طبعا) وخالٍ من التعقيد والتكرار.
3) الكفاءة Quality: عدم وجود أخطاء برمجية في طريقة الوصول لقاعدة البيانات، وفي حال وجود أخطاء يتم التعامل معها بصورة مناسبة يتم التطرق لها لاحقاً.
4) الأداء Performance: سرعة تنفيذ الاستعلامات عبر الوصول المباشر لقاعدة البيانات.
5) المرونة Flexibility: القدرة الكبيرة للمبرمج في التحكم بأجزاء البرنامج المختلفة بدون الحاجة لتغيير الكود.
6) الحجم الصغير Small Size: حجم الكود صغير جدا.
7) الأمن Security: ضمان مستوى معقول من الأمن على البيانات عند تمرير الاستعلامات.
أدوات التطوير Development Tools
مشروع Data Access Base عبارة عن مكتبة فئاتWindows Class Library مكتوبة بلغة C# من خلال برنامج Microsoft Web Developer 2010 Express وهو برنامج مجاني من شركة مايكروسوفت.
قاعدة البيانات Database
يمكن استخدام أي قاعدة بيانات من نوع SQL Server 2005 Or 2008
كيفية الاستخدام
المكان الطبيعي لإضافة هذه المكتبة ضمن معمارية تعدد الطبقات N-Tier Architecture هي طبقة البيانات Data Layer لأن هذه الطبقة سوف تقوم باستخدام المكتبة التي قمنا بعملها وفي الحقيقة إذا إردنا أن ننظر من منظار هندسة البرمجيات ستكون هذه المكتبة هي الواسطة بين قاعدة البيانات وبين Data Layer الخاصة بأي مشروع. ولكن يمكن استخدامها مباشرة في أي تطبيق حتى لو لم يكن يعتمد معمارية تعدد الطبقات وفي هذه الحالة سيتم إضافة هذه المكتبة مباشرة في تطبيق الويب Web Application أو في تطبيق الويندوز Windows Application وسيتم استخدام هذه الطريقة في عرض أمثلة استخدام هذه المكتبة.
خطوات الاستخدام
أولاً:
كما قلنا سابقا المشروع عبارة عن مكتبة فئات Windows Class Library يمكن إدراج هذه المكتبة ضمن أي مشروع Project عبر الوظيفة Add Reference الموجودة تحت القائمة Project أو بالضغط على يمين الفأرة على References.
ثانياً:
إضافة نص الاتصال ConnectionString الخاص بقاعدة البيانات في ملف Web.Config ويقع تحت قسم ConnectionStrings ، ويمكن إضافة أكثر من نص اتصال وذلك في حالة كان التطبيق يحتاج للاتصال بأكثر من قاعدة بيانات واحدة.
<connectionStrings>
<add name="MySchool_DB" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=MySchool;Persist Security Info=True;User ID=sa; Password=12345" providerName="System.Data.SqlClient"/>
</connectionStrings>ثالثاً:
إضافة مفتاح جديد في تحت قسم AppConfig في نفس الملف Web.cofig وذلك باسم ConnectionStringName وذلك لتحديد قاعدة البيانات الافتراضية، والفائدة من إضافة هذا المفتاح هي امكانية تغيير قاعدة البيانات الافتراضية بسهولة في حال وجود أكثر من قاعدة بيانات وعادة ما يحدث ذلك في فترات التطوير حيث تستخدم قاعدة بيانات ومن ثم يتم التغيير لقاعدة البيانات أخرى بعد انتهاء هذه الفترة.
<appSettings>
<add key="ConnectionStringName" value="MySchool_DB"/>
</appSettings>رابعاً:
في هذا المثال نستعرض طريقة عمل مكتبة (قاعدة الوصول للبيانات) ، في المثال التالي سوف نقوم باستخدام قاعدة الوصول للبيانات Data Access Base مباشرة في تطبيق ويب Web Application تحت الحدث Page_Load ، اترك لكم قراءة الكود:
protected void Page_Load(object sender, EventArgs e)
{
SqlServerDb schoolDb = new SqlServerDb();
SqlDataReader reader = schoolDb.ExecuteReader("SELECT * FROM Students");
if (reader.HasRows)
{
while (reader.Read())
{
Response.Write(reader[0] + "-" + reader[1] + "<br/>");
}
}
reader.Close();
}في المثال السابق نقطتين مهمتين الأولى تتعلق بالسطر الاول:
SqlServerDb schoolDb = new SqlServerDb();
هذا السطر يقوم بعمل نموذج جديد من الفئة SqlServerDB والذي بشكل افتراضي يقوم باعتبار أن النص الافتراضي لقاعدة البيانات هو النص الذي حددنا اسمه ConnectionStringName ولذلك لا حاجة لذكر أي شيء يتعلق بنص الاتصال أو بقاعدة البيانات.
أما السطر التالي:
SqlDataReader reader = schoolDb.ExecuteReader("SELECT * FROM Students");فيتم فيه استدعاء الدالة ExecuteReader وتمرير الاستعلام SQL Query لتنفيذه واستلام نتيجة الاستعلام في المتغير reader، بقية الأسطر واضحة ولكن فقط أردت الإشارة بأن من المهم استدعاء الدالة Close بعد الانتهاء من قراءة البيانات.
امثلة أخرى:
في المثال السابق استعرضنا طريقة تنفيذ استعلام عن طريق الدالة ExecuteReader ولكن هناك دوال أخرى يمكن استخدامها وبالمناسبة هي أسماء مألوفة الاستخدام ولها معان واضحة ، مثال على ذلك:
schoolDb.ExecuteNonQuery("INSERT INTO Students (StudentID, StudentName) VALUES (1,'Ali');");مثال آخر:
object objCount = schoolDb.ExecuteScalar("SELECT COUNT(*) FROM Students");هناك دالة أخرى خاصة باسترجاع البيانات بهيئة DataSet ونستخدمها بالصورة التالية:
DataSet dsStudents = schoolDb.GetDataSet("SELECT * FROM Students");تمرير الوسائط للاستعلامات
عادة ما يتم استخدام الوسائط Parameters لتمريرها للاستعلامات، هناك طريقتنان لعمل ذلك الطريقة الاولى وهو تمرير المتغيرات مباشرة إلى الاستدعاء ولكن هذه الطريقة غير آمنة نظرا لإمكانية حدوث اختراق من نوع SQL Injection ، أما الطريقة الثانية هي تمرير الوسائط SqlParameters ولكن يعيب هذه الطريقة أنها تحتاج لكتابة الكثير من الأكواد وتكرارها في حال كتابة أي استدعاء بالطريقة التقليدية فيجب تمريره إلى CommandObject ثم اضافة كل وسيط على حدة وتفاصيل أخرى. من أهم مزايا استخدام هذه المكتبة هي الطريقة السهلة والمختصرة في تطبيق هذا الأمر، المثال التالي يوضح ذلك:
ArrayList arParameters = new ArrayList();
arParameters.Add(new SqlParameter("@ClassRoomID", txtClassRoom.Text));
SqlDataReader reader = schoolDb.ExecuteReader("SELECT * FROM Students Where ClassRoomID = @ ClassRoomID", arParameters);بالمثال السابق نستطيع تجاوز مشكلة عدم القدرة على تمرير SqlParameters كمجموعة Collection نظرا لبعض الصعوبات التقنية المرتبطة بطريقة تطوير هذه العناصر ضمن إطار عمل الدوت نت .Net Framework وهي كثيرا ما واجهتني شخصيا لكني مؤخرا تغلبت عليها عبر تمرير ArrayList يضم قائمة الوسائط وهو اسهل بكثير في التعامل من ال Array العادي في تمرير الوسائط وفي طريقة الاضافة حيث أنه من النوع ال Dynamic.
إشارة أخرى هي أن كل دالة من الدوال التي ذكرتها سابقا يمكن تمرير الوسائط إليها بالطريقة المذكورة كما يمكن استخدامها بدون تمرير وسائط، أما تمرير الوسائط بشكل مباشر فهي كما ذكرت طريقة غير آمنة إذا كانت هذه الوسائط مرتبطة بمدخلات المستخدم User Inputs.
استخدام قاعدة بيانات أخرى
يمكن استخدام أية قاعدة بيانات أخرى غير الافتراضية عبر تحديد اسم نص الاتصال لقاعدة البيانات بكل سهولة عند استدعاء الفئة SqlServerDb
SqlServerDb schoolDb = new SqlServerDb("OrdersDB");معالجة الاستثناءات Exceptions
عند حدوث أي خطأ لأي سبب كان عند استدعاء أي دالة من دوال المكتبة سوف يتم رفع استثناء من نوع DataAccessException يحتوي على تفاصيل الخطأ ويمكن معالجة هذا الخطأ بالطريقة المناسبة ، مثال على ذلك:
try
{
SqlServerDb schoolDb = new SqlServerDb();
ArrayList arSqlParameters = new ArrayList();
arSqlParameters.Add(new SqlParameter("@StudentID", 2));
SqlDataReader reader = schoolDb.ExecuteReader("SELECT * FROM Students Where StudentID = @StudentID", arSqlParameters);
if (reader.HasRows)
{
while (reader.Read())
{
Response.Write(reader[0] + "-" + reader[1] + "<br/>");
}
}
reader.Close();
}
catch (DataAccessException exp)
{
Response.Write("Error Message: " + exp.Message);
}برمجة المشروع
ربما يتفاجأ من يقوم بالاطلاع على برمجة المشروع من صغر حجم المشروع، وربما يعتبره عمليا ليس مشروع حيث لا يتعدى عدد أسطر البرمجة فيه 250 سطر، وجهة نظري هي أن أهمية الفكرة وفائدتها والتطبيق العملي لهذه الفكرة هي العوامل التي تحدد نجاح المشروع من عدمه وليس فقط عدد أسطر البرمجة فيه. هناك الكثير من التطويرات والإضافات الممكنة لهذا المشروع فضلا عن الاصلاحات والتعديلات الممكن تطبيقها عليه ولكني فضلت عدم التأخر في طرح هذا المشروع، ما أتمناه هو أن استمع إلى وجهة نظر المبرمجين وتعليقاتهم حول المشروع، ولي عودة بعد ذلك إن شاء الله، يمكنكم تنزيل ملف المشروع لوحده واستخدامه وهو DataAccessBase.dll وطبعا لمن يحب الاطلاع على برمجة المشروع ملفات المشروع مرفقة كذلك.