احنا كنا عايزين نخزن البيانات بتاعنا في DB مش مجرد اننا نخزنها في الـ Memory عشان متتمسحش وكل الكلام اللي قولناه قبل كدا في كورس الـ DB

فاحنا نفترض اننا عملنا الـ Application وعملنا الـ Database فاحنا حاليا محتاجين نعمل Link بينهم ونبعت الداتا ونسيفها وهكذا.

فهما موفرينلنا كالعادة Library كاملة عشان نعمل الموضوع دا

ADO .NET

هنستخدمها عشان نعمل Link بين الاتنين زي ما قولنا

  • هي مجموعة من الـ Classes في الـ .NET بتساعدني عشان أعمل Access لـ Data Sources زي الـ DB
  • بتسمح بالـ Communication بين الـ Applications و Databases
  • مهم جدًا انك تبقا عارفها ولسا بتستخدم لحد دلوقتي وهي أسرع من الـ EF Core كمان.

Components

دي عبارة عن الـ Classes زي ما قولنا زي: Connection, Command, DataReader, DataSet, DataAdapter, etc.

أول حاجة بنفتح زي قناة كدا بيننا وبين الـ SQL Server عشان لو عايز انقل داتا او كدا فدا الـ Connection ودا ممكن يبقا منه أكتر من واحد لو عايز استخدم كل واحد في حاجة

وبعدين بيبقا عندي أمر أو Query عايز انفذها فأنا بيبقا عندي شخص بديله المهمة دي، بقوله بص انت خد الـ Query وخد الـ Connection او الطريق اللي توصل من خلاله للسيرفر وعايزك تبقا تنفذهم لما اقولك

Connection

أول حاجة كنا بنقولها ان عندنا DB Server اللي هو مثلًا SQL Server وبنقول عندنا DBMS اللي هو SQL Server Management Studio فكنا بننزله وبنقوله الـ Path بتاع الـ Server بتاعنا لو تفتكر فهو الـ Management Studio اتعامل مع الـ Server انه Client له وكان بيدخله بيانات الدخول للـ Server دا.

فدلوقتي احنا عايزين نوصل من الـ Application للـ Server نفسه مش للـ MS بتاعنا فمفيش شك إن برضو لازم أدخل نفس البيانات اللي كنت بعملها معاه زي اسم السيرفر والـ Credentials وغيرهم.

فاحنا المعلومات دي هي اسمها الـ Connection String وبنستخدمه دايما عشان نوصل لسيرفر الـ SQL.

using System.Data.SqlClient;
 
string connectionString = "Data Source=ServerName; Initial Catalog=DatabaseName; Integrated Security=True";
 
sqlConnection conn = new SqlConnection(connectionString);
 
conn.Open();
// Perform operations ...
conn.Close();

CRUD using ADO .NET

We talked about CRUD before. هنعمل ال Create والـ Update والـ Delete هو نفسه بالظبط بس هنغير الـ Query نفسها.

Create Operation (INSERT)

أول حاجة طبعا احنا محتاجين نجهز الـ Query بتاعتنا.

يفضل انك تكتب أي لوجيك أو اي تجهيزات زي الـ Query مثلًا قبل ما تعمل Open للـ Connection مع الـ DB فأنت كل اللي عليك انك تفتح الكونيكشن تعمل اللي انت عايزة في الـ DB وبعدين تقفله تاني لا أكتر ولا أقل.

زمان كنا بنقول على العمليات اللي هي زي الـ INSERT, DELETE, UPDATE وكدا كنا بنقول عليهم Queries إنما في لغة الـ Application الـ Query هي مجرد بس طلب بيانات او Data غير كدا Orders or Commands

string connectionString = "Data Source=ServerName; Initial Catalog=DatabaseName; Integrated Security=True";
 
sqlConnection conn = new SqlConnection(connectionString);
 
string insertQuery = "INSERT INTO Employees VALUES('Mahmoud', '15000', 'Tanta;)";
 
SqlCommand insertCommand = new SqlCommand(insertQuery, conn)
 
conn.Open();
 
insertCommand.ExecuteNonQuery();
 
conn.Close();

Parameters

في ADO.NET بنستخدم الـParameters (المعروفة بالـParameterised Queries) عشان نفصل بين (SQL Statement) وبين البيانات اللي بيدخلها المستخدم. بالطريقة دي بنحمي التطبيق من الـSQL Injection اللي بيحصل لو دمجنا Inputs من الـ User في الـ Query مباشرةً بنص عادي (String Concatenation).

• ليه ده بيمنع الـInjection؟

  • لأن الـParameter بيفضل معرّف كقيمة Data فقط، وما بيتعاملش كجزء من بنية الاستعلام نفسها، فحتى لو المستخدم أدخل كلام “غريب” محتمل إنه يكون كود SQL ضار، هيتمّ تخزينه كقيمة نصية مش كتعديل على الـQuery.

• إزاي بنستخدمه؟

  • بنكتب في الـCommand الـ Query وفيه علامات للـParameters (زي @paramName في SQL Server).
  • بعد كده بنضيف Values للـParameters دي في الـCommand.Parameters.
  • أخيرًا بننفذ الاستعلام (ExecuteReader / ExecuteNonQuery / ExecuteScalar).

مثال صغير:

using System;
using System.Data;
using System.Data.SqlClient;
 
