تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
الدرس الثالث والستون - عالم ADO.net وما يتعلق به - الجزء الثالث
#1
كاتب الموضوع : أحمد جمال

بسم الله الرحمن الرحيم .
السلام عليكم ورحمة الله وبركاته .

في هذا الدرس سنحاول التعرف على ماهية الوضع المتصل Connected Layer في ADO.net .
يسمح لك الوضع المتصل Connected Layer والذي يعتمد على وجود اتصال مفعل بقاعدة بياناتك اثناء عمليات التعديل بالتعامل مع قاعدة بياناتك من خلال connection للاتصال و command لتنفيذ الاستعلامات اضافة إلى data reader الخاصة بال DataProvider الذي تنوي التعامل معه وفي حالتنا هذه هو ال SQL .

سنقوم الآن بعمل تطبيق بسيط لعرض كافة اسماء الموظفين في قائمة ، لذا قم بالمتابعة معنا خطوة بخطوة :

1- جلب المكتبات التي سوف نتعامل معها وهي في حالتنا هذه System.Data.SqlClient :
C#:

كود :
using System.Data.SqlClient;

vb.net:

كود :
Imports System.Data.SqlClient


2- التعامل مع Connection من اجل الوصول إلى قاعدة البيانات الخاصة بنا وفتحها :
C#:

كود :
SqlConnection cn = new SqlConnection();

كود :
[SIZE=3] cn.ConnectionString = @"Data Source=AHMED-PC\SQLEXPRESS;Initial Catalog=Employee;Integrated Security=True;Pooling=False";[/SIZE]
[SIZE=3]cn.Open();[/SIZE]
vb.net:

كود :
Dim cn As New SqlConnection()

كود :
[SIZE=3]cn.ConnectionString = "Data Source=AHMED-PC\SQLEXPRESS;Initial Catalog=Employee;Integrated Security=True;Pooling=False" [/SIZE]
[SIZE=3]cn.Open() [/SIZE]
*** كيف تعرف ال ConnectionString .
أ- هناك العديد من المواقع التي تقدم لك خدمة معرفة ال ConnectionString مثل موقع : www.connectionstrings.com .
ب- لو كنت تعمل من خلال قاعدة بيانات داخل بيئة عمل .net يمكنك معرفة ال ConnectioString لها باختيارها من قائمة Server Explorer ومن ثم مشاهدة ال Properties بالشكل التالي :


ج- باستخدام اي من أدوات .net الجاهزة والتي سنتعرف على بعض منها في مراحل قادمة .

3- كتابة جملة استعلام بسيطة لطلب كل أسماء الموظفين الأولى + الثانية على شكل fullname بالاضافة إلى العمر - لو لم تفهم هذه الجملة راجع دروس TSQL - :
C#:

كود :
[SIZE=3]string strSQL = "Select [First Name]+[Last Name] as [Full Name], Age From Employee_info";[/SIZE]
[SIZE=3]SqlCommand myCommand = new SqlCommand(strSQL, cn);[/SIZE]
vb.net:

كود :
[SIZE=3]Dim strSQL As String = "Select [First Name]+[Last Name] as [Full Name], Age From Employee_info"
Dim myCommand As New SqlCommand(strSQL, cn) [/SIZE]
4- تعريف DataReader لقراءة البيانات وتنفيذها على شكل ExecuteReader :

C#:

كود :
[SIZE=3]SqlDataReader myDataReader;[/SIZE]
[SIZE=3]myDataReader = myCommand.ExecuteReader();[/SIZE]
vb.net:

كود :
[SIZE=3]Dim myDataReader As SqlDataReader [/SIZE]
[SIZE=3]myDataReader = myCommand.ExecuteReader() [/SIZE]
5- الدوران على كافة الحقول ووضعها في ListBox1 :
C#:

كود :
[SIZE=3]string listItem = "";[/SIZE]
[SIZE=3]while (myDataReader.Read())[/SIZE]
[SIZE=3]{[/SIZE]
[SIZE=3]listItem= "Full Name: " + myDataReader["Full Name"].ToString() + " Age: " + myDataReader["Age"].ToString();[/SIZE]
[SIZE=3]listBox1.Items.Add(listItem);[/SIZE]
[SIZE=3]}[/SIZE]
vb.net:

كود :
[SIZE=3]Dim listItem As String = "" [/SIZE]
[SIZE=3]While myDataReader.Read() [/SIZE]
[SIZE=3]listItem = "Full Name: " + myDataReader("Full Name").ToString() + " Age: " + myDataReader("Age").ToString() [/SIZE]
[SIZE=3]listBox1.Items.Add(listItem) [/SIZE]
[SIZE=3]End While [/SIZE]
سيكون ناتج تنفيذ هذا الكود شيئاً مثل هذا :


6- اغلاق وسائل الاتصال وعدم تركها مفتوحة :
C#:

كود :
myDataReader.Close();

كود :
[SIZE=3] cn.Close();[/SIZE]
vb.net:

كود :
myDataReader.Close()

