Wednesday, March 08, 2006

Unit Testing Using NUnit

بسم الله الرحمن الرحيم

هل سمعت عن الTesting ؟؟؟؟
بالطبع فال
Testing هو المرحلة قبل الأخيرة في دورة حياة تطوير البرنامج Software Development Life Cycle SDLC
و هي المرحلة التي تلي مرحلة ال
Development مباشرة

ما أهميتها؟؟؟
أهميتها بديهية و هي تجنب أخطاء قد تحدث أثناء استخدام البرامج و التي قد تؤدي لخسائر ضخمة مثل أنظمة البنوك و ربما خسائر في الأرواح لو كان هذا البرنامج حيوى جداً مثل أنظمة الطائرات الملاحية و غيرها
و أهمية أخرى هي ضمان مستوى معين من جودة البرامج و توجد مقاييس عالمية مثل ال
CMMI و غيرها

هل المبرمج مطالب بتعلم الTesting ؟؟
ال
Testing في اطلاقه فرع كامل و كبير من فروع الSoftware و يقع تحت الQuality Assurance أو ضمان الجودة و يوجد له متخصصين مثل الTest Engineers و الQA Specialists و غيرهم

هل يعني هذا أنه لا فائدة من تعلمه بالنسبة للمبرمج؟؟؟
بالطبع معرفة المبادئ الأساسية في ال
Testing شئ أساسي و هذا لأنك في النهاية ستعمل جنباً بجنب مع Tesers و أحياناً في بعض الMethodologies مثل Scrum أو Extreme Programming قد تجلس أنت كDeveloper مع Tester على جهاز واحد لضمان اكتشاف الأخطاء أثناء كتابتها...

ماهو الجزء الذي يُهم المبرمج؟؟؟
الجزء الذي يُهم المبرمج و الذي يكون هو مُطالب بأداءه هو ال
Unit Testing و هو جزء صغير من الكود يقوم المبرمج بكتابته لعمل Testing لجزء اخر أكبر من الكود
و لنفهم ذلك تابع هذا المثال...
بفرض أنك تقوم بكتابة
Class تقوم بعمليات حسابية جمع و طرح مثلاً

CODE

public class MathOps
{
public static int Add(int x, int y)
{
return x + y;
}

public static int Sub(int x, int y)
{
return x - y;
}
}


و تريد أن تتأكد من صحة العمليات فماذا ستفعل؟؟؟
أغلبنا سيقوم بعمل
Application بسيط (Console Application مثلاً) و يقوم باستخدام الClass و باعطاء قيم input و قراءة الoutputs و مقارنتها بالنتيجة الصحيحة المتوقعة Expected Result
هل هذه طريقة خطأ؟؟؟
لا بل ستؤدي المطلوب

ما عيوب هذه الطريقة؟؟؟
1- لا تصلح في المشاريع الضخمة التي تحتوي على عشرات ال
Classes و الMethods
2- كود ال
Testing منفصل تماماً عن الكود الحقيقي مما يجعل المشكلة الكبرى هي ربط كل class بالapplication الخاص بتجربتها و هذا يؤدي الى مشكلة تنظيمية أكبر
3-لا يمكنك عمل
Testing للPrivate Classes بسهولة و هذا لأن الكود الخاص بالTesting منفصل
4- ستحتاج كل مرة لعمليتين
Compile مرة للClasses الأساسية سواء كانت في dll منفصل او اي شئ اخر و مرة أخرى للTesting Application و هذا يُهدر وقت و مجهود

هل توجد طريقة أخرى؟؟؟
بالطبع و هي موضوع هذا المقال و هي ال
Unit Testing

مميزات الUnit Testing
1- تضمين كود ال
Testing داخل الUnit أو الDll التي تحتوي على الكود الأصلي و هذا يجعل الحل Modular بحيث لن تحتاج لحفظ الTesting Code منفصلاً عن الdll
2- سرعة أكثر في انجاز عملية ال
Unit Testing

اذن كيف يمكننا كتابة class تقوم بعمل testing للMathOps Class التي كتبناها منذ قليل
الموضوع بسيط جداً

CODE

[TestFixture]
public class TestClass
{

[SetUp]public void Init()
{

}

[Test]public void TestAdd()
{
int result = MathOps.Add(10, 20);
Assert.AreEqual(result, 30);
}
[Test]public void TestSub()
{
int result = MathOps.Sub(20, 10);
Assert.AreEqual(result, 10);
}
}


يمكنك اهمال ال
Attributes الموجودة للحظات سنتطرق لها بعد عدة سطور...
كما ترى أن ال
Class بسيطة فأنت تكتب فيها مجموعة من الFunctions تقوم بعملية Testing لFunction من الMathOps Class عن طريق اعطاءها قيم كمدخلات و مقارنة المخرجات مع القيم المتوقعة ثم عمل Assertion على أساس شرط معين و هنا الشرط هو أن تساوي القيم المتوقعة مع المخرجات
يمكن أن تكون عملية ال
Testing تقيس تعامل البرنامج مع الأخطاء فمثلاً في عملية القسمة تريد أن تتأكد أنه اذا كان المقسوم عليه يساوي صفر فان الFunction ستطلق ZeroDivisionException مثلاً و في هذه الحالة نعتبر الTestصحيحاً

ما هي الأدوات المستخدمة في الUnit Testing ؟؟؟؟
لكل لغة أو تكنولوجيا الأدوات الخاصة بها فمثلاً في ال
Java يوجد الJUnit و في الDotNet يوجد الNUnit و هو مجال حديثنا في الجزء الثاني من المقال

NUnit
هو مشروع مفتوح المصدر من أحد المشاريع الموجودة في
www.sourceforge.net و هو مجاني و يدعم جميع لغات الDotNet سواء في الاصدارة 1 أو 1.1 أو 2.0
و يمكنك تحميله من على http://www.nunit.com

كيف نستخدم Nunit
اولاً تحميل و تثبيت
Nunit بسيط فقط حمل الNunit Installer من على الموقع و السابق ثم ثبته ببساطة
ثانياً ستقوم باضافة ال
dll التالية لمشاريعك التي تنوي عمل testing لها و هي nunit.framework.dll و ستجدها موجودة عندما تقوم بعمل Add Reference من داخل الVisual Studio
ثالثاً قم بعمل

CODE

using NUnit.Framework;

في الملف الذي تنوي أن تكتب فيه الTesting Class
رابعاً استخدم ال
Attributes التالية
TestFixture و يستخدم قبل الTesting Class
SetUp و يستخدم قبل الinitialization method و التي تستخدمها عادة لعمل أي initializations
Test و يستخدم قبل كل Test Method
Ignore و يستخدم لتجاهل Test Method معينة
يوجد العديد من ال
Attributes الأخرى يمكنك استكشافها بنفسك و لكن سنكتفي هنا بتجربة هذه الAttributes

CODE

[TestFixture]
public class TestClass
{

[SetUp]public void Init()
{

}

[Test]public void TestAdd()
{
int result = MathOps.Add(10, 20);
Assert.AreEqual(result, 30);
}
[Test]public void TestSub()
{
int result = MathOps.Sub(20, 10);
Assert.AreEqual(result, 10);
}
}


سيكون هذا هو شكل ال
Class التي سنستخدمها في الTesting
سنقوم الان بعمل
Build للSolution ثم نفتح الNunit (ستجد له رابط على الDesktop بعد التثبيت)
سيظهر لنا بهذا الشكل

