زي ما اتفقنا، بعد ما بنعمل الـ 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).
- الـ EF Core بيقدر يراقب (
خطوات عمل Migration
- تنزيل الـ 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” ونزلها.
- عن طريق الـ Package Manager Console (
- عمل الـ 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
- الـ
Add-Migration
: ده الأمر نفسه. - الـ
InitialCreate
: ده الاسم اللي إنت بتختاره للـ Managing Database Schema with EF Core Migrations دي. كويس تختار اسم بيوصف التغيير اللي حصل (مثلًاAddProductReviews
أوRenameCustomerAddress
). أول Managing Database Schema with EF Core Migrations في المشروع غالبًا بنسميهاInitialCreate
.
- الـ
إزاي الـ EF Core بيعرف التغيير؟ الـ Snapshot السحري!
لما بتنفذ أمر Add-Migration
، الـ EF Core بيعمل كذا حاجة ذكية عشان يعرف إيه اللي اتغير بالظبط:
- بيعمل Folder: لو مش موجود، بينشئ فولدر جديد في الـ Project بتاعك اسمه
Migrations
. - بيعمل ملف الـ Migration File: جوه فولدر
Migrations
، بيعمل ملف C# جديد. اسم الملف ده بيكون عبارة عن رقم timestamp (عشان يضمن إنهunique
ومترتب صح) زائد الاسم اللي إنت اخترته (زي20231027103015_InitialCreate.cs
). الملف ده جواهclass
بيورث منMigration
وفيه اتنينmethods
مهمين:- الـ
Up()
: دي فيها الكود اللي بيطبق التغييرات بتاعتك على الداتابيز (زيCreateTable
). - الـ
Down()
: دي فيها الكود اللي بيلغي التغييرات دي لو حبيت ترجع في كلامك (زيDropTable
).
- الـ
- بيعمل/يحدث ملف الـ 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 شايفها بعد التغيير اللي إنت لسه عامله. هو بيوصف كل الـ
- مثال كاميرا المراقبة: تخيل إن الـ Snapshot هو آخر تسجيل فيديو لكاميرا المراقبة اللي بتصور الـ Models بتاعتك. الـ EF Core هو الحارس اللي بيراقب. لما بتعمل تغيير في الكود وتقوله
Add-Migration
، الحارس ده بيبص على الوضع الحالي ويقارنه بآخر تسجيل (الـ Snapshot). بيكتب تقرير (الـ Managing Database Schema with EF Core Migrations file) بكل التغييرات اللي شافها، وبعدين ياخد تسجيل جديد للوضع الحالي ويحدث بيه شريط الفيديو بتاعه (يحدّث الـ Snapshot).
تفاصيل الـ Managing Database Schema with EF Core Migrations File
- الـ Timestamp: بيتحط في اسم الفايل عشان يضمن إن كل Managing Database Schema with EF Core Migrations ليها اسم فريد، وعشان الـ EF Core يعرف يطبقهم بالترتيب الصح.
- الـ Partial Class: الـ
class
بتاع الـ Managing Database Schema with EF Core Migrations بيكونpartial
. دي ميزة في الـ C# بتسمحلك لو حبيت تزود كود مخصص للـ Managing Database Schema with EF Core Migrations دي في ملف تاني من غير ما تعدل في الملف الأصلي اللي الـ EF Core عمله. (مش بنحتاجها كتير غالبًا).
شكل الـ 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
، إزاي تتأكد إن الدنيا تمام؟
- افتح الداتابيز وشوف بنفسك: استخدم SSMS أو أي أداة تانية، واعمل
Refresh
وشوف الـ Tables والـColumns
الجديدة. - (اختياري) استخدم SQL Server Profiler: شغل الـ
Profiler
قبلUpdate-Database
عشان تشوف أوامر الـ SQL اللي اتنفذت. - الأهم: بص على جدول
__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
.
الحل (الطريقة الصح)
- عدّل الكود: غير اسم الـ
property
فيEmployee.cs
لـEmpName
. - اعمل Managing Database Schema with EF Core Migrations جديدة:
// Create a new migration describing the rename operation Add-Migration RenameEmployeeNameColumn
- الـ EF Core هيقارن الكود الحالي بالـ Snapshot القديم ويعمل Migration فيها RenameColumn.
- طبّق الـ Managing Database Schema with EF Core Migrations الجديدة:
// Apply the new migration to the database Update-Database
- كده اسم الـ column هيتغير في الداتابيز وهيتسجل في
__EFMigrationsHistory
.
- كده اسم الـ column هيتغير في الداتابيز وهيتسجل في
سيناريو 2: محاولة مسح Migration File يدويًا بعد تطبيقها (الطريقة الغلط تمامًا!)
المشكلة
عملت الخطوات اللي فاتت، بعدين رحت مسحت ملف ..._RenameEmployeeNameColumn.cs
يدويًا.
ليه دي طريقة غلط ومدمرة؟
الداتابيز اتغيرت، واسم الـ Managing Database Schema with EF Core Migrations متسجل في __EFMigrationsHistory
، والـ Snapshot اتغير. مسح الملف بس بيعمل لخبطة تامة وهيخلي الـ EF Core يتجنن المرة الجاية. أوعى تعمل كده.
سيناريو 3: الطريقة الصح للإلغاء/الرجوع (Rollback)
المشكلة
عملت RenameEmployeeNameColumn
وطبقتها، وعايز أرجع في كلامي وأرجع الاسم لـ Name
تاني في الداتابيز والكود.
الحل (الخطوات الصح بالترتيب)
- اعمل 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
.
- كده الداتابيز هترجع زي ما كانت بعد
- عدّل الكود بتاعك: رجّع اسم الـ
property
فيEmployee.cs
لـName
. - امسح الـ 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
).
الحل
- اعمل 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 المتطبقة بالترتيب العكسي.
- ده هينفذ الـ
- (اختياري) امسح الـ 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 كلها؟
لأ! ده هيمسح الاتنين.
الحل
- عدّل الكود: امسح
property
الـAddress
بس. - اعمل 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”).
- طبّق الـ 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
. مش هينفع.
الحل (بيبقى أصعب شوية)
- اعكس تغييرات B في الكود بتاعك: شوف
B
كانت بتعمل إيه (مثلًا بتضيفcolumn
اسمهTempData
). روح إنت في الكود بتاعك واعمل عكس التغيير ده (امسح الـproperty
TempData
). - اعمل 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”)).
- طبّق الـ Managing Database Schema with EF Core Migrations الجديدة:
// Apply the reverting migration Update-Database
- كده لغيت تأثير
B
بـ Managing Database Schema with EF Core Migrations جديدة، من غير ما تلمسA
أوC
.
- كده لغيت تأثير
ملخص التعامل مع الـ 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.