كود :
[SIZE=3] cn.Close()[/SIZE]
*** هناك حل آخر لفتح قاعدة البيانات واغلاقها مباشرة بعد انتهاء تنفيذ المهمة ، اثناء عملك ExecuteReader يمكنك تحديد CommandBehavior يحدث بعد اغلاقها ، يحتوي هذا ال Enum على الخيارات التالية :


الخيار الأول هو فقط ما يهمنا ، في هذه الحالة سيكون شكل الكود بالشكل التالي :
C#:


كود :
[SIZE=3]myDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);[/SIZE]
vb.net:

كود :
[SIZE=3]myDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection)[/SIZE]
هنا لن نقلق مجدداً بشأن اغلاق ال Coonection حيث سيتم اغلاقه تلقائياً بعد كتابة هذا السطر :
C#:

كود :
[SIZE=3] myDataReader.Close()[/SIZE]


vb.net:


myDataReader.Close()
- طبعاً لا تنس أن بامكانك عمل نفس جملة الاستعلام Update او Delete أو Insert بدلاً من Select ، وحسب جملة الاستعلام المطلوبة .
}}}
تم الشكر بواسطة:
#2
- التعامل مع ConnectionStringBuilder :

يوفر لك هذا الكائن طريقة تفصيلية لبناء ال ConnectioString الخاص بك ، هذا المثال يوضح الأكثر استخداماً :
C#:

كود :
[SIZE=3]SqlConnectionStringBuilder cnStrBuilder = new SqlConnectionStringBuilder();
cnStrBuilder.InitialCatalog = "Employee";
cnStrBuilder.DataSource = @"(local)\SQLEXPRESS";
cnStrBuilder.ConnectTimeout = 30;
cnStrBuilder.Password = "124";
cnStrBuilder.UserID = "Ahmed";[/SIZE]
[SIZE=3]SqlConnection cn = new SqlConnection();
cn.ConnectionString = cnStrBuilder.ConnectionString;
cn.Open();[/SIZE]

vb.net:

كود :
[SIZE=3]Dim cnStrBuilder As New SqlConnectionStringBuilder()
cnStrBuilder.InitialCatalog = "Employee"
cnStrBuilder.DataSource = "(local)\SQLEXPRESS"
cnStrBuilder.ConnectTimeout = 30
cnStrBuilder.Password = "124"
cnStrBuilder.UserID = "Ahmed" [/SIZE]
[SIZE=3]Dim cn As New SqlConnection()
cn.ConnectionString = cnStrBuilder.ConnectionString
cn.Open() [/SIZE]


حيث تجد :

- InitialCatalog لتحديد اسم قاعدة البيانات
- DataSource لتحديد مسار قاعدة البيانات
- ConnectTimeout لتحديد الوقت الذي يمكن استغراقه من اجل محاولة الوصول إلى قاعدة البيانات او ايقاف العملية عند انتهاءه .
- Password كلمة مرور قاعدة البيانات إن وجدت .
- UserID اسم المستخدم لقاعدة البيانات إن وجد .


هناك العديد من العناصر الأخرى أيضاً يمكنك استعراضها من هنا :
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder_m embers.aspx
}}}
تم الشكر بواسطة:
#3
التعامل مع الفئة Command :

ال Command هي جملة الاستعلام التي نستخدمها لتنفيذ اي نوع من العمليات على قاعدة البيانات ، تنقسم إلى ثلاث انواع رئيسية :

- StoredProcedure
- TableDirect
- Text


في المثال السابق استخدمنا ال Command كجملة استعلام نصية مباشرة Text ، كانت بالشكل التالي مثلاً :
C#:

كود :
string strSQL = "Select * From Employee_Info";
SqlCommand myCommand = new SqlCommand(strSQL, cn);

vb.net:

كود :
[SIZE=3]Dim strSQL As String = "Select * From Employee_Info"
Dim myCommand As New SqlCommand(strSQL, cn)[/SIZE]

أو بهذا الشكل :
c#:

كود :
[SIZE=3]SqlCommand testCommand = new SqlCommand();
testCommand.Connection = cn;
testCommand.CommandText = strSQL;[/SIZE]

vb.net:

كود :
[SIZE=3]Dim testCommand As New SqlCommand()
testCommand.Connection = cn
testCommand.CommandText = strSQL [/SIZE]

اثناء التنفيذ قمنا بربطها مباشرة باستخدام ExecuteReader ، في الواقع هناك عدة طرق عدة طرق للتنتفيذ :

ExecuteReader : في حالة كون الناتج عدد كبير من البيانات ، يتم تعريف DataReader وربط الناتج به لقراءته ، وهو ما تعرفنا عليه في مراحل سابقة .
ExecuteNonQuery : في حالة عدم وجود نواتج اصلاً ، مثل تعريف عملية update او Delete حيث الناتج الوحيد هو تنفيذ العملية من عدمه ، سيتم التعرف عليه لاحقاً .
ExecuteScalar : في حالة كون الناتج وحيد ، مثل الاستعلام عن فقط عن الاسم الأول للشخص صاحب الرقم القومي xxxxxx .
ExecuteXmlReader : تنفيذ الناتج واعادته على شكل XML يتم تعريف .XmlReader وربط الناتج به لقراءته ، يمكنك الرجوع إلى دروس XML لمعرفة المزيد عن XmlReader .
}}}
تم الشكر بواسطة:
#4
- استخدام ال Parameterized Command Objects :

