زي ما اتفقنا، بعد ما بنعمل الـ Entities (زي Employee class) والـ DbContext، بنحتاج ننقل التغييرات دي من الكود بتاعنا ونطبقها على الـ Database. فيه طريقتين عشان نعمل كده:

الطريقة الأولى: المسح والإنشاء من جديد (مش كويسة أبدًا!)

دي طريقة بسيطة بتعتمد إنك تمسح الداتابيز لو موجودة وترجع تبنيها تاني من الصفر، عشان تضمن إن أي تغيير عملته في الكود يتنفذ.

// Main method or wherever you initialize the context
using CompanyDbContext dbContext = new CompanyDbContext();
 
// !!! WARNING: This deletes the entire database if it exists !!!
// dbContext.Database.EnsureDeleted(); // Use with extreme caution!
 
// This creates the database and schema based on the current model if it doesn't exist
// Does NOT apply migrations or handle schema updates.
// dbContext.Database.EnsureCreated(); // Creates DB if not exists, but doesn't handle updates
  • ليه وحشة؟ ببساطة لأنها بتمسح كل الداتا اللي متخزنة في الداتابيز بتاعتك. أكيد مينفعش نستخدمها في الشغل الحقيقي أو على داتابيز فيها بيانات مهمة. ممكن بس تكون مفيدة في أول مرحلة خالص وأنت لسه بتجرب، أو في الـ automated tests اللي محتاجة تبدأ بداتابيز نضيفة كل مرة.

الطريقة التانية: الـ Migrations (الأحسن والأصح)