user posted image
سنقوم بعدها بعمل File->Open ثم نبحث عن البرنامج الذي قمنا بكتابته سواء كان dll أو exe
سيظهر لنا بهذا الشكل
user posted image
سنجد أن القائمة على اليسار تظهر لنا الClass التي وضعنا قبلها الTestFixture Attribute و أيضاً تظهر لنا الMethods التي وضعنا قبلها الTest Attribute
قم بالضغط على
Run
سيظهر لك هذا الشكل و الذي يبين أن عملية ال
Testing قد نجحت و الكود سليم

user posted image
و لكن لو أحدى العمليات فشلت سيظهر لك هذا الشكل
و الذي يبين لك أن العمليات التي نجحت و العمليات التي فشلت و يوضح لك سبب الخطاً
user posted image
و الان تخيل معي انك تعمل في مشروع ضخم و تقوم بعمليات تعديل في Methods موجودة بالفعل و تغير في طريقة عملها و لكن في النهاية المهم أن تكون النتائج سليمة فكل ماعليك هو تشغيل الTest كل مرة تغير أو تعدل في الكود للتأكد من أن كل شئ على ما يرام ثم تكمل عملك و أنت واثق

ال
Nunit كمشروع ناجح و لاقى شعبية جيدة تم تضمينه في SharDevelop داخل الIDE كما أن الTesting Environment الموجودة في Visual Studio 2005 Team System مستوحاة من Nunit

ارجو أن يكون الموضوع بسيط و سهل و واضح

Friday, March 03, 2006

Three Tier Model

أغلبنا سمع عن الThree Tier Model و الذي يتكون من الأجزاء الثلاثة الشهيرة
1- ال
Data Tier
2- ال
Business Logic Tier
3- ال
Presentation Tier


و هذا هو النظام الأشهر في بناء التطبيقات التجارية و سنحاول شرحه ببساطة و اعطاء أمثلة ان شاء الله

1- ال Data Tier
و هي الجزء الذي يتولى التعامل مع مصدر المعلومات مهما كان نوعه (قاعدة بيانات, ملفات نصية , .... الخ) و هي التي تتولى تخزين و استرجاع البيانات بغض النظر عن النظام نفسه و ماذا يفعل
و ينقسم هذا الجزء الى قسمين أصغر
أ- Data Access Layer و هي الجزء المسؤول عن التعامل المباشر مع قاعدة البيانات فهي تأخذ الQuery أو الStored Procedure و تنفذها و تعود بالناتج بدون تفكير و يمكن ببساطة نقل هذا الجزء من نظام لآخر دون تغيير في الكود الخاص بها ... و مثال بسيط عليها هو ال ADO.net نفسه
ب- Proxy Layer
و هي
Classes تنفذ الوظائف الأساسية على الTables مثل الUpdate و الSelect و الDelete و الInsert و لكل Table له Class Proxy خاصة به تتولى العمليات الخاصة به
بمعنى أنه اذا فرضنا أننا نملك
Database تحوي Tables بهذه الأسماء Users, Articles ...
و هكذا سيكون لدينــا 2
Classes من نوع Proxy و كل منهم يحوي الCode اللازم لاضافة و حذف و تعديل و قراءة المعلومات من الجدول المناظر له في الDatabase
و لكن يظهر سؤال ماهي الطريقة التي سنمثل بها ال
Data خارج الDatabase أي عندما نقوم بعمل شئ مشابه لهذا

CODE


Proxy_Users user = new Proxy_Users();
user.GetUser(2)


فكيف سنخزن ال
User Data و أين ؟؟؟
يجب أن يكون هناك مخزن للمعلومات و هنــا يمكن استخدام ال
Strongly Typed DataSets مثلاً أو أن تقوم بعمل Class تمثل كل Table من اختيارك بدون الاعتماد على ال STDataSets
و سنأتي لهذا الموضوع لاحقاً

2- الBusiness Tier
و هي المنطقة التي يتحول فيها النظام من كونه
Tables مخزنة في Database الى Object Oriented Model أي أننا بعد كتابة هذا الجزء يمكننا أن ننسى تماماً طبيعة تخزين البيانات..
كمثال على ال
Database السابقة سيكون لدينا Class User و Class Article و داخل ال Article يوجد reference على Object من نوع User تمثل كاتب المقال و أيضاً داخل الUser يوجد Collection من الArticle Objects تمثل المقالات التي تخص هذا المستخدم و هكذا يمكننا استخدام الClasses بهذه الطريقة

CODE

User user = new User();
user.FirstName = "Mohammed";
user.LastName = "Ahmed";
user.Age = 12;
user.Save();
Article article = new Article();
article.Subject = "anyThing";
article.CreationDate = DateTime.Now;
article.Author = user;
article.Save();
user.Articles.Add(article);


و هكذا تجد أن الكود أصبح أكثر قوة و أسهل في التعامل و أصبح ايضاًً
Encapsulated بشكل متميز و استطعنا اخفاء تفاصيل التعامل مع الDatabase في الtiers الأدنى و أيضاً داخل ال BusinessObjects استطعنا اخفاء استخدامنا للDataTier حتى يمكننا في الLayer الأعلى أن ننسى فعليا طبيعة البيانات

3- الPresentation Tier
و هي الجزء الذي يتعامل مع مستخدم البرنامج (من الاخر ال
User interface ) بجميع أنواعه و البيئات المستخدمة سواء كان Windows Application أو Web Application أو حتى Console Applications و يتم في هذا الجزء التعامل المباشر مع الBusinessTier و لا يُسمح أبدا بالتعامل المباشر مع الDataTier و في الحقيقة لو كان تصميمك جيداً فلن تحتاج أبدأ الDataTier في الPresentation Tier

و كمثال على تصميم لData Tier
افترض وجود
Database بهذا التصميم
بمعنى أنه اذا فرضنا أننا نملك
Database تحوي Tables بهذه الأسماء User, Article and Category


user posted image


يمكننا أن نقول أن كل
Proxy Class تتكون من Base Class و Child Class
ال
Base Class تحدد الشكل الأساسي للClass و الChild يحدد الImplementation الخاص بهذه الClass على نوع محدد من الData Sources بمعنى أنه اذا اردنا أن نجعل الApplication يعمل على ال SqlServer سنقوم بعمل Inheritance من ال Proxy Base و عمل SqlProxy Child مخصص للSql Server
و هذا يسهل التحول من من
Data Source الى اخر مجرد اعادة عمل الInheritance من الPorxyBase لعمل مثلاً OracleProxy و تحديد الImplementation الخاص بالOracle


user posted image


هذا مثال على
Proxy Base Class

CODE

using System.Collections;
using DataAccessLayer;
using Info_Tier;

namespace Data_Tier
{
public abstract class Proxy_Article
{
protected DAL m_conn;
protected string m_strConn;

public Proxy_Article()
{
m_strConn = Connection.GetConnectionString();
m_conn = new DAL(DbProvider.SqlServer, m_strConn);
}

public abstract int ArticleAdd(Info_Article info_Article);
public abstract void ArticleModifyBySID(int SID, Info_Article info_Article);
public abstract ArrayList ArticleGetAll();
public abstract void ArticleDelete(int SID);
public abstract Info_Article ArticleGet(int SID);
public abstract ArrayList ArticleGetByPerson_SID(int Person_SID);
}

}




و يكون هذا شكل الSqlProxy

CODE

using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using Info_Tier;