class Program
{
    static void Main(string[] args)
    {
        string connectionString = "Data Source=.;Initial Catalog=TestDB;Integrated Security=True";
 
        // المُدخلات المفترضة (جايه من المستخدم مثلاً)
        string userInputUserName = "Ahmed";
        string userInputPassword = "12345";
 
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();
 
            string sqlQuery = "SELECT * FROM Users WHERE UserName = @UserName AND Password = @Password";
 
            using (SqlCommand command = new SqlCommand(sqlQuery, connection))
            {
                // بنضبط قيمة الباراميترز
                command.Parameters.AddWithValue("@UserName", userInputUserName);
                command.Parameters.AddWithValue("@Password", userInputPassword);
 
                // في سيناريوهات الإنتاج يفضل تحديد النوع والطول:
                // command.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = userInputUserName;
                // command.Parameters.Add("@Password", SqlDbType.NVarChar, 50).Value = userInputPassword;
 
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        // مثال بسيط على استقبال البيانات
                        Console.WriteLine($"{reader["UserName"]} - {reader["Email"]}");
                    }
                }
            }
        }
    }
}

• في المثال ده:

  1. عرفنا الاستعلام وكتبنا فيه @UserName و@Password.
  2. ضفنا قيم البراميترز باستخدام command.Parameters.AddWithValue(…).
  3. نفذنا الاستعلام ب ExecuteReader.

• النتيجة:

  • أي مُدخلات “غريبة” هتتعامل كنص فقط مش هتندمج في الاستعلام الفعلي، وبالتالي التطبيق محمي من حقن أي أوامر SQL إضافية.
  • كده بنفصل دائمًا نص الاستعلام عن القيم المتغيرة (المُدخلات)، وده الأساس في حماية البيانات من الـSQL Injection.

Reader

في ADO.NET، الـReader (أشهره SqlDataReader) هو كائن بيتيح لك تقرأ البيانات اللي بترجع من قاعدة البيانات بشكل سريع ومباشر “forward-only, read-only”.

الفكرة انه عامل زي بوينتر وبيبدأ يعمل Read سطر سطر وهكذا

فيما يلي أهم ملامح الـDataReader:

  1. Forward-only, Read-only:

    • الـforward-only: يعني بتتحرك في السجلات خطوة للأمام فقط (من الأول للآخر)، ومش بتقدر ترجع خطوة للخلف.
    • الـread-only: يعني ما تقدرش تعدّل القيم اللي بتستقبلها مباشرة من الـReader.
  2. الأداء والاستخدام:
    • الـDataReader ممتاز في الأداء لما تكون محتاج تعالج بيانات كبيرة نسبيًّا، أو تقرأ البيانات على التوالي بدون الحاجة للتنقّل ذهابًا وإيابًا.
    • بسبب إنه بيتعامل “Streaming” مع البيانات، بيكون خفيف على الذاكرة مقارنةً بأساليب تانية زي الـDataSet أو DataTable.

  3. يتطلب اتصال مفتوح (Open Connection):
    • لازم اتصال DB يفضل مفتوح وأنت بتقرأ من الـReader، وده معناه إنك مسؤول عن إغلاقه بعد الانتهاء.
    • عادةً بتستخدم using statement أو final لضمان إنك بتقفل الـReader والـConnection.

  4. طريقة الاستدعاء (ExecuteReader):

    • غالبًا بتنشئ SqlCommand أو OracleCommand… إلخ، وبعدها تستخدم .ExecuteReader() لتستقبل SqlDataReader (أو DataReader آخر حسب مزوّد البيانات).
    • بتعمل loop على الـReader (مع while(reader.Read())) لحد ما تخلص البيانات.
  5. مثال سريع لاستخدام الـDataReader في ADO.NET:

using (SqlConnection connection = new SqlConnection(connectionString))
{
   connection.Open();
   using (SqlCommand command = new SqlCommand("SELECT * FROM Users", connection))
   {
	   using (SqlDataReader reader = command.ExecuteReader())
	   {
		   while (reader.Read())
		   {
			   // مثال: اطبع حقل UserName و Email
			   string userName = reader["UserName"].ToString();
			   string email    = reader["Email"].ToString();
			   Console.WriteLine($"UserName: {userName}, Email: {email}");
		   }
	   }
   }
}
 
// Withoud using
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand("SELECT * FROM Users", connection);
connection.open();
 
SqlDataReader dataReader01 = command.ExecuteReader();
 
while (dataReader01.Read())
{
	string userName = reader["UserName"].ToString();
	string email    = reader["Email"].ToString();
	Console.WriteLine($"UserName: {userName}, Email: {email}");
}
dateReader01.Close();
 
connection.Close();

الفرق عن DataSet/DataAdapter:

• الـDataReader: التصفّح Forward-only، أسرع، ويشغل اتصال القاعدة مفتوح.
• الـDataSet/DataAdapter: بتحمّل البيانات كلها مرة واحدة في الذاكرة وتقدر تعمل عليها تعديلات أو تنقّل بين الأسطر بسهولة، لكن ده بيستهلك ذاكرة أكثر وبطيء نسبيًّا مقارنةً بالـDataReader.

باختصار، الـDataReader مفيد جدًا لو هدفك هو “قراءة سريعة Forward-only” من جداول قاعدة البيانات مع أقل استخدام للذاكرة، لكن على حساب إنك لازم تحافظ على اتصالك مفتوح وطريقة تنقّلك على السجلات محدودة للأمام فقط.