كما لاحظت من الدرس الذي تحدثنا فيه عن ال SQL Injection ، فإن الطريقة التقليدية لجمل الاستعلام تظل خطرة ، لذا نبدا باستخدام Parameters لجمل الاستعلام لدينا ايضاً حتى لو لم نكن نتعامل مع Stored Procedure .

لو أخذنا المثال التالي للتجربة :

C#:

كود :
[SIZE=3]string strSQL = "Select [First Name]+[Last Name] as [Full Name], Age From Employee where ID=" + ID;
SqlCommand myCommand = new SqlCommand(strSQL, cn);[/SIZE]

vb.net:

كود :
[SIZE=3]Dim strSQL As String = "Select [First Name]+[Last Name] as [Full Name], Age From Employee where ID=" + ID
Dim myCommand As New SqlCommand(strSQL, cn) [/SIZE]


ستجد اننا قادرين بمبادئ ال SQL Injection من اختراق هذا النظام بسهولة ، لذا البديل يكون باستخدام وتعريف SqlParameter:
C#:

كود :
[SIZE=3]string sql = string.Format("Select [First Name]+[Last Name] as [Full Name], Age From Employee where [/SIZE][SIZE=3]ID=@ID[/SIZE][SIZE=3]");
using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
// Fill params collection.
SqlParameter param = new SqlParameter();
param.ParameterName = "@ID";
param.Value = 10;
param.SqlDbType = SqlDbType.Int;
cmd.Parameters.Add(param);[/SIZE]
[SIZE=3]// تنفيذ جملة الاستعلام بأي شكل هنا
cmd.ExecuteNonQuery();
}[/SIZE]


vb.net:

كود :
Dim sql As String = String.Format("Select [First Name]+[Last Name] as [Full Name], Age From Employee where

كود :
[SIZE=3]ID=@ID[/SIZE][SIZE=3]")
Using cmd As New SqlCommand(sql, Me.sqlCn)
' Fill params collection.
Dim param As New SqlParameter()
param.ParameterName = "@ID"
param.Value = 10
param.SqlDbType = SqlDbType.Int
cmd.Parameters.Add(param)

' تنفيذ جملة الاستعلام بأي شكل هنا
cmd.ExecuteNonQuery()
End Using[/SIZE]

هنا كما لاحظت قمنا بتمرير باميترات إلى جملة استعلام قمنا نحن بكتابتها ضمن البرنامج .

- الحالة الثانية التي لدينا وهي الشائعة الاستخدام هي حالة تعريف Stored Procedure ، لو افترضنا مثلاً جملة الاستعلام التي أنشأناها في أول درس لنا بالشكل التالي :


كود :
[SIZE=3]ALTER PROCEDURE dbo.GetAge[/SIZE]
[SIZE=3]@condition int,
@firstname char(10) output[/SIZE]
[SIZE=3]AS[/SIZE]
[SIZE=3]SELECT @firstname=[First Name] from Employee_info where age > @condition[/SIZE]


وقمنا بحفظها باسم GetAge ، الآن نريد استدعاءها من البرنامج ، يتم ذلك بالشكل التالي مثلاً :

C#:

كود :
[SIZE=3]using (SqlCommand cmd = new SqlCommand("GetAge", cn))
{
cmd.CommandType = CommandType.StoredProcedure;[/SIZE]
[SIZE=3]SqlParameter param = new SqlParameter();
param.ParameterName = "@condition";
param.SqlDbType = SqlDbType.Int;
param.Value = myAge;
param.Direction = ParameterDirection.Input;
cmd.Parameters.Add(param);[/SIZE]

[SIZE=3]param = new SqlParameter();
param.ParameterName = "@firstnameName";
param.SqlDbType = SqlDbType.Char;
param.Size = 10;
param.Direction = ParameterDirection.Output;
cmd.Parameters.Add(param);[/SIZE]

[SIZE=3]cmd.ExecuteNonQuery();
MessageBox.Show(cmd.Parameters["@firstName"].Value.ToString());
}[/SIZE]

vb.net:

كود :
Using cmd As New SqlCommand("GetAge", cn)
cmd.CommandType = CommandType.StoredProcedure

Dim param As New SqlParameter()
param.ParameterName = "@condition"
param.SqlDbType = SqlDbType.Int
param.Value = myAge
param.Direction = ParameterDirection.Input
cmd.Parameters.Add(param)



param = New SqlParameter()
param.ParameterName = "@firstnameName"
param.SqlDbType = SqlDbType.[Char]
param.Size = 10
param.Direction = ParameterDirection.Output
cmd.Parameters.Add(param)



cmd.ExecuteNonQuery()
MessageBox.Show(cmd.Parameters("@firstName").Value.ToString())
End Using


كما لاحظت ، قمنا بتعريف نوع ال Command هنا نظراً لإن الافتراضي هو Text ، ومن ثم قمنا بتعريف متغير الدخول ومتغير الخروج أيضاً .
}}}
تم الشكر بواسطة:
#5
- التعامل مع DataReaders :