namespace Data_Tier
{
public class SqlProxy_Article : Proxy_Article
{
public SqlProxy_Article()
{
}

public override int ArticleAdd(Info_Article info_Article)
{
SqlParameter[] parameters = new SqlParameter[5];
parameters[0] = new SqlParameter("@SID", info_Article.SID);
parameters[0].Direction = ParameterDirection.Output;
parameters[1] = new SqlParameter("@Subject", info_Article.Subject);
parameters[1].Direction = ParameterDirection.Input;
parameters[2] = new SqlParameter("@Details", info_Article.Details);
parameters[2].Direction = ParameterDirection.Input;
parameters[3] = new SqlParameter("@MadeOn", info_Article.MadeOn);
parameters[3].Direction = ParameterDirection.Input;
parameters[4] = new SqlParameter("@Person_SID", info_Article.Person_SID);
parameters[4].Direction = ParameterDirection.Input;
m_conn.ExecuteStoredProcedureNonQuery("spArticleAdd", parameters);
return (int) parameters[0].Value;

}


public override void ArticleModifyBySID(int SID, Info_Article info_Article)
{
SqlParameter[] parameters = new SqlParameter[5];
parameters[0] = new SqlParameter("@SID", info_Article.SID);
parameters[1] = new SqlParameter("@Subject", info_Article.Subject);
parameters[2] = new SqlParameter("@Details", info_Article.Details);
parameters[3] = new SqlParameter("@MadeOn", info_Article.MadeOn);
parameters[4] = new SqlParameter("@Person_SID", info_Article.Person_SID);
m_conn.ExecuteStoredProcedureNonQuery("spArticleUpdateBySID", parameters);

}

public override ArrayList ArticleGetAll()
{
ArrayList info_array = new ArrayList();
IDataReader reader = m_conn.ExecuteStoredProcedureReader("spArticleGetAll", null);
while (reader.Read())
{
Info_Article info = new Info_Article();
info.SID = (int) reader["SID"];
info.Subject = reader["Subject"].ToString();
info.Details = reader["Details"].ToString();
info.MadeOn = DateTime.Parse(reader["MadeOn"].ToString());
info.Person_SID = (int) reader["Person_SID"];
info_array.Add(info);

}
return info_array;

}

public override void ArticleDelete(int SID)
{
m_conn.ExecuteStoredProcedureNonQuery("spArticleDeleteBySID", new SqlParameter[] {new SqlParameter("@SID", SID)});

}

public override Info_Article ArticleGet(int SID)
{
Info_Article info = new Info_Article();
IDataReader reader = m_conn.ExecuteStoredProcedureReader("spArticleGetBySID", new SqlParameter[] {new SqlParameter("@SID", SID)});
if (reader.Read())
{
info.SID = (int) reader["SID"];
info.Subject = reader["Subject"].ToString();
info.Details = reader["Details"].ToString();
info.MadeOn = DateTime.Parse(reader["MadeOn"].ToString());
info.Person_SID = (int) reader["Person_SID"];
}
return info;

}

public override ArrayList ArticleGetByPerson_SID(int Person_SID)
{
ArrayList info_array = new ArrayList();
IDataReader reader = m_conn.ExecuteStoredProcedureReader("spArticleGetByPerson_SID", new SqlParameter[]{new SqlParameter("@Person_SID", Person_SID)});
while (reader.Read())
{
Info_Article info = new Info_Article();
info.SID = (int) reader["SID"];
info.Subject = reader["Subject"].ToString();
info.Details = reader["Details"].ToString();
info.MadeOn = DateTime.Parse(reader["MadeOn"].ToString());
info.Person_SID = (int) reader["Person_SID"];
info_array.Add(info);

}
return info_array;
}

}

}



و ستلاحظ في ال
Codes استخدام Objects من نوع Info_Article و هي النقطة التي تكلمنا عنها في شرح الDataTier في الدرس السابق عندما ذكرنا استخدام الStrongly Typed DataSets في تخزين البيانات في الذاكرة أو عمل Class من اختيارك لتمثيل شكل الTable و هذه الطريقة الأخيرة هي المستخدمة فقد قمت بعمل Class تُسمى Info_Article تمثل الSchema الخاصة بالArticle Table و كل Object منها يمثل صف واحد من المعلوماتد
و هذا هو شكل ال
Info_Article Class


CODE

using System;

namespace Info_Tier
{
public class Info_Article
{
private int m_SID;
private string m_Subject;
private string m_Details;
private DateTime m_MadeOn;
private int m_Person_SID;

public int SID
{
set { this.m_SID = value; }
get { return this.m_SID; }
}

public string Subject
{
set { this.m_Subject = value; }
get { return this.m_Subject; }
}

public string Details
{
set { this.m_Details = value; }
get { return this.m_Details; }
}

public DateTime MadeOn
{
set { this.m_MadeOn = value; }
get { return this.m_MadeOn; }
}

public int Person_SID
{
set { this.m_Person_SID = value; }
get { return this.m_Person_SID; }
}

}
}



و هكذا يكون لكلل
Table لدينا في الDatabase هذه الClasses
1- ال
Base Proxy Class مثال الProxy_Article
2- ال
Child Specific Class مثال الSqlProxy_Article
3- ال
Info Class مثال ال Info_Article

و كما ذكرنا ال
Info_Article هي مجرد مخزن بيانات و الobject منه يماثل Row أو صف واحد من البيانات في الDatabase من هذا النوع و هكذا يمكننا تمثيل الQuery Result في هيئة Array of Info Objects و لهذا ستجد أن الدالة GetAll في ال Proxy_Article الناتج منها ArrayList و هو في الحقيقة Array of Info_Article

ثم نعود و نسأل عندما أحب أن أنشئ
object من الProxy فماذا أفعل ؟؟؟
في هذه الحالة التي يكون لديك
base abstract class و مجموعة من الchild classes و سيتم استخدام الbase class كpointer على أي من الchild objects فان الحل دائما و أبدأً يكون استخدام الDesign Pattern الشهير Factory
و هو أن تصنع
Class أخرى مهمتها انشاء الProxy Classes من النوع المطلوب و ارجاع pointer من نوع الparent class
و يكون الكود في هذه الحالة لل
Factory Class


CODE

public class Factory
{
public static Proxy_Article GetArticleProxy()
{
if ( certain condition)
return new SqlProxy_Article();
return new OracleProxy_Article();
}
}



حيث ال
Certain Condition هو الشرط الذي يحدد نوع الDatabase التي أتخاطب معها فربما كان هذا الشرط قيمة في الRegistry أو ملف Config أو اي شئ اخر
و هكذا عندما أحتاج لاستخدام ال
Proxy فان الكود يكون بسيط

CODE


Proxy_Article proxyArticle = Factory.GetArticleProxy();



و أخيراً قبل أن ننهي شرح ال
DataAccess Layer
ماذا عن ال
Connection String من سيكون المسؤول عنه
ببساطة سنقوم بعمل
Class اسمها Connection تحوي function واحدة فقط static و هي GetConnectionString و تكون هذه الfunction مسؤولة عن احضار الConnection String سواء كان مخزن HardCoded أو في ملف خارجي أو مُشفر في ملف أو في الRegistry و يكون الكود على هذا الشكل مثلاً


CODE

public class Connection
{
public static string GetConnectionString()
{
if(certain condition)
return "Data Source=(local); Database = Northwind; Integrated Security = SSPI;
return "Oracle Connection String ";
}
}


و هكذا يمكننا تغيير ال
Database الخاصة بالبرنامج في ثواني بدون اللجوء الى اعادة كتابة الCode كله كل ما سيتم في هذه الحالة
كتابة ال
Proxy الخاص بالDatabase الجديدة و تغيير الConnection String و ستكون الDataTier جاهزة للتعامل مع الDatabase الجديدة و لن نحتاج لتغيير سطر واحد في الطبقات الأعلى و بفرض أن هذه الطبقة على شكل dll سيتم استبدال الdll الخاصة بالتعامل مع الSql Server بالdll الخاصة بالOracle و ما يمكن ببعض التفاصيل البسيطة الاستغناء عن هذا

