بسم الله الرحمن الرحيم
وصلى الله على محمد وآله الطاهرين
السلام عليكم ورحمة الله وبركاته
كل عام وأنتم بخير بمناسبة عيد الفطر المبارك أعاده الله علينا وعليكم بالخير والبركة
وصلى الله على محمد وآله الطاهرين
السلام عليكم ورحمة الله وبركاته
كل عام وأنتم بخير بمناسبة عيد الفطر المبارك أعاده الله علينا وعليكم بالخير والبركة
مقدمة:
بحول الله تعالى سنتكلم في هذا الموضوع عن تعدد الطبقات وأهميتها في تصميم التطبيقات.
تعريف:
N-Tier: تعني بأن هيكلية التطبيق تتألف من عدة طبقات تتعامل مع بعضها البعض بنسق واضح, وكل طبقة تكون مسؤولة عن عمل معين ولها وظيفتها.
إن اغلب التطبيقات تنقسم الى ثلاث أجزاء تعرف بـ 3Tier وهذه الطبقات هي:
1- طبقة العرض. (Presentation Layer)
2- طبقة الاعمال. Business Layer) )
أ- طبقة الخصائص. (طبقة جزئية من طبقة الاعمال)
3- طبقة البيانات. (Data Layer)
المخطط التالي يببن ذلك:
لنشرح هذه الطبقات بالتفصيل:
1- طبقة العرض (Presentation Layer):
وهي طبقة توفر واجهة رسومية للمستخدم حيث تستخدم لعرض البيانات والتعامل معها, وهذه الطبقة الوحيدة التي تكون في تفاعل مباشر مع المستخدم.
وظيفتها تمرير البيانات المدخلة الى الطبقة الوسطى (طبقة الاعمال)أو لجلب البيانات المعالجة منها.
طبقة العرض كما بينا توفر واجهة رسومية كأن تكون تطبيقات ويندوز أو ويب وهذه التطبيقات تتكون من عناصر التحكم مثل TextBox, CommandButton أو أي اداة أخرى.
2- طبقة الاعمال (Business Layer ):
طبقة تكتب فيها الدوال أو الوظائف أو أي عمليات أخرى تعمل كوسيط بين طبقة العرض وطبقة البيانات, وظيفتها نقل البيانات بين طبقتي العرض والبيانات.
وهي عبارة عن فئة تحتوي على دوال تقوم بجلب البيانات المدخلة من طبقة العرض وتقوم بارسالها الى طبقة البيانات.
أ- طبقة الخصائص: طبقة جزئية من طبقة الاعمال تكتب فيها المتغيرات المقابلة للحقول في الجدول في قاعدة البيانات. حيث تستخدم هذه الطبقة كحاوية للبيانات وذلك لتمرير البيانات المدخلة أو المعالجة بين الطبقات.
*المدخلة من قبل المستخدم والمعالجة من قبل طبقة البيانات.
3- طبقة البيانات (Data Layer):
طبقة تقوم بتمرير البيانات من والى قاعدة البيانات وتتعامل فقط مع الطبقة الوسطى (طبقة الاعمال), وهي الجزء الوحيد التي تكون في تماس مباشر مع قاعدة البيانات حيث تشمل كل ما يتعلق من عمليات على قاعدة البيانات من كتابة الاستعلامات او استخدام الدوال او الاجراءات المخزنة.
وهي عبارة عن فئة تقوم بجلب البيانات من الطبقة الوسطى وتمريرها الى قاعدة البيانات أو تقوم بجلب البيانات من قاعدة البيانات وتمريرها الى الطبقة الوسطى.
فوائدها: أهم الفوائد
1- إعادة الاستخدام: إن من أهم فوائد الطبقات هو المقدرة على استخدام الطبقات في تطبيقات اخرى وخاصة طبقة الاعمال.
2- سهولة الصيانة: بما أن كل طبقة مستقلة عن الاخرى لذا التعديل على على أي طبقة لن يؤثر في الاخرى بصورة كبيرة. مثلا التعديل على طبقة البيانات لن يؤثر عل طبقة الاعمال والتعديل على طبقة العرض لن يؤثر على طبقتي الاعمال والبيانات.
3- من الممكن كتابة الطبقات بلغة مختلفة واستخدامها مع لغة اخرى. مثلا كتابة طبقتي العرض والبيانات بلغة VB واستخدامها في برنامج مكتوب بلغة C# وهذا مفيد جدا في نظام عمل الفرق حيث فريق يتعامل مع الطبقات وفريق يتعامل مع الواجهة بغض النظر عن اللغة كأن تكون المجموعة الاولى التي اسندت اليها تطوير طبقتي الاعمال والبيانات تتعامل بلغة VB والمجموعة التي تطور الواجهة تتعامل بلغة C#. برأيي هذه من أهم الفوائد.
عيوبها: أهم العيوب
1- هيكليتها معقدة وصعبة.
2- تأخذ المزيد من الوقت.
بصورة عامة أن التعامل مع هكذا هيكلية ممتع جدا واحترافي في نفس الوقت, صح أن انهائها تأخذ الكثير من الوقت لكن تقسيم التطبيق الى طبقات كل طبقة تؤدي وظيفتها تسهل للمستخدم التعامل مع التطبيق, بحث في آخر الامر سوف تتعامل فقط مع الدوال أو اسمائها بالاحرى والبيانات الممررة اليها.
لتوضيح ما سبق من شرح سنقوم بعمل مثال يبين ذلك بلغة #C
نعمل مشروع جديد من نوع Windows Application ونسميه Student_Project ونصمم النموذج بهذا الشكل:
ونغير اسم النموذج الى MyForm
بعد أن ننتهي من تصميم النموذج ننتقل الى انشاء الطبقات لذلك سوف نقوم ببناء طبقة البيانات ومن ثم نتدرج الى طبقة الاعمال ومنه الى تكملة طبقة العرض.
1- بناء الجزء الاول من طبقة البيانات (Data Layer) وهي انشاء قاعدة البيانات:
من الـ Server Explorer نقوم بانشاء قاعدة بيانات SQL ولتكن اسمها Sample في مجلد المخرجات للمشروع Student_Project ثم نقوم باضافة جدولين باسماء tblStudent و tblProject ونقوم باضافة الحقول اليها ونضبط خصائصها كما في الشكل التالي:
مع جعل الـSid والـPid كـ Identity Column وذلك من نافذة الـProperties التي على اليمين.
ولعمل علاقة بين الجدولين نتبع الخطوات التي في الصورة التالية:
1- كلك يمين على جدول tblProject.
2- اضغط على Relationships.
3- اضغط Add.
4- اضغط على Table And Columns Specification ستظهر نقاط صغير اضغط عليها.
5- من هذه النافذة نحدد الجدول الرئيسي والجدول الثانوي وكذلك الحقل الرئيسي والثانوي.
نفتح الـ INSERT And UPDATE Specification ونجعل Delete Rule و Update Rule الى Casecade لغرض تطبيق التغييرات على الجدول الثانوي.
بعد الانتهاء من انشاء العلاقة بين الجدولين نقوم بانشاء View باسم tblView بالشكل التالي:
1- كلك يمين على Views.
2- نضغط على Add New View.
3- نختار جدول tblStudent ثم Add.
4- نختار جدول tblProject ثم Add.
5- نختار الحقول كما في الصورة ثم نخزن الـView باسم tblView.
الغرض من انشاء View هو انشاء استعلام جاهز يربط بين الجدولين لغرض البحث وجلب البيانات, بمعنى عند انشاءنا للاجراءات المخزنة سوف لن نضطر الى ربط الجدولين ببعض أي سوف لن نستعلم من الجدولين في عملية البحث وجلب البيانات بل نعتمد على الـView ونستعلم من الـView فقط.
الان نأتي الى الاجراءات المخزنة:
سوف ننشأ 7 اجراءات مخزنة للتعامل مع قاعدة البيانات تشمل الاضافة التعديل الحذف البحث واستيراد البيانات.
لانشاء اجراء مخزن نتبع الخطوات التالية:
وهذه الاجراءات هي :
1- اجراء اضافة طالب:
PHP كود :
CREATE PROCEDURE dbo.spInsertStudent
(
@sname nvarchar(20)
)
AS
insert into tblStudent (sname) values (@sname)
RETURN
2- اجراء اضافة مشروع:
PHP كود :
CREATE PROCEDURE dbo.spInsertProject
(
@sid int,
@pname nvarchar(20)
)
AS
insert into tblProject (sid, pname) values (@sid, @pname)
RETURN
3- اجراء الحذف:
PHP كود :
CREATE PROCEDURE dbo.spDelete
(
@sid int
)
AS
delete from tblStudent where sid = @sid
RETURN
4- اجراء تعديل طالب:
PHP كود :
CREATE PROCEDURE dbo.spUpdateStudent
(
@sid int,
@sname nvarchar(20)
)
AS
update tblStudent set sname = @sname where sid = @sid
RETURN
5- اجراء تعديل مشروع:
PHP كود :
CREATE PROCEDURE dbo.spUpdateProject
(
@pid int,
@sid int,
@pname nvarchar(20)
)
AS
update tblProject set sid = @sid, pname = @pname where pid = @pid
RETURN
6- اجراء جلب البيانات:
PHP كود :
CREATE PROCEDURE dbo.spGetAllData
AS
select * from tblView
RETURN
7- اجراء البحث باسم الطالب:
PHP كود :
CREATE PROCEDURE dbo.spSearchByName
(
@sname nvarchar(20)
)
AS
select * from tblView where sname like @sname
RETURN
لاحظ اخي العزيز أننا في الاجرائين البحث واستيراد البيانات استعلمنا من الـView.
بعد الانتهاء من انشاء الاجراءات المخزنة نضيف الى المشروع مشروع جديد من نوع Class Library بالشكل التالي:
كلك يمين على الـSolution ثم Add ثم New Project واختر Class Library غير الاسم الى MyDlls, بعد اضافة المشروع امسح الفئة الافتراضية التي هي باسم Class1 من الـSolution Explorer.
نضيف مجلدين الى MyDlls باسماء MyClasses و LinqClass بعد اضافة المجلدين نضيف ثلاث فئات الى المجلد MyClasses عن طريق كلك يمين على المجلد ومن ثم اختيار Class
الاول باسم BLayer والثاني باسم DLayer والثالث باسم Variables بعدها كلك يمين على مجلد LinqClass ومن ثم Add ثم New Item ثم Data ثم أختر LINQ To SQL Classes ثم Add بدون تغيير الاسم (DataClasses1.dbml)
نفتح مجلد الـLinqClass ثم نضغط مرتين على DataClasses.dbml لتنفتح لان منصة LINQ ثم نضغط على منصة LINQ (أي المساحات الفارغة), ونغير اسم الـLINQ الى DataBase عن طريق الـProperties من اليمين.
الان نقوم بسحب الجداول والاجراءات الى منصة LINQ بالشكل التالي:
لاحظ يتم ادراج الجداول والاجراءات الى المنصة عن طريق السحب والافلات.
وعند سحب الجداول سيتم انشاء نص الاتصال في الـSettings باسم SampleConnectionString.
الان سوف نبدأ بالعمل على طبقة البيانات بجزئها الثاني:
كما لاحظت في ما سبق انتهينا من الجزء الاول لطبقة البيانات وذلك بانشاء قاعدة البيانات والجداول والاجراءات
لذا سنبدأ بالجزء الثاني:
الفئة التالية وهي الفئة DLayer تمثل طبقة البيانات بجزئها الثاني:
PHP كود :
using System.Collections.Generic;
using System.Linq;
using MyDLLs.LinqClass;
using MyDLLs.Properties;
namespace MyDLLs.MyClasses
{
public class DLayer
{
#region المتغيرات
private readonly string _constr = Settings.Default.SampleConnectionString;
private DataBase _dbo;
private List<Variables> _vv;
#endregion
#region الاتصال
public void Connect()
{
_dbo = new DataBase(_constr);
}
#endregion
#region اضافة طالب
public int InsertStudent(Variables v)
{
_dbo.spInsertStudent(v.Sname);
// return (from st in dbo.tblStudents
// select st.sid).ToList().Last();
// or
return _dbo.tblStudents.Select(id => id.sid).ToList().Last();
}
#endregion
#region اضافة مشروع
public void InsertProject(Variables v)
{
_dbo.spInsertProject(v.Sid, v.Pname);
}
#endregion
#region تعديل طالب
public void UpdateStudent(Variables v)
{
_dbo.spUpdateStudent(v.Sid, v.Sname);
}
#endregion
# region تعديل مشروع
public void UpdateProject(Variables v)
{
_dbo.spUpdateProject(v.Pid, v.Sid, v.Pname);
}
#endregion
#region حذف طالب
public void DeleteStudent(Variables v)
{
_dbo.spDelete(v.Sid);
}
#endregion
#region جلب كافة البيانات
public List<Variables> SelectAll()
{
_vv = new List<Variables>();
foreach (var item in _dbo.spGetAllData())
{
_vv.Add
(
new Variables
{
Sid = item.sid,
Pid = item.pid,
Sname = item.sname,
Pname = item.pname
}
);
}
return _vv;
}
#endregion
#region البحث باسم الطالب
public List<Variables> SearchStudent(Variables v)
{
_vv = new List<Variables>();
foreach (var name in _dbo.spSearchByName(v.Sname))
{
_vv.Add
(
new Variables
{
Sid = name.sid,
Pid = name.pid,
Sname = name.sname,
Pname = name.pname
}
);
}
return _vv;
}
#endregion
// طبقة البيانات
// Linq باستخدام الاجراءات المخزنة وتقنية
}
}
لنبدأ بتوضيح الفئة DLayer خطوة خطوة:
لاحظ مجالات الاسماء التالية:
PHP كود :
using System.Collections.Generic;
using System.Linq;
using MyDLLs.LinqClass;
using MyDLLs.Properties;
الاول لغرض استخدام القوائم الثاني لاستخدام استعلامات LINQ والثالث لاستخدام الفئة DataBase لان هذه الفئة داخل المجلد LinqClass والبرنامج يعتبر المجلد كـNameSpace والرابع للوصول الى الـSettings.
المتغيرات:
PHP كود :
#region المتغيرات
private readonly string _constr = Settings.Default.SampleConnectionString;
private DataBase _dbo;
private List<Variables> _vv;
#endregion
الاول لتعيين نص الاتصال, كما اسلفنا عند وضع الجداول الى منصة LINQ يتم انشاء الاتصال في الSettings.
الثاني متغير من نوع DataBase للوصول الى الجداول والاجراءات التي في منصة الـLINQ الرابع متغير من الطبقة الجزئية من طبقة الاعمال لغرض البحث والتصفح.
شرح الدوال:
الدالة Connect لغرض الاتصال.
بقية الدوال تأخذ مدخل من نوع Variables لغرض احتواء البيانات وتوصيلها بين الطبقات كما بينا سابقا, ما عدا دالة الـSelectAll لا تأخذ أي مدخل.
وبما أننا نتعامل مع الاجراءا المخزنة لذا سوف لن نتعامل مع الدوال التالية:
PHP كود :
InsertOnSubmit
SubmitChanges
DeleteOnSubmit
بل سوف نستدعي الاجراءا المخزنة لانها اصبحت جزءا من دوال الـLINQ.
لذا كما ترى في عمليات الاضافة والحذف والتعديل نستدعي الاجراءات المخزنة ثم نمرر اليها المدخلات اللازمة فقط لا أكثر ولا أقل.
في دالة اضافة طالب جديد نقوم بعد الاضافة بارجاع رقم الطالب الذي اضفناه الى جدول tblStudent لغرض اضافة الرقم الى جدول tblProject لانه يوجد علاقة بين الجدولين حسب رقم الطالب.
2-لننتقل الان الى الطبقة الوسطى طبقة الاعمال (BLayer):
اولا الطبقة الجزئية من طبقة الاعمال وهي طبقة المتغيرات أو الخصائص:
PHP كود :
namespace MyDLLs.MyClasses
{
public class Variables
{
// طبقة جزئية من طبقة الاعمال
#region الخصائص
public int Sid { set; get; }
public int Pid { set; get; }
public string Sname { set; get; }
public string Pname { set; get; }
#endregion
}
}
وهي طبقة تضم المتغيرات (الحاويات) التي سوف تنقل البيانات بين الطبقات.
طبقة الاعمال (BLayer):
PHP كود :
using System.Collections.Generic;
namespace MyDLLs.MyClasses
{
public class BLayer
{
// طبقة الاعمال
private readonly DLayer _dl;
public BLayer()
{
_dl = new DLayer();
}
#region الاتصال
public void Connect()
{
_dl.Connect();
}
#endregion
#region جلب كل البيانات
public List<Variables> SelectAll()
{
return _dl.SelectAll();
}
#endregion
#region البحث بالاسم
public List<Variables> Search(Variables v)
{
return _dl.SearchStudent(v);
}
#endregion
#region اضافة
public void Insert(Variables v)
{
v.Sid = _dl.InsertStudent(v);
_dl.InsertProject(v);
}
#endregion
#region تعديل
public void Update(Variables v)
{
_dl.UpdateStudent(v);
_dl.UpdateProject(v);
}
#endregion
#region حذف
public void Delete(Variables v)
{
_dl.DeleteStudent(v);
}
#endregion
}
}
هذه الطبقة تضم متغير من نوع DLayer لغرض الوصول الى دوال طبقة البيانات, لانه وكما بينا هي الطبقة الوحيدة التي تتعامل مع طبقة البيانات (DLayer).
دوال طبقة الاعمال:
دالة بناء وفيه ننشء Instance من الفئة DLayer وبقية الدوال واضحة ولاتحتاج الى شرح
هنالك ملاحظة صغير في دالة الـInsert في هذا السطر
PHP كود :
v.Sid = _dl.InsertStudent(v);
3- أخيرا ننتقل الى طبقة العرض:
اولا ننشئ مجلد جديد في Students_Projects نسميه MyFroms ثم ننقل MyForm.cs و Program.cs الى المجلد
ثم نعمل Build لـMyDlls بعد عمل ذلك ننتقل الى Student_Project ثم نضيف ملف الـdll اليه عن طريق كلك يمين على References للـStudent_Project ومن ثم اختيار MyDlls ومن ثم نضبفه الى الكود بالشكل التالي:
PHP كود :
using MyDLLs.MyClasses;
لاحظ أنه الفئات كلها داخل مجلد MyClasses لذا يجب ذكر ذلك فبدونها لن نستطيع أن تستخدم الفئات.
المتغيرات:
PHP كود :
#region المتغيرات العامة
private List<Variables> _vList;
private readonly Variables _v;
private readonly BLayer _bl;
private int _pid, _pos;
#endregion
شرح الكود:
1- قائمة من نوع Variables لتصفح البيانات.
2- متغير من فئة Variables لاحتواء البيانات التي ستمرر الى طبقة الاعمال.
3- متغير من فئة BLayer لاستدعاء دوال الطبقة الوسطى.
4- متغيرين من نوع int
ونضع هذا الكود في دالة البناء أو في حدث الـLoad للـ Form
PHP كود :
_v = new Variables();
_vList = new List<Variables>();
_bl = new BLayer();
_bl.Connect();
الدوال العامة:
PHP كود :
#region الدوال
private void Fill(List<Variables> vv, int pos)
{
if (vv.Count <= 0) return;
idtxt.Text = vv[pos].Sid.ToString();
nametxt.Text = vv[pos].Sname;
pnamecbx.Text = vv[pos].Pname;
_pid = vv[pos].Pid;
}
private void FillDataGrid(List<Variables> vv)
{
dataGridView1.DataSource = vv;
dataGridView1.Columns.RemoveAt(1);// GridView نحذف رقم المشروع لعدم عرضه في الـ
dataGridView1.Columns[0].HeaderText = @"التسلسل";
dataGridView1.Columns[1].HeaderText = @"الاسم";
dataGridView1.Columns[2].HeaderText = @"المشروع";
}
private void ShowData()
{
_vList = _bl.SelectAll();
///////////////////////////////////////////////////
FillDataGrid(_vv);
Fill(_vList, _pos);
}
#endregion
شرح الكود:
الدالة الاولى لغرض عرض البيانات في الـFrom والدالة الثانية لعرض البيانات في الـDataGridView
والدالة الثالثة لاستدعاء دالة SelectAll ودالتي العرض.
عرض البيانات:
PHP كود :
private void Showbtn_Click(object sender, EventArgs e)
{
ShowData();
}
اضافة البيانات:
PHP كود :
private void Addbtn_Click(object sender, EventArgs e)
{
try
{
_v.Sname = nametxt.Text;
_v.Pname = pnamecbx.Text;
///////////////////////////////////////
_bl.Insert(_v);
////////////////////////
ShowData();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
[u]شرح الكود:[/u]
لاحظ كيف يتم احتواء البيانات من قبل الفئة Variables المتمثل بالمتغير _v ومن ثم يتم تمريرها الى دالة الـInsert.
[u]تعديل البيانات:[/u]
PHP كود :
private void Updatebtn_Click(object sender, EventArgs e)
{
try
{
_v.Sid = int.Parse(idtxt.Text);
_v.Sname = nametxt.Text;
_v.Pid = _pid;
_v.Pname = pnamecbx.Text;
/////////////////////////////////////////
_bl.Update(_v);
/////////////////////
ShowData();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
شرح الكود:
مثل الكود السابق يتم احتواء البيانات وبعدها يتم ارسالها الى الدالة Update.
حذف البيانات:
PHP كود :
private void Deletebtn_Click(object sender, EventArgs e)
{
if (_bl.SelectAll().Count == 0) return;
if (_pos < 0) return;
_v.Sid = _vList[_pos].Sid;
_bl.Delete(_v);
_pos--;
if (_pos < 0)
_pos = 0;
//////////////
ShowData();
}
شرح الكود:
في البداية يتم التأكد من عدم خلو الجدول من البيانات وبعد ذلك يتم الحذف على اساس الـSid والتي يتم جلبه من خلال القائمة List<Variables> المتمثلة بالمتغير _vList .
البحث:
PHP كود :
private void Searchbtn_Click(object sender, EventArgs e)
{
if (srchtxt.Text != "")
{
_v.Sname = "%" + srchtxt.Text + "%";
_vv = _bl.Search(_v);
FillDataGrid(_vv);
Fill(_vv, _pos);
}
else
{
MessageBox.Show(@"الحقل فارغ ,رجاءا ادخل اسما", @"تنبيه");
}
}
شرح الكود:
لاحظ كيف يتم احاطة كلمة البحث بعلامتي % وذلك لاستتخدامنا لـ Like Clause في عملية البحث بالاجراء المخزن الخاص بالبحث.
مسح محتوى الـTextBox:
PHP كود :
private void deletecontent_Click(object sender, EventArgs e)
{
foreach (Control c in setting.Controls)
{
if (c.GetType() == typeof(TextBox))
{
var t = (TextBox)c;
t.Clear();
}
if (c.GetType() != typeof (ComboBox)) continue;
var combo = (ComboBox) c;
combo.Text = "";
}
}
أكواد السجل الاول, التالي, السابق, الاخير:
PHP كود :
private void nextbtn_Click(object sender, EventArgs e)
{
if (_pos >= _vv.Count - 1) return;
_pos++;
Fill(_vv, _pos);
}
private void prevbtn_Click(object sender, EventArgs e)
{
if (_pos <= 0) return;
_pos--;
Fill(_vv, _pos);
}
private void lastbtn_Click(object sender, EventArgs e)
{
_pos = _vv.Count - 1;
Fill(_vv, _pos);
}
private void firstbtn_Click(object sender, EventArgs e)
{
_pos = 0;
Fill(_vv, _pos);
}
المثال في المرفقات بلغة #C واوعدكم انني باذن الله سوف اعمل مثال بلغة VB.NET وارفقه حال انتهائي منه في هذا الموضوع.
وإني بانتظار ملاحظاتكم واستفساراتكم القيمة.
اسأل الله أن أكون قد وفقت لتوصيل الفكرة اليكم ,ولا تنسوني و والدي من صالح دعواتكم.
والسلام عليكم ورحمة الله وبركاته