كما شاهدت في المثال السابق ، يمكن قراءة البيانات من DataReader بالشكل التالي مثلاً :
C#:

كود :
string listItem = "";
while (myDataReader.Read())
{
listItem= "Full Name: " + myDataReader["Full Name"].ToString() + " Age: " + myDataReader["Age"].ToString();
listBox1.Items.Add(listItem);
}

vb.net:

كود :
Dim listItem As String = ""
While myDataReader.Read()
listItem = "Full Name: " + myDataReader("Full Name").ToString() + " Age: " + myDataReader("Age").ToString()
listBox1.Items.Add(listItem)
End While


حيث تعود الدالة Read ب true ما دام هناك سجلات للقراءة ، وفي نفس الوقت مع كل استدعاء لها تنتقل إلى السجل التالي ...

طريقة القراءة تكون بتحديد الحقل المراد قراءته myDataReader["Age"] او بتحديد رقمه في الترتيب myDataReader[2] مثلاً .

الخاصية FieldCount تعطينا عدد النتائج المعادة ، لذا يمكننا تنفيذ نفس العملية السابقة بالشكل التالي :
c#:

كود :
for (int i = 0; i < myDataReader.FieldCount; i++)
{
listItem= "Full Name: " + myDataReader["Full Name"].ToString() + " Age: " + myDataReader["Age"].ToString();
listBox1.Items.Add(listItem);
}

vb.net:

كود :
For i As Integer = 0 To myDataReader.FieldCount - 1
listItem = "Full Name: " + myDataReader("Full Name").ToString() + " Age: " + myDataReader("Age").ToString()
listBox1.Items.Add(listItem)
Next


NextResult :

تمكنك ال DataReader من تعريف جملتي استعلام لاعادة الناتج ، فمثلاً لاعادة اسماء الموظفين ثم اسماء المشاريع :
C#:

كود :
[SIZE=3]string strSQL = "Select * From Employee_info;Select * from projects";
SqlCommand myCommand = new SqlCommand(strSQL, cn);[/SIZE]
[SIZE=3]do
{
while (myDataReader.Read())
{
for (int i = 0; i < myDataReader.FieldCount; i++)
listItem= "Data: " + myDataReader[0].ToString() ;
}
} while (myDataReader.NextResult());[/SIZE]
vb.net:

كود :
[SIZE=3]Dim strSQL As String = "Select * From Employee_info;Select * from projects"
Dim myCommand As New SqlCommand(strSQL, cn) [/SIZE]
[SIZE=3]Do
While myDataReader.Read()
For i As Integer = 0 To myDataReader.FieldCount - 1
listItem = "Data: " + myDataReader(0).ToString()
Next
End While
Loop While myDataReader.NextResult() [/SIZE]
}}}
تم الشكر بواسطة:
#6
Data Access Layer :

في التطبيقات الجدية ، لا يتم وضع الكود مع المظهر مع سيناريو وعمليات البرنامج اضافة لطبقة البيانات، بل يتم فصل كل منها في طبقة منفصلة وهو ما يعرف باسم Layers ، لمعرفة المزيد عن هذا الموضوع يمكنك مراجعة الرابط التالي :
http://www.al-asiri.com/ShowRecord.aspx?Action=Open&id=cefa426c-d9e0-4625-a66b-87fd6082ff89
وهناك تطبيق ايضاً هنا :
http://vb4arb.com/vb/showthread.php?824

في هذه المرحلة ، سنحاول عمل data layer تكون خاصة بالتعامل مع قواعد البيانات لتحقيق نقطتين مهمتين :

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


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

يمكنك عمل هذه الطبقة بعدة طرق ، ابسطها استخدام بعض البرامج الجاهزة التي تقوم بهذه العملية وتقوم بعمل استخراج لهذه ال layer حسب لغة البرمجة التي تحددها ، ايضاً يوفر لك الدوت نت طريقة لهذه العملية ، الطريقة الثانية هي الطريقة اليدوية ...

في العجالة التالية سنتعرف على مثال بسيط لهذه العملية من اجل الموظفين ، يمكن تطبيق نفس المفاهيم على البرامج الجدية لاحقا .

1- عمليات فتح واغلاق قواعد البيانات :
C#:

كود :
[SIZE=3]private SqlConnection cn = new SqlConnection();
public void OpenConnection(string connectionString)
{
cn.ConnectionString = connectionString;
cn.Open();
}
public void CloseConnection()
{
cn.Close();
}[/SIZE]

vb.net:

كود :
Private cn As New SqlConnection()
Public Sub OpenConnection(ByVal connectionString As String)
cn.ConnectionString = connectionString
cn.Open()
End Sub
Public Sub CloseConnection()
cn.Close()
End Sub