و للحديث بقية في ال
Business Tier

Threading in .net

بسم الله الرحمن الرحيم


هل جربت من قبل أن تتعامل مع الThreads ؟؟؟؟
اذا كانت اجابتك بلا فاقرأ معي هذا الموضوع و اذا كانت اجابتك بنعم فقرأه أيضاً ففيه فائدة عظيمة ستجدها بين سطور الموضوع

هل ترى التعامل مع الThreads صعب ؟؟؟؟
هو ليس صعباً و لكنه فقط مرحلة أخرى متقدمة و لا يجب أن تكون كل مرحلة متقدمة صعبة و العكس صحيح فأحيانا البداية تكون الأصعب على الاطلاق

ما هي الThreads ؟
ال
Threads هي محاولة تجزئة البرنامج الى مجموعة من العمليات المستقلة و التي يمكن أداءها بطريقة متوازية Parallel بدلاً من اضاعة الوقت في انتظار لا طائل منه و هو النظام المتسلسل Serial Execution و يهدف النظام الى تحسين أداء البرامج عموماً فاذا تخيلنا مثلاً برنامجأ رياضياً يقوم بعمل حسابات طويلة و معقدة (Matlab مثلاً) فان البرنامج أثناء تنفيذ هذه العمليات لا يمكنه اداء اي مهمة اخرى حتى ينتهي من الحسابات و من مساوئ هذا هو عدم استجابة البرنامج الى المستخدم (بالبلدي الشاشة بتهنج)...

و لكن هل هذا مانراه فعلاً في البرامج الحقيقية؟؟؟
بالطبع لا فبرنامج
Matlab مثلاً يمكتك من تشغيل برامجك و كتابة برامج أخرى و التفاعل مع واجهاته في نفس الوقت بدون أي مشاكل و هذا لأن جميع هذه العمليات هي عمليات مستقلة يمكن التعامل معها من خلال البرنامج بطريقة متوازية

هل ال
Threading مفهوم جديد
ال
Threading بصفة عامة مفهوم معروف و لكن للأسف لم يتم تطبيقه على مستوى لغات البرمجة بل تُرك مفتوحاً لكل نظام يطبقه كما يشاء و يدعم ما يريد و هذا ما جعل الThreading في ال++C مثلاً من المواضيع السيئة فكل نظام له الAPI's الخاصة به و التي تختلف كلياً عن أي نظام اخر...
و لكن مع ظهور الأجيال الحديثة من لغات البرمجة بدأً من ال
JAVA و التي حسب معلوماتي أول لغة ظهر الThreading فيها كجزء من اللغة نفسها بحيث أنتقلت مشاكل التعامل مع النظام و طريقة تطبيق الThreading نفسها الى الJVM بدلاً من المبرمج المسكين...
و بالطبع مع ظهور ال.
net كان الThreading جزء لا يتجزأ من الFramework و يمكن استخدامه من أي لغة تتبع الCLR ..

اذاً كيف نقوم بعمل Threading في ال#C ؟؟؟
أولاً هناك
Namespace تختص بالThreading و هي System.Threading و هي تحتوي على جميع الclasses الخاصة بالThreading....
اذاً فأول سطر في برنامج يتعامل مع ال
Threading هو

CODE

using System.Threading


أهم
Class في الThreading بالطبع هي System.Threading.Thread و هي التي توجد فيها أغلب العمليات الأساسية
اذاً هل الخطوة الثانية هي انشاء
object من نوع Thread ؟؟
الحقيقة هذه الخطوة تسبقها خطوة قبلها و هي كتابة ال
function التي ستقوم هذه الThread بتنفيذها و لتكن مثلاً

CODE

void PrintHello()
{
Console.WriteLine("Hello Threading...");
}


ثم نقوم بعمل ال
Object من نوع Thread

CODE

Thread thread = new Thread(new ThreadStart(PrintHello));


ثانية واحدة ايه اللي انت كاتبه ده ؟؟؟؟ مين ThreadStart دي و ازاي كاتب اسم الfunction كأنه parameter ؟؟؟؟
لو سألت نفسك السؤال السابق اذن فأنت أول مرة تتعامل مع ال
Delegates و هي باختصار شديد بديل محترم لما كنا نستخدمه في ال++C/C و هو الFunction Pointer ...
الحقيقة المجال لا يتسع لشرح ال
Delegate هنا و لكن يمكنك أن ترجع للMSDN أو ابحث في المنتدى... و لو مستعجل كل اللي محتاج تعرفه في السطر اللي فات اني بقول للThread بتاعتي ان الfunction اللي حتنفذها اسمها PrintHello
شفت خرجتني من الموضوع ازاي؟؟؟
mad.gifعلى العموم ما علينا

بقيت خطوة أخرى و هي تشغيل ال
Thread نفسها باضافة سطر اخر

CODE

thread.Start();



هي دي الThreads بقة و عاملين الهوليلا دي على ال 3 سطور دول؟
لأ طبعاً دي البداية بس الموضوع طويل و فيه كلام كبير صحصح معايا كده لسة التقيل ورا

طيب هل هذه هي الطريقة الوحيدة لانشاء Threads

الاجابة بالطبع لا و لكن توجد طرق أخرى كثيرة و هناك أيضاً طرق يتم فيها انشاء
Threads بواسطة النظام دون أن يدخل المبرمج في تفاصيل انشاء الThreads و هي ما يسمى بالAsynchronous Operations و ستجد الكثير منها في الdotnet فمبدئيأ أي عملية تبدأ بكلمة BeginْXXX و يوجد لها مثيل يبدأ بEndXXX تعتبر عمليات غير متزامنه و مثال على هذا في الStreams ستجد BeginRead و EndRead و أيضاً BeginWrite و EndWrite و هذا موضوع اخر مثير في الdotnet و سأحاول شرحه لاحقاً
و يوجد أيضاً طريقة أخرى لانشاء ال
Threads و تعتمد على فكرة أن انشاء Thread جديدة و تشغيلها و مايتطلبه هذا من بعض العمليات الأخرى أحياناً يكون وقت هذه العمليات أكبر من العملية التي ستنفذها الThread في النهاية مما يؤثر سلباً على الأداء و هنا ظهر مفهوم الThreadPool أي مستودع الThreads و هي مجموعة من الThreads المنشأة (بضم الميم) مسبقاً و هي جاهزة و تحتاج فقط للعملية التي ستقوم بها و يكون لها عدد مُحدد و يمكن للمستخدم أن يستخدمها ببساطة جداً
طريقة استخدامها تكون من ال
ThreadPool Class عن طريق وضع الfunction المُراد تنفيذها في الQueue حتى تفرغ احدى الThreads لتنفيذ هذه العملية و يكون هكذا

CODE

ThreadPool.QueueUserWorkItem(new WaitCallback(PrintHello));


و هكذا سيتم وضع العملية في ال
Queue و سيكمل البرنامج عمله دون انتظار و عندما تكون هناك Thread جاهزة سيتم التنفيذ

مشكلة كبيرة جداً تواجه البرامج التي تعمل فيها أكثر من Thread و هي مشكلة ال Data Race أو التسابق...
شرح هذه المشكلة بسيط
انظر معي الى هذا الكود

CODE

using System;
using System.Threading;

public class Test
{
static int count=0;

static void Main()
{
ThreadStart job = new ThreadStart(ThreadJob);
Thread thread = new Thread(job);
thread.Start();

for (int i=0; i < 5; i++)
{
count++;
}

thread.Join();
Console.WriteLine ("Final count: {0}", count);
}

static void ThreadJob()
{
for (int i=0; i < 5; i++)
{
count++;
}
}
}