دي الطريقة الصح والمُوصى بيها عشان نتعامل مع التغييرات في الداتابيز بشكل تدريجي ومن غير ما نمسح الداتا الموجودة.

  • فكرتها إيه؟
    • الـ EF Core بيقدر يراقب (Track) التغييرات اللي بتعملها في الـ Models بتاعتك وفي إعدادات الـ DbContext.
    • الـ Managing Database Schema with EF Core Migrations هو عبارة عن “وصفة” أو مجموعة أوامر الـ EF Core بيجهزها وبيكتبها في ملف كود (C#). الوصفة دي بتقول إزاي ننقل التغييرات اللي حصلت في الكود بتاعك ونطبقها على هيكل (Schema) الـ Database.
    • الهدف إن الكود بتاعك وهيكل الداتابيز يبقوا دايمًا متوافقين مع بعض (بنقول عليهم Migrated لنفس الحالة أو الـ Version).

خطوات عمل Migration

  1. تنزيل الـ Tools Package: عشان نقدر نستخدم أوامر الـ Migrations، لازم ننزل package إضافية اسمها Microsoft.EntityFrameworkCore.Tools.
    • عن طريق الـ Package Manager Console (PM):
      // Installs the EF Core tools package
      Install-Package Microsoft.EntityFrameworkCore.Tools
      // Or install a specific version
      // Install-Package Microsoft.EntityFrameworkCore.Tools -v 6.0.29
    • عن طريق الـ NuGet Package Manager UI: ابحث عن Microsoft.EntityFrameworkCore.Tools في تاب “Browse” ونزلها.
  2. عمل الـ Managing Database Schema with EF Core Migrations: بعد ما نزلنا الـ Tools، بنفتح الـ PM ونكتب الأمر ده:
    // Adds a new migration with the specified name
    Add-Migration InitialCreate // Or any other meaningful name, e.g., AddEmployeeTable

إزاي الـ EF Core بيعرف التغيير؟ الـ Snapshot السحري!

لما بتنفذ أمر Add-Migration، الـ EF Core بيعمل كذا حاجة ذكية عشان يعرف إيه اللي اتغير بالظبط:

  1. بيعمل Folder: لو مش موجود، بينشئ فولدر جديد في الـ Project بتاعك اسمه Migrations.
  2. بيعمل ملف الـ Migration File: جوه فولدر Migrations، بيعمل ملف C# جديد. اسم الملف ده بيكون عبارة عن رقم timestamp (عشان يضمن إنه unique ومترتب صح) زائد الاسم اللي إنت اخترته (زي 20231027103015_InitialCreate.cs). الملف ده جواه class بيورث من Migration وفيه اتنين methods مهمين:
    • الـ Up(): دي فيها الكود اللي بيطبق التغييرات بتاعتك على الداتابيز (زي CreateTable).
    • الـ Down(): دي فيها الكود اللي بيلغي التغييرات دي لو حبيت ترجع في كلامك (زي DropTable).
  3. بيعمل/يحدث ملف الـ Snapshot File: أهم حاجة بقى! الـ EF Core بيعمل ملف تاني اسمه YourDbContextModelSnapshot.cs (مكان YourDbContext اسم الـ DbContext بتاعك).
    • إيه لازمة الـ Snapshot ده؟ الملف ده عبارة عن “صورة طبق الأصل” أو “لقطة شاشة” لحالة الـ Models بتاعتك زي ما الـ EF Core شايفها بعد التغيير اللي إنت لسه عامله. هو بيوصف كل الـ Classes والـ Properties والعلاقات وكل حاجة بالتفصيل.
    • المقارنة: لما بتيجي تعمل Add-Migration، الـ EF Core بيقارن بين شكل الـ Models بتاعتك الحالية في الكود وبين الشكل اللي متسجل في آخر Snapshot موجود. بناءً على الاختلافات اللي بيلاقيها، بيعرف إيه التغييرات اللي حصلت بالظبط، وبيكتب الكود المناسب في الـ Up() والـ Down() methods بتاعة الـ Managing Database Schema with EF Core Migrations file الجديد. بعد كده، بيقوم محدّث الـ Snapshot file عشان يعكس الحالة الجديدة للكود.
  • مثال كاميرا المراقبة: تخيل إن الـ Snapshot هو آخر تسجيل فيديو لكاميرا المراقبة اللي بتصور الـ Models بتاعتك. الـ EF Core هو الحارس اللي بيراقب. لما بتعمل تغيير في الكود وتقوله Add-Migration، الحارس ده بيبص على الوضع الحالي ويقارنه بآخر تسجيل (الـ Snapshot). بيكتب تقرير (الـ Managing Database Schema with EF Core Migrations file) بكل التغييرات اللي شافها، وبعدين ياخد تسجيل جديد للوضع الحالي ويحدث بيه شريط الفيديو بتاعه (يحدّث الـ Snapshot).

تفاصيل الـ Managing Database Schema with EF Core Migrations File

شكل الـ Managing Database Schema with EF Core Migrations بتاع Employee (مثال لمحتوى Up method):

// Inside the Up() method of the ..._InitialCreate.cs migration file
protected override void Up(MigrationBuilder migrationBuilder)
{
    // Creates a new table named "Employees"
    migrationBuilder.CreateTable(
        name: "Employees", // Table name in the database
        columns: table => new // Defines the columns of the table
        {
            // Column 'Id' configuration
            Id = table.Column<int>(type: "int", nullable: false) // Data type: int, NOT NULL
                .Annotation("SqlServer:Identity", "1, 1"), // Makes it Primary Key and Auto-incrementing (Identity)
 
            // Column 'Name' configuration
            Name = table.Column<string>(type: "nvarchar(max)", nullable: false), // Data type: nvarchar(max), NOT NULL
 
            // Column 'Salary' configuration
            Salary = table.Column<double>(type: "float", nullable: false), // Data type: float (maps to double in C#), NOT NULL
 
            // Column 'Age' configuration
            Age = table.Column<int>(type: "int", nullable: true) // Data type: int, ALLOWS NULL (if Age prop is nullable int?)
        },
        constraints: table =>
        {
            // Defines the Primary Key constraint for the table
            table.PrimaryKey("PK_Employees", x => x.Id); // Names the constraint and specifies the column(s)
        });
}
 
// The Down() method would typically contain code to reverse the Up() operation
// protected override void Down(MigrationBuilder migrationBuilder)
// {
//     migrationBuilder.DropTable(name: "Employees");
// }
  • الشرح: ده مجرد كود C# عادي بينادي methods معرفة في الـ EF Core (زي CreateTable, Column, PrimaryKey, Annotation). الـ methods دي هي اللي بتترجم لأوامر SQL المناسبة لنوع الداتابيز اللي إنت محدده (في حالتنا SQL Server). الـ EF Core بيستنتج التفاصيل دي بناءً على الـ class بتاع Employee والـ Data Annotations أو الـ Fluent API configurations.
  • ملاحظات مهمة:
    • الـ Up() بتعمل CreateTable (أو AddColumn, AlterColumn, etc.). مش بتعمل CreateDatabase.
    • الـ Down() بتعمل العكس، غالبًا DropTable (أو DropColumn, etc.) عشان تقدر ترجع الداتابيز للحالة اللي قبلها.

الـ Snapshot Code

زي ما قلنا، ده ملف C# تاني بيحتوي على وصف كامل للـ Models بتاعتك زي ما الـ EF Core شايفها حاليًا. بمثابة “لقطة شاشة” بيستخدمها الـ EF Core للمقارنات المستقبلية.

تطبيق الـ Managing Database Schema with EF Core Migrations على الداتابيز (Apply Migration)

تمام، إحنا دلوقتي عملنا الـ Managing Database Schema with EF Core Migrations وجهزناها. بس هي لسه متطبقتش على الداتابيز الفعلية. عشان ننفذ الـ Up() method ونغير شكل الداتابيز، عندنا طريقتين:

1. من الكود (dbContext.Database.Migrate())

ممكن تيجي في الكود بتاعك، غالبًا في بداية تشغيل الأبلكيشن، وتكتب السطر ده:

// Inside Program.cs (Main method or application startup)
using CompanyDbContext dbContext = new CompanyDbContext(); // Or get instance via Dependency Injection
 
// Applies any pending migrations to the database.
// Creates the database if it doesn't exist.
dbContext.Database.Migrate(); // Apply pending migrations programmatically
  • إيه اللي بيحصل؟ الـ method دي بتقارن الـ Migrations اللي في الكود باللي متسجل في جدول __EFMigrationsHistory في الداتابيز، وتنفذ الـ Up() لأي Managing Database Schema with EF Core Migrations ناقصة. لو الداتابيز مش موجودة، بتنشئها الأول.
  • عيبها: لازم الكود يشتغل عشان الـ Migrations تتطبق، وممكن تعمل مشاكل في بيئات الـ Production المعقدة أو تطول وقت بدء التشغيل. مش مفضلة في التطوير غالبًا.

2. من الـ Package Manager Console (Update-Database)

دي الطريقة الأشهر والأسهل والأكتر استخدامًا، خصوصًا في مرحلة التطوير.

  • ببساطة، افتح الـ PM Console واكتب الأمر ده:
    // Applies pending migrations to the database
    Update-Database
  • إيه اللي بيحصل؟ بيعمل نفس اللي بتعمله Migrate() بالظبط. بيقارن وينفذ الـ Up() للـ Migrations الناقصة. ولو الداتابيز مش موجودة بينشئها.
  • الميزة: سريعة ومباشرة، مش محتاجة تشغل الأبلكيشن، وبتديك تحكم أكتر.

إزاي أتأكد إن التغييرات اتنفذت؟

بعد ما عملت Update-Database، إزاي تتأكد إن الدنيا تمام؟

  1. افتح الداتابيز وشوف بنفسك: استخدم SSMS أو أي أداة تانية، واعمل Refresh وشوف الـ Tables والـ Columns الجديدة.
  2. (اختياري) استخدم SQL Server Profiler: شغل الـ Profiler قبل Update-Database عشان تشوف أوامر الـ SQL اللي اتنفذت.
  3. الأهم: بص على جدول __EFMigrationsHistory: الـ EF Core بيعمل جدول خاص اسمه __EFMigrationsHistory (بـ __ في الأول) وبيسجل فيه اسم كل Managing Database Schema with EF Core Migrations طبقها بنجاح. هو بيستخدم الجدول ده عشان يعرف واقف فين ويكمل منين المرة الجاية.

تعديل وإلغاء الـ Migrations بعد تطبيقها (Rollback)

القاعدة الذهبية: أوعى تعدل أو تمسح Managing Database Schema with EF Core Migrations files أو الـ Snapshot file يدويًا من الفولدر بعد ما تكون طبقت الـ Managing Database Schema with EF Core Migrations دي على الداتابيز بـ Update-Database. ده غالبًا هيسبب مشاكل كبيرة. استخدم دايمًا أوامر الـ EF Core (Update-Database, Remove-Migration).

تعالى نشوف أشهر السيناريوهات:

سيناريو 1: تغيير اسم Column (تغيير بسيط بعد التطبيق)

المشكلة

عملت InitialCreate وطبقتها. بقى عندك column اسمه Name. عايز تخليه EmpName.

الحل (الطريقة الصح)

  1. عدّل الكود: غير اسم الـ property في Employee.cs لـ EmpName.
  2. اعمل Managing Database Schema with EF Core Migrations جديدة:
    // Create a new migration describing the rename operation
    Add-Migration RenameEmployeeNameColumn
    • الـ EF Core هيقارن الكود الحالي بالـ Snapshot القديم ويعمل Migration فيها RenameColumn.
  3. طبّق الـ Managing Database Schema with EF Core Migrations الجديدة:
    // Apply the new migration to the database
    Update-Database
    • كده اسم الـ column هيتغير في الداتابيز وهيتسجل في __EFMigrationsHistory.

سيناريو 2: محاولة مسح Migration File يدويًا بعد تطبيقها (الطريقة الغلط تمامًا!)

المشكلة

عملت الخطوات اللي فاتت، بعدين رحت مسحت ملف ..._RenameEmployeeNameColumn.cs يدويًا.

ليه دي طريقة غلط ومدمرة؟

الداتابيز اتغيرت، واسم الـ Managing Database Schema with EF Core Migrations متسجل في __EFMigrationsHistory، والـ Snapshot اتغير. مسح الملف بس بيعمل لخبطة تامة وهيخلي الـ EF Core يتجنن المرة الجاية. أوعى تعمل كده.


سيناريو 3: الطريقة الصح للإلغاء/الرجوع (Rollback)

المشكلة

عملت RenameEmployeeNameColumn وطبقتها، وعايز أرجع في كلامي وأرجع الاسم لـ Name تاني في الداتابيز والكود.

الحل (الخطوات الصح بالترتيب)

  1. اعمل Rollback للداتابيز الأول: استخدم Update-Database عشان ترجع للـ Managing Database Schema with EF Core Migrations اللي قبل اللي عايز تلغيها.
    // Revert the database to the state of the 'InitialCreate' migration
    // This will execute the Down() method of 'RenameEmployeeNameColumn'
    Update-Database InitialCreate
    • كده الداتابيز هترجع زي ما كانت بعد InitialCreate، واسم الـ Migration الملغية هيتشال من __EFMigrationsHistory.
  2. عدّل الكود بتاعك: رجّع اسم الـ property في Employee.cs لـ Name.
  3. امسح الـ Managing Database Schema with EF Core Migrations من الكود (بشكل آمن): استخدم Remove-Migration.
    // Remove the last migration file (..._RenameEmployeeNameColumn.cs)
    // This also reverts the Snapshot file.
    // This only works if the migration is NOT applied to the database (which we ensured in step 1).
    Remove-Migration
    • الأمر ده هيمسح الـ Migration file ويعدل الـ Snapshot file ويرجعه للحالة اللي قبلها.

سيناريو 4: الرجوع للصفر (قبل أول Managing Database Schema with EF Core Migrations)

المشكلة

عايز ألغي كل الـ Migrations وأرجع الداتابيز فاضية (قبل InitialCreate).

الحل

  1. اعمل Rollback للصفر: استخدم اسم الـ Managing Database Schema with EF Core Migrations الخاص 0.
    // Revert all applied migrations, effectively emptying the EF Core managed schema
    Update-Database 0
    • ده هينفذ الـ Down() لكل الـ Migrations المتطبقة بالترتيب العكسي.
  2. (اختياري) امسح الـ Migrations من الكود: ممكن تمسح ملفات فولدر Migrations يدويًا أو تستخدم Remove-Migration كذا مرة لو عايز تبدأ بكود نضيف خالص.

سيناريو 5: إضافة Property بعد مشاكل الـ Rollback (أهمية الـ Snapshot المتوافق)

المشكلة

عملت Rollback للداتابيز (Update-Database Previous) بس نسيت تعمل Remove-Migration. جيت تضيف property جديدة (Address) وعملت Add-Migration AddAddressColumn.

إيه اللي هيحصل؟ (اللخبطة)

الـ EF Core هيقارن الكود الحالي بالـ Snapshot القديم (اللي لسه بيعكس الـ Managing Database Schema with EF Core Migrations اللي المفروض مسحتها). الـ Managing Database Schema with EF Core Migrations الجديدة اللي هتطلع ممكن تكون غلط ومش بتعمل اللي إنت عايزه.

الدرس المستفاد: دايمًا خلي الـ Snapshot متوافق مع الكود. لو عملت Rollback لـ Managing Database Schema with EF Core Migrations، اعملها Remove-Migration بعدها.


سيناريو 6: الرجوع عن جزء من Managing Database Schema with EF Core Migrations (مش كلها)

المشكلة

Managing Database Schema with EF Core Migrations واحدة (AddEmployeeDetails) ضافت Address و PhoneNumber. طبقتها. عايز تشيل Address بس وتخلي PhoneNumber.

هل أعمل Rollback للـ Managing Database Schema with EF Core Migrations كلها؟

لأ! ده هيمسح الاتنين.

الحل

  1. عدّل الكود: امسح property الـ Address بس.
  2. اعمل Managing Database Schema with EF Core Migrations جديدة لوصف الإزالة دي:
    // Create a migration specifically to remove the Address column
    Add-Migration RemoveAddressColumn
    • الـ EF Core هيقارن الكود الحالي (اللي فيه PhoneNumber بس) بالـ Snapshot (اللي فيه الاتنين) ويعمل Migration فيها DropColumn(“Address”).
  3. طبّق الـ Managing Database Schema with EF Core Migrations الجديدة:
    // Apply the migration to remove the column from the database
    Update-Database
    • كده هيتمسح الـ Address column بس.

سيناريو 7: إزالة تأثير Managing Database Schema with EF Core Migrations في النص

المشكلة

عندك تسلسل: A B C. كلهم متطبقين. عايز تلغي تأثير B بس، وتحتفظ بـ A و C.

هل أعمل Rollback لـ B؟

لو عملت Update-Database A، ده هيلغي B و C. مش هينفع.

الحل (بيبقى أصعب شوية)

  1. اعكس تغييرات B في الكود بتاعك: شوف B كانت بتعمل إيه (مثلًا بتضيف column اسمه TempData). روح إنت في الكود بتاعك واعمل عكس التغيير ده (امسح الـ property TempData).
  2. اعمل Managing Database Schema with EF Core Migrations جديدة لوصف العكس ده:
    // Create a migration to revert the changes made by Migration B
    Add-Migration RevertMigrationBChanges // Or e.g., RemoveTempDataColumn
    • الـ EF Core هيقارن الكود الحالي بالـ Snapshot ويعمل Migration بتعمل عكس B (مثلًا DropColumn(“TempData”)).
  3. طبّق الـ Managing Database Schema with EF Core Migrations الجديدة:
    // Apply the reverting migration
    Update-Database

ملخص التعامل مع الـ Migrations بعد تطبيقها:

  • للتعديل البسيط: عدل الكود Add-Migration Update-Database.
  • للإلغاء الكامل لآخر Migration: Update-Database PreviousMigrationName عدل الكود Remove-Migration.
  • لإلغاء جزء من Migration أو Migration في النص: عدل الكود عشان تعمل عكس التغيير اللي مش عايزه Add-Migration جديدة بالمعنى ده Update-Database.
  • القاعدة الذهبية: أوعى تعدل أو تمسح Managing Database Schema with EF Core Migrations files أو الـ Snapshot file يدويًا بعد ما تكون طبقتهم. استخدم أوامر الـ EF Core.