2- سيناريو عملية الحذف .
في هذه العملية سنتيح للمستخدم حذف الموظف باسمه ، او حذف الموظف برقمه ، او حذف الموظف مثلاً بدلالة العمر وبعدة اختيارات سواء اكبر او اقل او يساوي مثلاً مع استخدام مبدأ ال OverLoading ومبدأ عمل Enums ايضاً واللذان تم شرحهما في دروس سابقة .

*** لاحظ ان مهمتك في هذه المرحلة هي عمل كل الدوال التي يمكن استخدامها في البرنامج تحت اي ظرف من الظروف :
C#:

كود :
[SIZE=3]public void DeleteEmployee(int id)
{
string sql = string.Format("Delete from Employee where ID = {0}",
id);
using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
try
{
cmd.ExecuteNonQuery();
}
catch(SqlException ex)
{
Exception error = new Exception("some error occures: ", ex);
throw error;
}
}
}[/SIZE]

[SIZE=3]public void DeleteEmployee(string name)
{
string sql = string.Format("Delete from Employee where [First Name] = '{0}'",
name);
using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
try
{
cmd.ExecuteNonQuery();
}
catch(SqlException ex)
{
Exception error = new Exception("some error occures: ", ex);
throw error;
}
}
}[/SIZE]

[SIZE=3]enum deletecondition
{
morethan=0,
lessthan=1,
equal=2
}[/SIZE]
[SIZE=3]public void DeleteEmployee(int age,deletecondition delcondition)
{
string sql="";
if(delcondition == deletecondition.morethan)
sql = string.Format("Delete from Employee where age > {0}",age);
else if(delcondition == deletecondition.lessthan)
sql = string.Format("Delete from Employee where age < {0}",age);
else
sql = string.Format("Delete from Employee where age = {0}",age);[/SIZE]

[SIZE=3]using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
try
{
cmd.ExecuteNonQuery();
}
catch(SqlException ex)
{
Exception error = new Exception("some error occures: ", ex);
throw error;
}
}
}[/SIZE]

vb.net:

كود :
[SIZE=3]Public Sub DeleteEmployee(ByVal id As Integer)
Dim sql As String = String.Format("Delete from Employee where ID = '{0}'", id)
Using cmd As New SqlCommand(sql, Me.sqlCn)
Try
cmd.ExecuteNonQuery()
Catch ex As SqlException
Dim [error] As New Exception("some error occures: ", ex)
Throw [error]
End Try
End Using
End Sub [/SIZE]
[SIZE=3]Public Sub DeleteEmployee(ByVal name As String)
Dim sql As String = String.Format("Delete from Employee where [First Name] = '{0}'", name)
Using cmd As New SqlCommand(sql, Me.sqlCn)
Try
cmd.ExecuteNonQuery()
Catch ex As SqlException
Dim [error] As New Exception("some error occures: ", ex)
Throw [error]
End Try
End Using
End Sub[/SIZE]
[SIZE=3]enum deletecondition
{
morethan=0,
lessthan=1,
equal=2
}[/SIZE]

[SIZE=3]Public Sub DeleteEmployee(ByVal age As Integer, ByVal delcondition As deletecondition)
Dim sql As String = ""
If delcondition = deletecondition.morethan Then
sql = String.Format("Delete from Employee where age > {0}", age)
ElseIf delcondition = deletecondition.lessthan Then
sql = String.Format("Delete from Employee where age < {0}", age)
Else
sql = String.Format("Delete from Employee where age = {0}", age)
End If


Using cmd As New SqlCommand(sql, Me.sqlCn)
Try
cmd.ExecuteNonQuery()
Catch ex As SqlException
Dim [error] As New Exception("some error occures: ", ex)
Throw [error]
End Try
End Using
End Sub [/SIZE]


3- سيناريو عمليات الاضافة والتعديل :

بنفس الطريقة يمكن التعديل بعدة خيارات او الاضافة بعدة طرق .
بالنسبة لعملية الاضافة سنجبره على ادخال الاسم الأول والأخير فقط .
والتعديل ينبغي أيضاً أن يكون بنفس الصورة ، ولكن منعاً للاطالة سنعدل بدلالة الرقم الاسم الأول فقط .

C#:

كود :
[SIZE=3]public void InsertEmployee(string fname, string lname, int age)
{
// Format and execute SQL statement.
string sql = string.Format("Insert Into Employee_info" +
"([First Name], [Last Name]) Values" +
"('{0}', '{1}')",fname, lname);[/SIZE]
[SIZE=3]using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
cmd.ExecuteNonQuery();
}
}[/SIZE]

[SIZE=3]public void UpdateEmployee(int id, string newFirstName)
{[/SIZE]
[SIZE=3]string sql =
string.Format("Update Employee Set [First Name] = '{0}' Where ID = '{1}'",
newFirstName, id);
using(SqlCommand cmd = new SqlCommand(sql, this.sqlCn))
{
cmd.ExecuteNonQuery();
}
}[/SIZE]

vb.net:

كود :
[SIZE=3]Public Sub InsertEmployee(ByVal fname As String, ByVal lname As String, ByVal age As Integer)
' Format and execute SQL statement.
Dim sql As String = String.Format("Insert Into Employee_info" + "([First Name], [Last Name]) Values" + "('{0}', '{1}')", fname, lname)