في الكود السابق ستلاحظ وجود متغير عام داخل ال
class و هو متغير integer اسمه count و هو عبارة عن عداد و يمثل بالنسبة لنا Shared Resource أو متغير مشترك...
ستجد أيضاً انه تم تعريف
Thread و تم اعلامها بالfunction الخاصة بها و هي عمل increment خمس مرات للمتغير count و في نفس الوقت تقوم الThread الرئيسية بعمل عملية مشابهة داخل الMain و هكذا أصبح لدينا 2 Threads يقوم كل منهم بعمل Increment خمس مرات بمجموع نهائي 10 حتى يصبح الناتج النهائي (نظرياً) 10 و لكن الواقع أن ال 2 Threads يتصارعان على نفس الShared Resource و هذه هي مشكلة الData Race ..
أين المشكلة...
المشكلة أنه يمكن ان يحدث
Overlapping أو تداخل بين عمليات القراءة و الكتابة المتوازية فتخيل معي أن الThread الرئيسية قد قرأت القيمة في المتغير count ( و لتكن 0 ) و قامت بعمل increment و قبل أن تقوم بكتابة القيمة الجديدة في المتغير count كانت الThread الأخرى قرأت القيمة القديمة (القيمة 0) و بدأت العمل عليها ثم جاءت الthread الرئيسية و كتبت القيمة الجديدة (القيمة 1) و هنا كانت الThread الجديدة قد أنهت عملها و جاءت لتخزن القيمة الجديدة بعد الincrement و هي (القيمة 1) !!!!!!!!!!!
هكذا أصبح هناك خطأ لأنه نظرياً حدثت عمليتي
increment للرقم صفر و لكن القيمة النهائية أصبحت 1 و هكذا لن تكون القيمة النهائية للبرنامج 10 كما هو متوقع !!!!!
مش مصدقني جرب الكود اللي فات .....
جربته؟؟؟
حتيجي تقوللي أنا جربت الكود و طلعت النتيجة النهائية 10 يعني كلامك مش صحيح...؟؟؟؟
لأ الكلام صحيح و لكن لكي تفهم ماحدث يجب أولاً أن نفهم كيف يُعامل نظام التشغيل ال
Threads ...
يقوم نظام التشغيل بمنح ال
Thread وقتاً محدداً على الProcessor و يسمى Round و ليكن زمنها T و تقوم الthread بأداء عملياتها و هنا يحدث حدث من 3 احداث لتتوقف الThread عن استخدام الProcessor
الأول هو أن تنتهي الThread من عملها قبل انتهاء الزمن T و هنا يقوم نظام التشغيل بازالة الThread و وضع أخرى مكانها
الثاتي هو أن ينتهي الزمن T قبل أن تنتهي الThread من عملها و هنا يقوم النظام بعمل Block للThread و حفظ حالتها و وضع Thread أخرى مكانها الى أن يأتي دور الThread الأولى مرة أخرى فيقوم النظام بعمل UnBlock و اعادتها الى حالتها لتستكمل عملها
الحدث الثالث هو أن يقوم أي من النظام أو المستخدم بعمل Abort للThread و انهاء عملها.. أو عمل Sleep و هنا سيعاملها النظام كأنها قد أنهت الفترة المتاحة لها T و سيتم عمل Block لها الى ان يأتي دورها مرة أخرى

اذن ما الذي جعل الكود السابق -الذي نعتبره نظرياً خطأ - يعمل بشكل صحيح ؟؟؟
السبب هو أن ال
Thread قصيرة جداً فهي تقوم بعملية بسيطة و هذا ماجعلها تستغرق وقتاً أقل من T فلم تحدث مشاكل الData Race و لكن يمكن أن تظهر المشكلة في جهاز اخر ابطأ أو تكون فيه T أقل و هذه تعتبر نقطة ضعف في الكود
يمكننا أن نرى هذه المشاكل باضافة بعض السطور المعطلة للكود هكذا

CODE

using System;
using System.Threading;

public class Test
{
static int count=0;

static void Main()
{
ThreadStart job = new ThreadStart(ThreadJob);
Thread thread = new Thread(job);
thread.Start();

for (int i=0; i < 5; i++)
{
int tmp = count;
Console.WriteLine ("Read count={0}", tmp);
Thread.Sleep(50);
tmp++;
Console.WriteLine ("Incremented tmp to {0}", tmp);
Thread.Sleep(20);
count = tmp;
Console.WriteLine ("Written count={0}", tmp);
Thread.Sleep(30);
}

thread.Join();
Console.WriteLine ("Final count: {0}", count);
}

static void ThreadJob()
{
for (int i=0; i < 5; i++)
{
int tmp = count;
Console.WriteLine ("\t\t\t\tRead count={0}", tmp);
Thread.Sleep(20);
tmp++;
Console.WriteLine ("\t\t\t\tIncremented tmp to {0}", tmp);
Thread.Sleep(10);
count = tmp;
Console.WriteLine ("\t\t\t\tWritten count={0}", tmp);
Thread.Sleep(40);
}
}
}


ستجد أننا ملأنا الكود بتعليمات
Thread.Sleep في أماكن مدروسة بين القراءة و التعديل و الكتابة حتى تظهر المشكلة و ستجد أن الناتج النهائي أصبح 6 و ليس 10 !!!!!!
اذن ماهو الحل لهذه المشكلة؟؟؟؟
الحل علمياً استخدام ال
Mutual Exclusion أو مايطلق عليه اختصاراً الMutex و يطلق على الفكرة عموماً Monitors و تعتمد الفكرة على فكرة عزل المناطق التي يُحتمل حدوث هذه المشكلة فيها و هي مناطق التعامل مع الShared Resources و جعل هذه المناطق تعمل بطريقة غير متوازية ...
لكي تفهم فكرتها تخيل أننا وضعنا باباً له قفل على هذه المناطق بمجرد أن تدخل
Thread من الباب تغلق الباب خلفها حتى تنتهي قم تخرج و تفتح القفل و أثناء وجودها في الداخل لا تستطيع اي Thread أخرى أن تدخل ....
و لاستخدام هذه الخاصية يمكننا استخدام ال
class Monitor بوضع Monitor.Enter و Monitor.Exit حول هذه المناطق..
أو يمكننا استخدام تعليمة أخرى و هي
lock هكذا

CODE

using System;
using System.Threading;

public class Test
{
static int count = 0;
static readonly object countLock = new object();

static void Main()
{
ThreadStart job = new ThreadStart(ThreadJob);
Thread thread = new Thread(job);
thread.Start();

for (int i = 0; i < 5; i++)
{
lock(countLock)
{
int tmp = count;
Console.WriteLine("Read count={0}", tmp);
Thread.Sleep(50);
tmp++;
Console.WriteLine("Incremented tmp to {0}", tmp);
Thread.Sleep(20);
count = tmp;
Console.WriteLine("Written count={0}", tmp);
}
Thread.Sleep(30);
}

thread.Join();
Console.WriteLine("Final count: {0}", count);
}

static void ThreadJob()
{
for (int i = 0; i < 5; i++)
{
lock (countLock)
{
int tmp = count;
Console.WriteLine("\t\t\t\tRead count={0}", tmp);
Thread.Sleep(20);
tmp++;
Console.WriteLine("\t\t\t\tIncremented tmp to {0}", tmp);
Thread.Sleep(10);
count = tmp;
Console.WriteLine("\t\t\t\tWritten count={0}", tmp);
}
Thread.Sleep(40);
}
}
}


لاحظ أننا عرفنا متغير من نوع
object و هو يمثل هنا القفل و استخدمنا نفس المتغير مع كل عملية lock تتعلق بنفس الshared resource و هكذا فانك عند تنفيذ الكود السابق فان النتيجة ستكون دائماً 10
و الى اللقاء قريباً في عرض لمشاكل ال
Threads مع الGUI

مشاكل الThreading مع الGUI Controls و المشكلة الأساسية و هي في هذا المثال
لو فرضنا أننا نملك
Form تحتوي على Label اسمه lblTest و سنقوم بتشغيل Thread لتغيير قيمة الText في هذا الLabel
هل هذه مشكلة؟؟؟؟
أعتقد أن الكثير منا سيشمر عن أكمامه و يكتب كود مشابه لهذا الكود

CODE


public void InitThread()
{
Thread thread = new Thread(new ThreadStart(ChangeText));
thread.Start();
}

private void ChangeText()
{
this.lblTest.Text = "Changed";
}


و لكن عند تنفيذ هذا الكود سنفاجأ بتوقف البرنامج عن العمل و ظهور
Exception يوضح لنا أنه لا يُمكن للThread الجديدة أن تُعدل في أي Controls أنشأتها الParent Thread !!!!!!!!!!!!

هل هذا يعني أنه لا يمكن للChild Thread أن تغير في أي Control على الForm ؟
بالطبع لا, يمكن لل
thread أن تقوم بتغيير الControls و لكن قبل أن نشرح الطريقة يجب أولاً أن نُعطي نبذة صغيرة عن
Windows Message Queue & Message Pumping
ببساطة
عندما تقوم بانشاء و تشغيل أي مشروع من أي نوع فان هناك ما يُسمى ال
Main Thread و هي الthread التي تقوم بتنفيذ ال Main function و هي التي تتولى تنفيذ العمليات الموجودة في البرنامج ...
و لكن
طبيعة البرامج ذات الواجهات الرسومية
GUI Application مثل Windows Forms تتميز بأن المستخدم يستطيع أن يتفاعل مع الForm أو البرنامج بشكل عام و في نفس الوقت يقوم البرنامج بتنفيذ هذه الأوامر بالاضافة الى الحفاظ على شكل الForm أي أنه يقوم برسمها مرة بعد أخرى ...
هنا يقوم ال
Windows بتحويل جميع العمليات و الأوامر على الForm الى ما يُسمى Messages و يضعها بترتيب حدوثها في الMessage Queue حتى يتم تنفيذها و يتم تنفيذ هذه العملية عن طريق الMessage Pumping بأن يقوم البرنامج بعد التشغيل بتشغيل Module يقوم بعمل loop على جميع الMessages داخل الMessage Queue و ينفذ الأوامر المتعلقة بكل Message مثل الضغط على زر أو تحريك المؤشر و هكذا يمكننا تخيل عملية الMessage Pumping كأنها على هذا الشكل

CODE


Message message = null;
while ((message = GetNextMessage()) != null)
{
ProcessMessage(message);
}


و هكذا يستمر البرنامج في العمل حتى تنتهي كل ال
Messages أو تأتي Message لانهاء البرنامج نفسه ...
و هنا نسأل أين تُنفذ عملية ال Message Pumping ؟؟؟؟
الجواب بسيط في ال
Main Thread و الدليل أنه اذا تسببت احد الMessages التي يستغرق تنفيذها بعض الوقت الى تجمد الشاشة و هذا لأن الMain Thread مشغولة بتنفيذ الMessage و لا يوجد لديها وقت لاعادة رسم الForm ....

سؤال اخر متى يتم تشغيل الMessage Pumping ؟؟؟
يتم تشغيل ال
Message Pumping مع تنفيذ هذه الinstruction الموجودة في كل Windows App و هي

CODE

Application.Run(new AnyFormYouWant());

داخل الMain

و الان بعد أن أخذنا نبذة عن ال
Message Queueing دعونا نغوص أكثر داخل الWindows Messaging in Win32
ماذا كنا نفعل أيام الWin32 API's لتغيير خاصية في Form أو أي شئ من هذا القبيل؟؟
كنا نملك 2
API's و هما
SendMessage
PostMessage
و كلٍ منهما له استخدام أمثل و هنا يجب أن نعرف الفرق بينهما أولاً
SendMessage
تقوم بوضع
Message جديدة داخل الMessageQueue و تنتظر الى أن تقوم عملية الMessage Pumping بتنفيذ هذه الMessage حتى تنتهي [Blocking Send] و هي الطريقة الأنسب لتغيير الخواص من ال Main Thread و لكنها ستفشل عند استخدامها من Thread أخرى لتغيير خواص Control أنشأه الMain Thread
PostMessage
تقوم أيضاً بوضع
Message جديدة داخل الMessage Queue و لكنها لا تنتظر حتى ينتهي تنفيذ الMessage أو حتى يبدأ التنفيذ فهي تنتهي بمجرد وصول الMessage الى الQueue و هذا يُسمى [Non Blocking Send] و هي تصلح في حالة الChild Thread تريد تغيير خواص في Control أنشأه الMain Thread لأنها أشبه بترك رسالة من الChild Thread الى الMain Thread حتى تقوم الMain Thread أثناء عملية الPumping بتنفيذها.. اي في النهاية الMain Thread هي التي قامت بالتغيير

و الان نعود مرة أخرى الى ال
Dot Net
ماذا يحدث عندما يتم تنفيذ عملية مثل
myForm.Height = 100;
يقوم ال
JIT Compiler بتحويل هذا الأمر الى SendMessage و يتم عندها ارسال Message لتغيير طول الForm و توضع في الQueue حتى يتم تنفيذها في الMessage Pumping و هذه العملية تنجح فقط اذا كانت الMain Thread و التي أنشأت الForm منذ البداية هي التي قامت بعمل تغيير الخاصية...
و لكن ماذا اذا أردت أن استخدم Post Message ؟؟؟؟ هل يمكنني ذلك؟
بالطبع يمكنك ذلك عن طريق استخدام
BeginInvoke أو Invoke الموجودتين في كل الControls و في هذه الحالة يتم ارسال Message الى الQueue و لا ننتظر انتهاء تنفيذها فوراً... و هذا هو ما نحتاجة حتى تستطيع الChild Threads تغيير خصائص الForm أو الControls

و الآن نعود الى نفس ال
Code الذي كتبناه في أول المقال و الذي أردنا به تغيير Label Text باستخدام Thread أخرى...


CODE


private delegate void ChangeDelegate();
public void InitThread()
{
Thread thread = new Thread(new ThreadStart(ChangeText));
thread.Start();
}

private void ChangeText()
{
if(this.InvokeRequired)
{
this.BeginInvoke(new ChangeDelegate(ChangeText), null);
return;
}
this.lblTest.Text = "Changed";
}


و الان بعض التعديلات ظهرت على الكود و في هذه المرة الكود سيعمل طبيعياً و سأشرح ماتم تعديله
قبل الشرح هناك نقطة صغيرة...
كيف أستطيع أن أحدد متى سأستخدم BeginInvoke و متى لن أستخدمها ؟؟
الجواب بسيط توجد داخل كل
Control خاصية Property تدعى InvokeRequired و هي من نوع bool و تكون قيمتها ب False اذا كان الMain Thread هو الذي بقوم بتنفيذ InvokeRequired و تكون بtrue عندما تكون أي Thread أخرى ...
اذاً يمكننا الان فهم الكود السابق ببساطة فكل الفكرة فيه تكمن في ال
if condition التي تستشف من يقوم بتنفيذ هذا الكود فإذا كان الMain Thread فسيتم تغيير الText باستخدام الText Property مباشرة أما اذا كانت thread اخرى فسيتم ارسال رسالة بواسطة PostMessage من خلال BeginInvoke ووضعها في الMessageQueue و هنا عندما يأتي دور الMain Thread في التنفيذ فانها ستقوم بتغيير الText بنفسها