Using cmd As New SqlCommand(sql, Me.sqlCn)
cmd.ExecuteNonQuery()
End Using
End Sub [/SIZE]

[SIZE=3]Public Sub UpdateEmployee(ByVal id As Integer, ByVal newFirstName As String)

Dim sql As String = String.Format("Update Employee Set [First Name] = '{0}' Where ID = '{1}'", newFirstName, id)
Using cmd As New SqlCommand(sql, Me.sqlCn)
cmd.ExecuteNonQuery()
End Using
End Sub [/SIZE]


4- سيناريو عمليات البحث .
قبل البدء في سيناريو عملية البحث ، نود أن نشير إن الدوال السابقة ينقصها شيء هام وهي عملية ال Parameters لتلافي المشاكل الناتجة عن ال Sql Injection ، لكن كانت الامثلة السابقة للتوضيح فقط ، في عملية البحث الآن سنطبق ما تعلمناه لحل هذه المشكلة .
سنجرب عملية بحث واحدة عن الاسم الأول والأخير للأشخاص برقم ID معين ، لا تنسى أنك مطالب في Data Layer بعمل كل الطلبات التي قد يحتاجها مبرمج عمليات البرنامج لكي لا يحتاج لكتابة حتى جملة استعلام واحدة .


سنقوم اولاً بعمل Stored Procedure :


كود :
[SIZE=3]CREATE PROCEDURE GetFirstNameByID
@id int,
@fName char(10) output
AS
SELECT @fName=[First Name] from Employee_info where ID > @id[/SIZE]

ومن ثم نقوم نعرف الدالة الخاصة بعملية البحث بالشكل التالي :

C#:

كود :
[SIZE=3]public string SelectName(int id)
{[/SIZE]
[SIZE=3]using (SqlCommand cmd = new SqlCommand("GetFirstNameByID", cn))
{
cmd.CommandType = CommandType.StoredProcedure;[/SIZE]
[SIZE=3]SqlParameter param = new SqlParameter();
param.ParameterName = "@id";
param.SqlDbType = SqlDbType.Int;
param.Value = ID;
param.Direction = ParameterDirection.Input;
cmd.Parameters.Add(param);[/SIZE]
[SIZE=3]param = new SqlParameter();
param.ParameterName = "@fName";
param.SqlDbType = SqlDbType.Char;
param.Size = 10;
param.Direction = ParameterDirection.Output;
cmd.Parameters.Add(param);[/SIZE]
[SIZE=3]cmd.ExecuteNonQuery();[/SIZE]
[SIZE=3]return cmd.Parameters["@fName"].Value.ToString();
}
return carPetName;
}[/SIZE]


vb.net:

كود :
myDataLayer example=new myDataLayer();
example.OpenConnection(myconnectionstring);
example.DeleteEmployee(10);
example.DeleteEmployee("Ahmed");


--- لاحقاً يمكنك فصل ال DataLayer حتى في dll منفصلة لضمان تشغيلها مع اكثر من تطبيق ، في الاستخدام لاحقاً وعلى افتراض ان الفئة Class لطبقة البيانات DataLayer تحمل الاسم myDataLayer ، ولتطبيق عملية مثل الحذف كل المطلوب منك هو سطرين مثل الآتي :
C#:

كود :
[SIZE=3]Dim example As New myDataLayer()
example.OpenConnection(myconnectionstring)
example.DeleteEmployee(10)
example.DeleteEmployee("Ahmed") [/SIZE]


vb.net:


Dim example As New myDataLayer() example.OpenConnection(myconnectionstring) example.DeleteEmployee(10) example.DeleteEmployee("Ahmed")


وفقط !!!

وبنفس النظام لو كانت dll قم باستيرادها في برنامجك ثم قم باستخدامها بنفس الطريقة .
لو لاحظت ، بهذه الطريقة اصبح المبرمج لسيناريو البرنامج ولباقي عملياته بعيداً كل البعد عن قواعد البيانات ، كما ان تقسيم العمل أصبح أوضح وبالتالي اصبح بالامكان تدارك المشاكل بصورة أوضح .
}}}
تم الشكر بواسطة:
#7
Asynchronous Data Access :

في هذه الجزئية سنحاول تعلم طريقة تمنعنا من عمل عدة عمليات على قواعد البيانات في نفس الوقت لمنع التضارب ، ومع ان المشكلة ستواجهنا بصورة اكبر في الوضع المنفصل Disconnected والذي هو موضوع درسنا القادم ، إلا اننا سنجرب الحل في هذه العجالة السريعة ، جرب المثال التالي :

C#:

كود :
[SIZE=3]SqlConnection cn = new SqlConnection();
cn.ConnectionString =
@"Data Source=(local)\SQLEXPRESS;Integrated Security=SSPI;" +
"Initial Catalog=AutoLot;Asynchronous Processing=true";
cn.Open();[/SIZE]


vb.net:

كود :
[SIZE=3]Dim cn As New SqlConnection()
cn.ConnectionString = "Data Source=(local)\SQLEXPRESS;Integrated Security=SSPI;" + "Initial Catalog=AutoLot;Asynchronous Processing=true"
cn.Open() [/SIZE]