ال ADO.net في سطور

بسم الله الرحمن الرحيم


مقدمة

أرى أن الجميع يطلب أسئلة حول الADO.net و أخرون يقدمون دروساً حول الADO.net ... هل هو بهذه الصعوبة؟؟؟
الصراحة في رأيي ال
ADO.net حفظ أكثر منه برمجة فأنت تحاول حفظ خطوات كتابة أكواده أكثر من مجرد تعلمه ... و هنا تبدأ مشكلة تكرار كتابة الأكواد ....

ماهي مشكلة تكرار كتابة الأكواد؟؟ هي اعادة كتابة كود ينفذ نفس الوظيفة عشرات المرات في مشاريع مختلفة بدون محاولة الاستفادة من أنه لا يتغير من مشروع لآخر أو يتغير غيراً طفيفاً
ما هو الحل؟؟؟

الحل يكون دائماً في طريقتين ...

الأولى تستخدم في حالات التكرار المتشابه 100% فهو تسلسل معين من الأكواد يتغير فيه فقط أسماء المتغيرات و في هذه الحالة يكون الحل هو عمل Wrapper لهذا الكود و الWrapper بالمعنى الحرفي هي الغلاف.. أي أننا نقوم بعمل DLL يحوي هذا الكود في شكل منسق و سهل مجرد Function Call بحيث يسهل العمل في المستقبل ..... و هذه الحالة تناسب الADO.net بقوة
الطريقة
الثانية هي عمل Code Generators و هي في حالات التشابه الجزئي في الأكواد و نسبة التشابه تحدد مدى تعقيد الكود و هذه الحالة ليست نطاق دراستنا الآن و ان كنت سأتطرق لها قريباً ان شاء الله ....

عودة الى الADO.net


ببساطة ال
ADO.net يحاول تقسيم استخدام الDatabase الى قسمين أو طريقتين الأولى هو الConnection Oriented أو الطريقة المتصلة و الطريقة الثانية هي ال Connectionless Oriented أو الطريقة المنقطعة (التعريب صعب فلا تضحك من فضلك laugh.gif)
كما أنه يوجد تقسيم أخر من حيث طبيعة العملية نفسها بمعنى أنه قسم العمليات الى
Queries و NonQueries و Scalar
حيث ال
Queries هي استرجاع قيم و سجلات من قاعدة البيانات (مثال جمل الSelect) ...
و ال
NonQueries هي تغيير في الData المسجلة أو تعديل في شكل البناء الداخلي للجداول مثل عمليات (Update, Delete, Insert, Alter, Create ..etc)
أما ال
Scalar فهي عملية قراءة و لكن ذات طبيعة Summarization أو اختصار للبيانات بعمل عملية محددة عليها مثل حساب المجموع أو المتوسط أو العدد ... الخ

و من هنا ظهرت ال
Scenarios أو المواقف الثابتة المملة في الADO و سنحاول سرد الأكثر شيوعاً
1- بداية الConnection و هذا جزء ثابت دائماً و لهذا سأكتبه مرة واحدة ...

CODE


SqlConnection conn = new SqlConnection("my Connection String");



2- قراءة Records من الDatabase باتجاه ثابت (قراءة فقط) باستخدام الDataReader ----- من نوع Connection Oriented

CODE


SqlCommand command = new SqlCommand("Select * From tblUsers", conn);
try
{
conn.Open();
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
Console.WriteLine(reader["ColumnName"]);
}
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}



3- قراءة
Data Bulk أو كمية من الData خبطة واحدة (باستخدام الDataSet) ----- من نوع Connectionless Oriented

CODE


SqlCommand command = new SqlCommand("Select * From tblUsers", conn);
SqlDataAdapter adapter = new SqlDataAdapter(command);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds);
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}
//Do Whatever You want with the dataset



4- اجراء عمليات تعديل أو حذف أو اضافة ------------

CODE


SqlCommand command = new SqlCommand("Delete From tblUsers Where Username='bashmohandes'", conn);
try
{
conn.Open();
command.ExecuteNonQuery();
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}



5-اجراء عمليات على الStored Procedure في حالة Stored Procedure تقرأ معلومات
و هذه هي ال
Stored Procedure

CODE

Create Procedure sptblUserGetAll
AS
Select * From tblUser



و هذا الكود يقوم بالقراءة منها (
Connection Oriented Mode)

CODE

SqlCommand command = new SqlCommand("sptblUserGetAll", conn);
command.CommandType = CommandType.StoredProcedure;
try
{
conn.Open();
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
Console.WriteLine(reader["ColumnName"]);
}
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}



و هذا الكود في حالة الConnectionLess Oriented


CODE


SqlCommand command = new SqlCommand("sptblUserGetAll", conn);
command.CommandType = CommandType.StoredProcedure;
SqlDataAdapter adapter = new SqlDataAdapter(command);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds);
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}
//Do Whatever You want with the dataset



6- استدعاء StoredProcedure تقوم بعمليات تعديل على الData
في هذا المثال عملية الحذف

CODE

Create Procedure sptblUserDeleteBySID
@SID Int
AS
Delete From tblUser Where SID = @SID


فتلاحظ أن هذه ال
StoredProcedure تحتاج الى Parameter من نوع int و يكون كود تنفيذها

CODE


SqlCommand command = new SqlCommand("sptblUserDeleteBySID", conn);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@SID", 10);
try
{
conn.Open();
command.ExecuteNonQuery();
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}



7- عمل Query من النوع العددي Scalar مثل عد الUsers في الTable


CODE


SqlCommand command = new SqlCommand("Select count(*) as num_of_users From tblUsers", conn);
int number;
try
{
conn.Open();
number = (int)command.ExecuteScalar();
}
catch(SqlException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
}



و هناك بعض السيناريوهات الأخرى و لكن ماسبق كان أشهرها و أكثرها استخداماً و ما كان حاضراً في ذهني الآن و ستجد أنك ستعيد كتابة هذه الأسطر مئات المرات كلما احتجت لتنفيذ هذه العمليات ------- تضييع وقت

و الحل بسيط و لن يأخذ منك ثواني
ماذا لو كتبت هذه الاسطر ك
functions في class واحدة سيكون العمل أبسط كثيراً و يسهل عملية اعادة الاستخدام

و تكون هذه ال
class تتبع هذا الInterface

CODE


public interface MyADO
{
void Open();

void Close();

SqlDataReader ExecuteReader(string SQL);

DataSet ExecuteDataSet(string SQL);

object ExecuteScalar(string SQL);

int ExecuteNonQuery(string SQL);

SqlDataReader ExecuteStoredProcedureReader(string spName, SqlParameter[] parameters);

int ExecuteStoredProcedureNonQuery(string spName, SqlParameter[] parameters);
}



و أعتقد أنك ستشعر بالفرق عندما تكتب ال
Code بهذه الطريقة

CODE


MyAdoClass myAdo = new MyAdoClass();
DataSet ds = myAdo.ExecuteDataSet("Select * from tblUsers");


كما أن عمليات مثل اكتشاف الأخطاء و تصحيحها ستكون سهلة جداً

أرجو أن يكون الدرس سهل و مفيد و أن تكون الفكرة قد وصلت بوضوح
و قريباً دروس في المواضيع المتقدمة ان شاء الله

ما الجديد في C# 3.0

بسم الله الرحمن الرحيم

أعتقد أن أغلبنا قد استخدم C# 2.0 بطريقة أو بأخرى و بالتأكيد أن أغلبنا لم يتطرق الى كل ماهو جديد في C# 2.0 و مع هذا فان Microsoft بدأت تُعلن عن C# 3.0 و قد صدرت بالفعل ال Language Specifications على موقع المستقبل الخاص بالMSDN
http://www.msdn.microsoft.com/netframework/future
و هذا يدل أن قطار ال
DotNet يكاد يسير بسرعة الضوء و علينا اللحاق به...

الأن ماهو الجديد في الC# 3.0

للاجابة على هذه السؤال سنتطرق أولاً للمشاكل الموجودة في ال#C حتى نستطيع استيعاب الحل...
1- ال
Database و الGeneral Purpose Languages ظلا دائماً عالمين منفصلين و لكلٍ منهم خصائصه و مميازته و عيوبه فمثلاً البرمجة في الDatabase هي Declarative Programming أي أنك تشرح ماذا تريد أن تفعل باستخدام الSQL ثم تترك للDBMS الحرية في التصرف في أفضل طريقة لعمل هذا
مثال عندما نكتب
Select * From Customers فاننا نقول للنظام أردي بيانات العملاء و لكن لم أقل له اذهب و افتح الملف على HDD ثم قم بعمل loop على العملاء و ضعهم في array ....الخ و لكن النظام هو من قال بعمل الQuery Map و الExecution لهذا السؤال
أما في ال
General Purpose Programming Languages مثل ال#C و ال++C/C و غيرها فهي Imperative Programming أي أنك تحدد كيف تقوم بحل المشكلة بالتفصيل الممل...
أيضاً هناك فروق اخرى تم حلها في
C# 2.0 مثل أن الDatabase تقبل متغيرات ذات قيمة NULL أما ال#C فلا تقبل و كان حلها هو ابتكار الNullable Types الرائعة

2- طبيعة المشكلة السابقة جعلت التعامل بين العالمين المختلفين يتم عن طريق وسيط مثل ال
ADO/ ADO.net و يتم اعطاء الQueries للADO على شكل Strings و هذا جعل نوع اخر من المشاكل تظهر و هو أن الCompiler الخاص بال #C لا يقوم بالتأكد من صحة الQueries و بالتالي لا يستطيع أن يحسنها فمثلاً لو قمت بكتابة
Selet * From Customers فلن يعترض الCompiler على كلمة Selet أنها مكتوبة خطأ و سيقوم بعمل Build و تظهر هذه المشكلة كRun Time Error و هذا أسوأ ما في المشكلة ....

3- مشكلة أخرى و هي تميز بعض اللغات مثل ال
Python و التي تتبع الDynamic Languages بأنه يمكنك تعريف متغيرات دون ذكر نوعها و يتم التعرف على نوعها من السياق
فمثلاً
var x = 10 هكذا أصبح x من نوع integer دون أن أذكرها و أيضا
var y = new File و هنا أصبح الy عبارة عن object من نوع File دون أن اضطر لذكر كلمة File مرتين قبل المتغير و بعده و هي تصبح مملة أحياناً عندما يكون اسم الClass كبير
أيضأً في ال
Dynamic Languages يمكنك أن تقوم بعمل Initialization للمتغيرات في الConstructor دون أن تقوم بعمل الكثير من الConstructors المختلفة فبفرض أن الClass Customer لها الخصائص Name, Company, Address ففي #C يجب عليك أن تقوم بانشاء Constructor يأخذ 3 Parameters حتى يمكنك عمل Initialization كامل في الConstructor و لكن في الDynamic Language يمكنك عمل هذا ببساطة هكذا

CODE

var c = new Customer(Name= "bashmohandes", Address= "Egypt", Company = "Whatever");


و هذا يقلل سطور الكود الخاصة بال
Initialization و أيضاً الخاصة بكتابة الconstructors ...

ما سبق هو بعض المشاكل التي كانت تواجه المبرمجين و التي سيتم حلها في ال
C# 3.0 و سنعرض حل هذه المشاكل الموجود في C# 3.0 الان

1- تم حل هذه المشكلة الى حد كبير في مشروع
LINQ و الذي يقدم طريقة لكتابة الQueries داخل ال#C
مثال

CODE

public void Linq2() {
List products = GetProductList();

var soldOutProducts =
from p in products
where p.UnitsInStock == 0
select p;

Console.WriteLine("Sold out products:");
foreach (var product in soldOutProducts) {
Console.WriteLine("{0} is sold out!", product.ProductName);
}
}


و هنا نلاحظ استحداث
Code Block جديدة و هو From .... In .... Where ..... Select كما أنه يمكنك استخدام بعض العمليات الأعقد مثل الorder by, group by و غيرها من عمليات الDatabase المختلفة
أيضاً ستلاحظ أن
From جاءت في أول الQuery بينما جاءت الSelect في النهاية و هذه الطريقة تعتبر أوضح قليلاً و ان كانت غريبة بعض الشئ...
و الأجمل أن هذه الطريقة غير مقصورة على ال
Database Results و لكن يمكن أن تستخدم في أي شئ يمكنك مثلاً أن تقوم باستخدامها مع الFile System للبحث عن ملفات أو مع الReflection للبحث عن عمليات في Dll معينة و أيضأً مع الXML و أيضاً مع الArrays العادية
مثال اخر

CODE

public void Linq1() {
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var lowNums =
from n in numbers
where n <>


و هنا استخدم ال
LINQ في تحديد الأرقام الأقل من 5 مجموعة الأعداد..

2- في المشكلة الثانية الناجمة عن طبيعة المشكلة الأولى فقد أصبح ال
Compiler مسؤولاً عن تصحيح الاخطاء البرمجية في الQueries و أيضاً عمل الOptimizations اللازمة

3- تم اضافة الكثير من مميزات ال
Dynamic Languages في C# 3.0 فمثلاً يمكن الان الاستفادة من خصائص الDynamic Langauges مع الاستفادة من قوة الStrongly Typed Languages فمثلاً

CODE

var x = 1;
x = "mohammed"; //Error x is Integer


يمكنك الان لل
Compiler أن يتعرف على نوع المتغير عن طريق نوع القيمة التي توضع فيه و أيضاً اذا تم تغيير نوع هذه القيمة فيما بعد فان الCompiler يعترض لأن هذا يُعتبر تجاوز لقوانين الStrongly Typed Languages
و أيضاً الان يمكنك استعمال الطريقة الجديدة في استخدام ال
Constructors و التي تشبه تماماً الConstructors في الDynamic Languages
هكذا

CODE

var c = new Customer(Name= "bashmohandes", Address= "Egypt", Company = "Whatever");



كما أن ال#
C كما دعمت الAnonymous Methods في C# 2.0 فأيضاً الان تدعم الAnonymous Classes
و أيضاً توجد بعض الخصائص الجديدة مثل ال
Sequence و غيرها
و يمكنك تحميل ال
C# 3.0 من نفس الموقع الذي ذكرته في أو المشاركة و هو يعمل مع أي نسخة من نسخ VS 2005 مثل الProfessional أو الExpress و يأتي معه الكثير من الأمثلة...

أرجو أن تكونوا قد استفدتم من الموضوع
شكراً

Wednesday, February 22, 2006

My techincal arabic articles

Dear all,
I know that I made this plogs long time ago, and I didn't make any changes in it since that time, but now I'll collect all my arabic technical articles from arabic forums and archiving them here for all readers,
Thanks in advance and expect very soon

Monday, October 17, 2005

Welcome To Technical Blogs

Dear Reader,
This is my new technical only blogs and I will start -from today- writing any IT technical articles like special codes, designs, patterns, models and news so check regularly and I promise you with great surprises and information...
Thanks for your visit and hope to see you as soon as possible..

Eng. Mohammed Hossam
Cairo, Egypt.
16th October, 2005