أول خطوة قمنا بها هي تعريف الوضع Asynchronous Processing=true في ال ConnectioString بهذه الطريقة اصبحنا قادرين على الاستفادة من الدوال التالية :

BeginExecuteReader() و EndExecuteReader()
BeginExecuteNonQuery() و EndExecuteNonQuery()
BeginExecuteXmlReader() وEndExecuteXmlReader()


سنجرب الآن على BeginExecuteReader و EndExecuteReader ، وسنبدأ بتأخير جملة الاستعلام الأولى لخمس ثوان مثلاً :
c#:

كود :
[SIZE=3]string strSQL = "WaitFor Delay '00:00:05';Select * From Employee_info";
SqlCommand myCommand = new SqlCommand(strSQL, cn);[/SIZE]

vb.net:

كود :
[SIZE=3]Dim strSQL As String = "WaitFor Delay '00:00:05';Select * From Employee_info"
Dim myCommand As New SqlCommand(strSQL, cn) [/SIZE]


هنا سنقوم بتنفيذ عمليات أخرى على ثريد آخر :
c#:

كود :
IAsyncResult itfAsynch;
itfAsynch = myCommand.BeginExecuteReader(CommandBehavior.CloseConnection);
vb.net:


كود :
[SIZE=3]Dim itfAsynch As IAsyncResult
itfAsynch = myCommand.BeginExecuteReader(CommandBehavior.CloseConnection)[/SIZE]


وتنفيذ بعض العمليات حتى الانتهاء من تنفيذ الثريد
C#:

كود :
[SIZE=3]while (!itfAsynch.IsCompleted)
{
// تغيير قيمة مؤشر مثلاً .
Thread.Sleep(1000);
}[/SIZE]

vb.net:


كود :
While Not itfAsynch.IsCompleted
' تغيير قيمة مؤشر مثلاً .
Thread.Sleep(1000)
End While


الآن بما أننا خرجنا من ال Loop السابقة فهذا يعني انتهاء التنفيذ الأول ، الآن سنقوم بتنفيذ العملية التي نريدها .
C#:

كود :
[SIZE=3]SqlDataReader myDataReader = myCommand.EndExecuteReader(itfAsynch);
while (myDataReader.Read())
{
MessageBox.Show(myDataReader[1].ToString());
}
myDataReader.Close();[/SIZE]


vb.net:

كود :
[SIZE=3]Dim myDataReader As SqlDataReader = myCommand.EndExecuteReader(itfAsynch)
While myDataReader.Read()
MessageBox.Show(myDataReader(0).ToString())
End While
myDataReader.Close()[/SIZE]


فقط !
تبقى نقطة واحدة قبل انهاء وضع ال Connected وهي نقطة ال Transactions ، وهي ما سنتحدث عنه لاحقاً .
}}}
تم الشكر بواسطة:
#8
Transactions :

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

لذا خطوات العمل في قواعد البيانات لا بد أن تكون بالشكل التالي :
- سحب 500 دولار من حساب عميل 1 .
- ايداع 500 دولار في حساب عميل 2 .


وهاتان العمليتان لابد ان تتم سوية ، بمعنى لو تمت العملية الأولى ولم تتم العملية الثانية لانقطاع الاتصال مثلاً فهذا غير مقبول ، لذا لا بد ان يتم اعتماد العمليتين أو حذف اي عملية منهم تتم دون الأخرى وهو ما يعرف باسم roll back .

إذا قمنا بعمل transaction سيتم تعريف هذه المجموعة من العمليات في نظام قاعدة البيانات DBMS على شكل وحدة واحدة بحيث يتم تنفيذها سوية او عدم تنفيذها سوية .
الفئة المسؤولة عن هذه العملية لدينا هي الفئة SqlTransaction الموجودة ضمن مجال الأسماء System.Data.SqlClient ، هناك فئات أخرى يمكنها تطبيق هذه العملية ضمن مجال أسماء .net ايضاً مثل :

System.EnterpriseServices : تتيح لنا هذه الفئة الاتصال مع مكونات COM+ التي تقدم لنا الدعم في هذه العملية .
System.Transactions : تتيح لنا هذه الفئة بناء تطبيقات تدعم ال transactions .
WCF و WWF : تمكننا هاتان الفئتان من تطبيق مبادئ ال transaction ايضاً .


حتى في نظم قواعد البيانات يمكنك القيام بهذه المهمة عن طريق تعريف Stored Procedure يقوم بعمل transactions عن طريق TRANSACTIONو ROLLBACKو COMMIT ، يمكنك البدء في هذا النوع من هنا :
http://msdn.microsoft.com/en-us/library/ms187844.aspx

في ADO.net لدينا الفئة DBTransaction التي تطبق الواجهة IDbTransaction interface والذي يحتوي على الدوال الرئيسية التالية :
C#:

كود :
public interface IDbTransaction : IDisposable
{
IDbConnection Connection { get; }
IsolationLevel IsolationLevel { get; }
void Commit();
void Rollback();
}

vb.net:

كود :
[SIZE=3]Public Interface IDbTransaction
Inherits IDisposable
ReadOnly Property Connection() As IDbConnection
ReadOnly Property IsolationLevel() As IsolationLevel
Sub Commit()
Sub Rollback()
End Interface [/SIZE]


يضيف لنا ال SqlTransaction دالة جديدة هي Save والتي تتيح لنا حفظ نقطة يتم الرجوع إليها في حالة الفشل في اتمام العملية بدل الرجوع في العملية بالكامل - لو كان هناك اجزاء من العملية أو العملية على مراحل - .
ابسط مثال على تطبيق هذه العملية ، هو افتراض وجود جدول يحتوي على ( اسم العميل - المبلغ المودع ) وعندما نقوم بعملية تحويل من حساب إلى آخر نقوم بعملية بالشكل التالي :
C#:

كود :
[SIZE=3]SqlCommand cmdGet = new SqlCommand("update customers set total=total-" + totalmoney.ToString() + " where ID" + custID.ToString(), cn);
cmdSelect.ExecuteNonQuery();[/SIZE]
[SIZE=3]SqlCommand cmdSet = new SqlCommand("update customers set total=total+" + totalmoney.ToString() + " where ID" + SuppID.ToString(), cn);
cmdSelect.ExecuteNonQuery();[/SIZE]


vb.net:

كود :
[SIZE=3]Dim cmdGet As New SqlCommand("update customers set total=total-" + totalmoney.ToString() + " where ID" + custID.ToString(), cn)
cmdSelect.ExecuteNonQuery() [/SIZE]
[SIZE=3]
Dim cmdSet As New SqlCommand("update customers set total=total+" + totalmoney.ToString() + " where ID" + SuppID.ToString(), cn)
cmdSelect.ExecuteNonQuery() [/SIZE]

في هذه العملية ولأي سبب كان قد يتوقف الجزء الثاني من العملية ، لذا سنحاول اخبار نظام قاعدة البيانات بأننا سننفذ كلا العمليتين في نفس الوقت ، ولو لم يتم تنفيذ واحدة منهما سيتم الغاء الأخرى مباشرة .
C#:


كود :
[SIZE=3]SqlCommand cmdGet = new SqlCommand("update customers set total=total-" + totalmoney.ToString() + " where ID" + custID.ToString(), cn);
SqlCommand cmdSet = new SqlCommand("update customers set total=total+" + totalmoney.ToString() + " where ID" + SuppID.ToString(), cn);[/SIZE]
[SIZE=3]SqlTransaction sqltr = null;
try
{
sqltr = sqlCn.BeginTransaction();[/SIZE]
[SIZE=3]cmdGet.Transaction = sqltr;
cmdSet.Transaction = sqltr;[/SIZE]
[SIZE=3]cmdGet.ExecuteNonQuery();
cmdSet.ExecuteNonQuery();[/SIZE]
[SIZE=3]if (throwEx)
{
throw new ApplicationException("all operation canceld, some errors occures");
}[/SIZE]
[SIZE=3]sqltr.Commit();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
tx.Rollback();
}
}[/SIZE]


vb.net:

كود :
[SIZE=3]Dim cmdGet As New SqlCommand("update customers set total=total-" + totalmoney.ToString() + " where ID" + custID.ToString(), cn)
Dim cmdSet As New SqlCommand("update customers set total=total+" + totalmoney.ToString() + " where ID" + SuppID.ToString(), cn) [/SIZE]
[SIZE=3]Dim sqltr As SqlTransaction = Nothing
Try
sqltr = sqlCn.BeginTransaction()

cmdGet.Transaction = sqltr
cmdSet.Transaction = sqltr

cmdGet.ExecuteNonQuery()
cmdSet.ExecuteNonQuery()

If throwEx Then
Throw New ApplicationException("all operation canceld, some errors occures")
End If

sqltr.Commit()
Catch ex As Exception
MessageBox.Show(ex.Message)
tx.Rollback()
End Try [/SIZE]


كما لاحظت ، نقوم ببدء عملية ال transaction ، وما لم يحدث أي خطأ فلن ندخل في الشرط ولذا سيتم تنفيذ دالة Commit لتنفيذ العمليتين واعتمادهما ، أما في حالة حدوث اي خطأ فسنقوم برمي throw Exception والذي ينقلنا مباشرة إلى Catch لنقوم هناك باستدعاء الدالة rollback من اجل الغاء جميع التأثيرات التي حدثت .

لو كنت تود تجربة هذا المثال لمعرفة كيفية حدوثة ، جرب جعل قيمة throwEx=true وجرب ما يحدث .

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

إلى هنا نكون قد انتهينا من دروس هذا اليوم من أجل ADO.net والوضع المتصل ، في الدرس القادم سوف نتعرف سوية بإذن الله على الوضع المنفصل قبل ان يكون درسنا الأخير في عالم قواعد البيانات من أجل مبادئ LINQ .

والله الموفق ...
والسلام عليكم ورحمة الله وبركاته ...
}}}
تم الشكر بواسطة:



التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم