Interview Handbook by Sayed Elmahdy English

المحور 09 · الداتا وقواعد البيانات

Entity Framework Core

DbContext، و Tracking، و Loading، و N+1، و Migrations، و Concurrency، و Dapper، والأداء.

20 سؤالJunior · 6Mid · 9Senior · 510 اختيارات

المفاهيم

هنفهم إيه؟ هنتابع البيانات بين كائنات C# وجداول قاعدة البيانات: إزاي يترجم ، ويتابع التغييرات، ويكتبها، وإمتى طلب بسيط يعمل queries كثيرة أو نتائج مكررة. هنتعلم اختيارات التحميل والتتبع والتزامن والترحيلات مع إبقاء في الصورة.

قبل ما تبدأ: أداة تربط كائنات البرنامج بالجداول. entity كائن يمثل بيانات من المجال، و جلسة عمل قصيرة مع قاعدة البيانات. معرفة بسيطة بالجداول وSQL مفيدة؛ لو ناقصة، ابدأ بمحور 10 بالتوازي.

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

#EF Core و DbContext كـ Unit of WorkEF Core and DbContext as a unit of work

كتابة mapping و متكرر لكل بتستهلك وقت. هو بيحوّل مدعوم لـ queries، ويرجع objects، ويتابع التغييرات ويحفظها. مش بيغنيك عن فهم SQL أو الـ provider.

زي ملف شغل قصير: بتجمع فيه التعديلات المرتبطة، وبعدين تسلّمها مرة واحدة بـ SaveChanges.

using Microsoft.EntityFrameworkCore;

public sealed class StoreDb(DbContextOptions<StoreDb> options) : DbContext(options)
{
    public DbSet<Product> Products => Set<Product>();       // (1)
}
public sealed class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
}

// In Program.cs, with the SQL Server provider installed:
builder.Services.AddDbContext<StoreDb>(o => o.UseSqlServer(connectionString)); // (2)
// In a scoped handler receiving StoreDb db:
db.Products.Add(new Product { Name = "Mug", Price = 120m });
await db.SaveChangesAsync(ct);                             // (3)
🧾 شرح الكود
  1. (1) DbSet query root ومكان إضافة entities، مش شرط يطابق جدول واحد في كل mapping.
  2. (2) AddDbContext بيسجّل scoped context؛ ماتشاركوش بين threads ولا تعمل عمليتين متزامنين عليه. background jobs ممكن تستخدم IDbContextFactory وتعمل dispose لكل context.
  3. (3) unit of work بيحفظ التغييرات المتتبعة. في relational provider، SaveChanges واحدة افتراضياً لو فيه حاجة محتاجة atomicity.
المكسبالتكلفة
LINQ و mapping و tracking و migrationsmaterialization و tracking overhead
parameterization للـ valuesraw SQL المركّب غلط لسه خطر
providers متعددةاختلاف translation والـ types والـ capabilities

generic repository بيكرر DbSet و SaveChanges مش شرط يضيف قيمة. اعمل abstraction لو بيحمي boundary أو business queries حقيقية.

#Fluent API و schema ownershipFluent API and schema ownership

conventions بداية كويسة، لكنها مش بديل تعريف precision والعلاقات والـ indexes. Fluent بتفصل persistence rules عن الـ entity.

using Microsoft.EntityFrameworkCore.Metadata.Builders;

public sealed class ProductConfig : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> e)
    {
        e.ToTable("Products");
        e.HasKey(p => p.Id);                              // (1)
        e.Property(p => p.Name).HasMaxLength(200).IsRequired();
        e.Property(p => p.Price).HasPrecision(18, 2);       // (2)
        e.HasIndex(p => p.Name);                          // (3)
    }
}
// Inside StoreDb:
// protected override void OnModelCreating(ModelBuilder b) =>
//     b.ApplyConfigurationsFromAssembly(typeof(StoreDb).Assembly);
🧾 شرح الكود
  1. (1) key ثابت للهوية؛ unique business rules ليها unique constraints منفصلة.
  2. (2) طول الاسم و precision للسعر بدل defaults غير مقصودة. validation في التطبيق برضه مطلوبة.
  3. (3) index حسب access pattern؛ مش كل field محتاج index. migration هي اللي تطبق تغيير .

للعلاقات استخدم HasMany و WithOne و HasForeignKey وحدد DeleteBehavior بوعي. database-first بيعمل scaffold من schema موجودة؛ code-first بيقود schema من model و migrations. اختار مصدر حقيقة واحد وماتعدّلش generated scaffold عشوائي من غير خطة إعادة توليد.

#Loading strategies و projectionLoading strategies and projection

مثال خطوة بخطوة
  1. صفحة الطلب تحتاج رقم الطلب واسم العميل وعدد البنود؛ مش محتاجة تفاصيل كل بند. العلاقة Order.Lines اسمها : خاصية تمثل بيانات مرتبطة بالطلب.
  2. Eager loading يعني تطلب العلاقات المطلوبة مقدمًا؛ explicit loading يعني تطلب علاقة بعينها لاحقًا بوضوح؛ lazy loading قد يطلبها تلقائيًا عندما تقرأ الخاصية.
  3. يعني ترسم شكل الرد باستخدام Select: الرقم والاسم والعدد فقط. راجع الناتج؛ اختيار شكل أصغر قد يوفر نقل كائنات كثيرة أصلًا.

عرض order summary مش محتاج تحميل كل product وكل payment. اختار أقل داتا تحقق المطلوب، وخلي الرحلات للـ database ظاهرة.

// These snippets assume an Order model with Lines and Customer navigations.
var entity = await db.Orders.Include(o => o.Lines)
    .SingleAsync(o => o.Id == id, ct);                     // (1)

await db.Entry(entity).Reference(o => o.Customer).LoadAsync(ct); // (2)

var summary = await db.Orders.Where(o => o.Id == id)
    .Select(o => new { o.Id, o.Total, LineCount = o.Lines.Count })
    .SingleAsync(ct);                                     // (3)
🧾 شرح الكود
  1. (1) eager loading بـ Include؛ ThenInclude لمستوى تاني. يقدر يستخدم query واحدة أو split.
  2. (2) explicit loading: request إضافي مقصود لعلاقة؛ Collection للـ collections.
  3. (3) projection تتحول لـ SQL للأعمدة والـ count المطلوبين؛ مفيش entity في الناتج، فمفيش entity tracking.

lazy loading يعمل query لما navigation تتقري. الـ proxies محتاجة package وإعدادات و navigations قابلة للـ override؛ ILazyLoader بديل. الاتنين ممكن يخبّوا ويسببوا . الـ projection اللي جواها entity، زي new { Order = o }، ممكن تعمل tracking للـ entity دي.

#Tracking و AsNoTracking و identity resolutionTracking, no-tracking and identity resolution

لو هتعدّل entity وتحفظها، محتاج يعرف الأصل والتغيير. لو بتعرض catalog بس، bookkeeping ده غالباً مش مطلوب.

tracking زي نسخة قبل التعديل: بتقارن الجديد بالأصل بدل ما تخمّن أي خانات اتغيرت.

var product = await db.Products.SingleAsync(p => p.Id == id, ct); // (1)
product.Price = newPrice;
await db.SaveChangesAsync(ct);

var catalog = await db.Products.AsNoTracking()
    .OrderBy(p => p.Id).Take(20).ToListAsync(ct);             // (2)

var graph = await db.Orders.AsNoTrackingWithIdentityResolution()
    .Include(o => o.Customer).Take(20).ToListAsync(ct);       // (3)
🧾 شرح الكود
  1. (1) tracked entity بتبقى Unchanged وبعد اكتشاف التعديل Modified؛ فيه كمان Added و Deleted و Detached. SaveChanges بيعمل DetectChanges افتراضياً.
  2. (2) full entities من غير attachment للـ context؛ تعديلها مش هيتحفظ تلقائي.
  3. (3) temporary identity map يجمع نفس key في object واحد جوه النتيجة، من غير tracking دائم للـ context.

tracking query ممكن ترجع object موجود بالفعل في context بقيمه الحالية؛ مش refresh مضمون. للـ detached updates اعمل attach بحذر وحدد الحقول المسموحة و original concurrency . Update على graph جاي من client ممكن يعلّم حقول زيادة ويعمل over-posting.

#N+1 و cartesian explosion و split queriesN+1, cartesian explosion and split queries

مثال خطوة بخطوة
  1. افترض عرض 10 طلبات مع عدد بنود كل طلب، مع lazy loading مفعّل. أول استعلام يجيب الطلبات، ثم قراءة Lines لكل طلب تطلق استعلامًا إضافيًا.
  2. الإجمالي 1 + 10 = 11 استعلامًا. N هو عدد الطلبات؛ من هنا جاء الاسم . مع 100 طلب قد يصبحوا 101 رحلة بدل 11.
  3. لو المطلوب العدد فقط، Select يحسب Count في قابل للترجمة ويعيد الأعداد. أما cartesian explosion فمشكلة أخرى: علاقات متعددة تكرر الصفوف داخل نتيجة كبيرة، حتى لو عدد الاستعلامات قليل.

endpoint ممكن يبقى بطيء من عدد الرحلات أو من حجم query واحدة. دول مش نفس المشكلة.

❌ مع lazy loading: query لكل order
var orders = await db.Orders.ToListAsync(ct);
foreach (var order in orders)
    Console.WriteLine(order.Lines.Count);
✅ count جوه SQL
var counts = await db.Orders
    .Select(o => new { o.Id, Count = o.Lines.Count })
    .ToListAsync(ct);
var orders = await db.Orders
    .Include(o => o.Lines)
    .Include(o => o.Payments)
    .AsSplitQuery()                                       // (1)
    .OrderBy(o => o.Id).Take(20).ToListAsync(ct);
🧾 شرح الكود
  1. (1) sibling collections في JOIN ممكن تنتج 10 lines × 5 payments = 50 row للـ order. split بيجيب roots والـ collections في statements منفصلة ويركّبها.

split بيقلل التكرار لكن بيزود round trips والـ buffering. مجرد على Read Committed مش snapshot موحّد؛ لو الاتساق بين statements مطلوب، اختار snapshot أو serializable مناسبين والـ cost بتاعتهم. راقب SQL logs وعدد queries والـ actual plan قبل الاختيار. ماتفعّلش sensitive data logging في production لمجرد التشخيص.

#ExecuteUpdate و ExecuteDelete في EF Core 10Set-based changes in EF Core 10

تعديل آلاف entities بعد تحميلها بيكلف memory و tracking و كتير. set-based operations بتعمل التغيير في الـ database مباشرة.

int changed = await db.Orders.Where(o => o.Status == "Pending")
    .ExecuteUpdateAsync(setters =>                         // (1)
    {
        setters.SetProperty(o => o.Status, "Expired");
        if (stampTime)
            setters.SetProperty(o => o.UpdatedAt, now);
    }, ct);

int deleted = await db.Orders
    .Where(o => o.Status == "Draft" && o.CreatedAt < cutoff)
    .ExecuteDeleteAsync(ct);                              // (2)
🧾 شرح الكود
  1. (1) 10 يقبل regular ببلوك و if لبناء setters. تعبيرات properties نفسها لسه لازم تتترجم لـ SQL.
  2. (2) delete فوري، مش مستني SaveChanges. الاتنين بيرجعوا affected rows.
قبل EF 10EF 10
expression lambda بس للـ setter builder؛ الشرطية محتاجة تركيب expressionregular lambda تسهّل conditional setters
الـ tracker مش بيتحدّث، و SaveChanges interceptors مش بتشتغل؛ command interceptors والـ database triggers ممكن تشتغل. مفيش check تلقائي: ضيفه في WHERE وافحص العدد. كل call منفصلة؛ صريحة لو عايز تجمعهم. ماتخلطش tracked objects قديمة مع bulk changes من غير reload أو context جديد.

#Compiled queries و DbContext poolingBonusCompiled queries and DbContext pooling

اختياري بس بيفرق: مش هيتسأل فيه في كل انترفيو، بس معرفته بتعمّق فهمك وبتميّزك عن غيرك.

بعد إصلاح والـ indexes، القياس ممكن يبيّن إن query cache lookup أو إنشاء contexts بقى تكلفة واضحة. ساعتها التحسينات دي مفيدة.

// A field inside a service class; all contexts must use the same EF model.
private static readonly Func<StoreDb, int, Task<Product?>> FindProduct =
    EF.CompileAsyncQuery((StoreDb db, int id) =>
        db.Products.AsNoTracking().FirstOrDefault(p => p.Id == id)); // (1)

// Usage:
var product = await FindProduct(db, id);

// Alternative registration to AddDbContext:
builder.Services.AddDbContextPool<StoreDb>(
    o => o.UseSqlServer(connectionString));                // (2)
🧾 شرح الكود
  1. (1) عنده query cache أصلاً. compiled بيتخطى lookup والمقارنة، مش بيحوّل query وحشة لـ SQL أحسن. المثال point lookup؛ queries اللي محتاجة cancellation أو model مختلف تتصمم حسب overload المناسب.
  2. (2) إعادة استخدام context instances بعد reset لحالة EF؛ مش هي connection pooling بتاعة driver.

OnConfiguring مش بيتنفذ لكل lease. أي tenant ID أو custom mutable state لازم يتضبط بأمان لكل استخدام ويتنضّف؛ EF مش بيضمن reset لأي state إنت أضفته أو connection state إنت غيرته. ماتستخدمش pooled context كـ singleton مشترك، وماتعملش optimization قبل benchmark واقعي.

#Migrations في production و expand-contractProduction migrations and expand-contract

change ممكن يقفل جدول أو يكسر النسخة القديمة أثناء rolling deploy. migration ملف قابل للمراجعة، مش إذن لتنفيذه تلقائي من كل instance.

# Requires matching dotnet-ef tools and EF Core Design package.
dotnet ef migrations add AddMobileNumber
dotnet ef migrations script --idempotent --output migrate.sql
# Alternative deployment artifact; self-contained requires an explicit option.
dotnet ef migrations bundle --self-contained -r linux-x64 -o efbundle

راجع وجرّبه على حجم قريب من production، وخطط للـ backup والـ forward repair والـ lock duration. نفّذ script أو bundle في deployment step واحدة بصلاحية مخصصة. الحديث فيه migration locking لبعض providers، لكنه مش بيحل compatibility أو صلاحيات التطبيق أو كل أخطار .

  1. Expand: ضيف MobileNumber nullable وسيب Phone.
  2. انشر كود متوافق. طول ما فيه writers قديمة، القديم هو المصدر المعتمد أو استخدم آلية sync موثوقة.
  3. بعد خروج writers القديمة، اكتب في الاتنين واعمل backfill على batches مع معالجة concurrent writes.
  4. اتحقق من اكتمال النقل، حوّل القراءة للجديد، وراقب.
  5. Contract: شيل القديم في release لاحقة لما rollback والـ consumers القديمة مايحتاجوهوش.

code-first بيولّد migration بمقارنة model بالـ snapshot، مش diff شامل للـ live database. خلّي ownership واضح وراجع drift. database-first يناسب schema بتديرها جهة تانية. ماتخلطش EnsureCreated مع migrations لنفس database.

#Optimistic و pessimistic concurrencyOptimistic and pessimistic concurrency

اتنين بيعدّلوا نفس product؛ آخر حفظ ماينفعش يمسح تعديل الأول بصمت. بيكتشف version اتغير وقت الكتابة.

// On an order entity, add: public byte[] Version { get; set; } = [];
// SQL Server configuration:
modelBuilder.Entity<Order>().Property(o => o.Version).IsRowVersion(); // (1)

try
{
    order.Status = "Confirmed";
    await db.SaveChangesAsync(ct);                         // (2)
}
catch (DbUpdateConcurrencyException)
{
    return Results.Conflict(new { code = "order.changed" }); // (3)
}
🧾 شرح الكود
  1. (1) rowversion database-generated في Server؛ مش date أو time. provider تاني ممكن يحتاج application-managed بـ IsConcurrencyToken.
  2. (2) UPDATE أو DELETE فيها key و original version في WHERE؛ لو expected row مش اتأثر يرمي exception.
  3. (3) رجّع conflict أو اعمل merge واضح؛ retry أعمى ممكن يكتب فوق قرار المستخدم. reload و revalidate لو هتعيد العملية.
-- PostgreSQL: inside a short transaction, lock before read-modify-write.
SELECT id, available FROM products WHERE id = @id FOR UPDATE;

-- For a simple reservation, an atomic statement can be enough.
UPDATE products SET available = available - @qty
WHERE id = @id AND available >= @qty;

pessimistic locking يمنع المنافس من التعديل لحد تنتهي، بس ممكن يسبب blocking و . EF مفيهوش portable lock hint لكل providers. للـ conditional update تحقق من qty موجبة وافحص affected rows؛ صفر ممكن يعني missing row أو stock مش كفاية.

#Transactions والـ execution strategyTransactions and execution strategies

SaveChanges واحدة غالباً كفاية. لو عندك أكتر من save أو bulk statement لازم ينجحوا مع بعض، حدد boundary، ومع retry strategy أعد الوحدة كلها.

// probe and factory use identical provider options with retries configured.
await using var probe = await factory.CreateDbContextAsync(ct);
var strategy = probe.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>                    // (1)
{
    await using var db = await factory.CreateDbContextAsync(ct);
    await using var tx = await db.Database.BeginTransactionAsync(ct); // (2)
    var order = await db.Orders.SingleAsync(o => o.Id == orderId, ct);
    order.Status = "Confirmed";
    await db.SaveChangesAsync(ct);
    await db.OrderLines.Where(l => l.OrderId == orderId)
        .ExecuteUpdateAsync(s => s.SetProperty(l => l.IsReserved, true), ct);
    await tx.CommitAsync(ct);                              // (3)
});
🧾 شرح الكود
  1. (1) الـ كلها قابلة لإعادة التنفيذ؛ context جديد لكل محاولة يمنع tracked state من محاولة فاشلة.
  2. (2) transaction جوه strategy، مش العكس؛ disposal قبل commit ناجح يسيبها من غير commit ويعمل rollback حسب provider.
  3. (3) لو connection ضاع وقت commit، النتيجة ممكن تكون unknown. التصميم هنا بيضبط قيم ثابتة؛ إنشاء payments محتاج أو verifySucceeded strategy.

ماتحطش email أو gateway charge جوه retry delegate؛ استخدم في نفس transaction. SaveChanges جوه transaction قائمة ممكن يستخدم savepoint؛ Server MARS بيمنع savepoints الآلية. cancellation مش إثبات إن commit ماحصلش. ما تعملش retry لكل exception، خصوصاً validation و conflicts.

#Raw SQL و query filters المسماةSafe raw SQL and named query filters

query معقدة ممكن تحتاج مباشر. ده escape hatch، مش سبب نتخلى عن parameterization أو tenant .

var products = await db.Products
    .FromSql($"SELECT * FROM Products WHERE Price > {minimum}") // (1)
    .AsNoTracking().ToListAsync(ct);

var prices = await db.Database
    .SqlQuery<decimal>($"SELECT Price AS Value FROM Products WHERE Id = {id}")
    .ToListAsync(ct);                                     // (2)

// In OnModelCreating; tenantId is current context state.
modelBuilder.Entity<Order>()
    .HasQueryFilter("SoftDelete", o => !o.IsDeleted)
    .HasQueryFilter("Tenant", o => o.TenantId == tenantId);  // (3)
var archived = await db.Orders.IgnoreQueryFilters(["SoftDelete"])
    .ToListAsync(ct);                                     // (4)
🧾 شرح الكود
  1. (1) FromSql تستقبل interpolated FormattableString مباشرة وتحول minimum لـ parameter. entity query لازم ترجع الأعمدة المطلوبة للـ mapping؛ المثال Product البسيط.
  2. (2) SqlQuery لنتائج scalar أو unmapped types؛ Value alias مفيد لو هتركّب فوق scalar query. stored procedures في SQL Server مش composable زي SELECT.
  3. (3) named filters في 10؛ قبلها ادمج الشروط بـ && في filter واحدة.
  4. (4) بيعطّل soft delete بس وبيسيب Tenant. IgnoreQueryFilters من غير أسماء بيعطّل الكل.
ماتحوّلش interpolation لـ string وتبعته لـ FromSqlRaw. values تتعمل parameters؛ identifiers زي column name لازم allowlist. filters مش security boundary كامل: raw commands و bypass paths والـ writes محتاجين checks، و database row-level security ممكن تضيف دفاع. required مع filters ممكن تخفي parent rows بسبب INNER JOIN.

#Owned و complex و JSON و اختيار أداة الداتاValue mapping and choosing a data tool

عنوان الشحن value داخل order، مش لازم يبقى entity مستقلة. اختار mapping حسب الهوية وطريقة query، وبعدها اختار أداة مناسبة للـ workload.

// EF Core 10, with a relational provider supporting complex JSON mapping.
modelBuilder.Entity<Order>()
    .ComplexProperty(o => o.ShippingAddress, address => address.ToJson()); // (1)

// Earlier owned-entity approach, as an ALTERNATIVE mapping:
// modelBuilder.Entity<Order>().OwnsOne(o => o.ShippingAddress);
🧾 شرح الكود
  1. (1) complex type من غير entity identity؛ 10 وسّع دعمه وفيه mapping. owned type entity ليها owner و identity حتى لو implicit، وممكن table splitting أو JSON حسب provider.

JSON يقلل جداول لبيانات بتتقري مع بعض؛ querying والـ indexing والـ constraints وتحديث أجزاء الوثيقة يعتمدوا على provider و database version. ماتحطش عملاق بيتعدل كله أو محتاج joins وتقارير كتير في JSON لمجرد المرونة.

EF CoreDapper
Query وترجمة provider؛ raw متاحSQL بإيدك و object mapping بسيط
Writestracking و unit of work و migrationsتدير SQL والـ والـ concurrency بنفسك
إمتىbusiness وتطوير modelquery محددة ومعقدة وتحكم SQL مطلوب
EF6EF Core 10
المنصة أقدم غالباً في تطبيقات legacy؛ بعض إصداراته تدعم modern ORM معاد تصميمه للـ modern .NET
ModelCode First و designer workflowscode model و migrations أو scaffolding؛ مفيش EDMX designer
الانتقال و behaviors مختلفةmigration مشروع مستقل؛ اختبر translation والـ loading والـ

مفيش قاعدة إن Dapper أسرع دايماً؛ SQL والـ round trips والـ payload غالباً أهم. خلط الأداتين محتاج shared connection و transaction لو العمليات لازم تبقى atomic.

مراجع للمراجعة: تغييرات EF Core 10، و SQL queries الآمنة، و transactions، و migration deployment، و compiled queries والـ pooling. المقتطفات مستقلة لتوضيح الفكرة؛ أمثلة orders بتفترض DbSet والـ entities والعلاقات المذكورة متعرفة في context مناسبة، مش إضافة تلقائية لمثال Product الأول.

أسئلة الانترفيو

راجع فهمك

ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟

9.1What does EF Core do, and what are its main trade-offs?Junior

بيترجم ويعمل mapping و tracking و migrations. الإنتاجية أعلى، بس لازم تراجع والـ provider behavior.

الإجابة النموذجية (English)

I use to map a domain model to a database, translate supported , results and persist tracked changes. It improves productivity through type-safe queries, relationships and migrations. The trade-offs are tracking and materialization overhead, translation limits and the possibility of inefficient queries. Provider differences still matter, so I inspect generated and execution plans. I use projections or targeted SQL when they better express a particular read workload.

9.2Why is DbContext a unit of work, and how long should it live?Junior

بيجمع tracked changes ويعمل save كوحدة. عمره قصير، غالباً scoped request؛ مش thread-safe ولا singleton.

الإجابة النموذجية (English)

I treat as a short-lived unit of work because it tracks related entity changes and submits them through SaveChanges. In a web application it is commonly scoped to a request, while a background worker can create one per operation through a factory. I dispose it promptly and never share it across concurrent operations. Long-lived contexts accumulate state and stale entities, so I avoid using them as an application cache or singleton.

9.3What is the Fluent API used for?Junior

تعريف keys والعلاقات والـ precision والـ indexes بشكل صريح. settings لوحدها مش بتغير database قبل migration.

الإجابة النموذجية (English)

I use Fluent to configure mapping details that conventions do not capture, such as keys, relationship ownership, delete behavior, string lengths, decimal precision and indexes. Separate configuration classes keep persistence concerns organized without filling entities with attributes. The model configuration describes intent; a migration or another deployment process applies it to the database. I still validate user input in the application, because database mapping rules are not a complete input-validation strategy.

9.4How do eager, lazy and explicit loading differ?Junior

Include يحدد العلاقات مسبقاً، lazy عند القراءة، explicit query مقصودة لعلاقة. غالباً أحسن للـ reads.

الإجابة النموذجية (English)

I use eager loading when I know I need related entities, expressing that through Include and ThenInclude. Lazy loading fetches a when accessed, which is convenient but can hide queries. Explicit loading deliberately loads a relationship through the context entry . For read endpoints I often prefer into a because it fetches only the required values. I choose based on the required shape and measured query behavior, not convenience alone.

9.5When should you use AsNoTracking?Junior

للقراية من غير تعديل وحفظ. مفيهاش entities مش tracked؛ projection فيها entity ممكن tracked.

الإجابة النموذجية (English)

I use AsNoTracking when I need entity results for reading but will not modify and save them through that context. It avoids persistent change-tracker bookkeeping. A pure scalar or already has no entities to track, but a projection containing an entity can still track it. If repeated keys in a read-only graph should share one object instance, I consider AsNoTrackingWithIdentityResolution. I measure memory and query behavior rather than assuming every no-tracking query must be faster.

9.6What is the difference between code-first and database-first?Junior

الأول model و migrations يقودوا ؛ التاني scaffold من schema متدارة برّه. مين صاحب schema أهم من اسم الطريقة.

الإجابة النموذجية (English)

I use code-first when the application team owns the model and evolves the through reviewed migrations. Database-first starts from an existing database and scaffolds context and entity code, which fits a separately managed schema. Either approach still needs careful mapping and deployment. I establish a clear schema owner, manage customizations so regeneration does not overwrite them, and review drift. Code-first migration generation compares the model with its snapshot, not with every live database difference.

9.7How do you diagnose and fix an N+1 query problem?Mid

راقب query count ونفس ب IDs مختلفة. أو Include يحلوا تحميل علاقة لكل row؛ مش Task.WhenAll على context واحدة.

الإجابة النموذجية (English)

I look at database command logs or for one list query followed by repeated queries with different identifiers. That usually comes from lazy loading or explicit loading inside a loop. I replace it with a that computes the required values in , or eager loading if entities are needed. I then verify query count, payload and . Running those queries concurrently on one is invalid and does not address the underlying access pattern.

9.8When do split queries help, and what do they cost?Mid

sibling Includes ممكن تضرب الصفوف. split يقلل duplication لكنه يزود queries ومش snapshot تلقائي.

الإجابة النموذجية (English)

I consider split queries when multiple collection includes create a large cross product and duplicate parent columns. Separate statements can transfer much less data, but add round trips and buffering. They can also observe different database states. A Read Committed alone does not guarantee one shared snapshot; stronger may be necessary if consistency across statements matters. I compare the real execution plans, row counts and before making split queries a default.

9.9How do ExecuteUpdate and ExecuteDelete differ from SaveChanges?Mid

بيتنفذوا فوراً في database من غير entities. tracker و concurrency checks و SaveChanges hooks مش تلقائي.

الإجابة النموذجية (English)

I use ExecuteUpdate and ExecuteDelete for set-based changes without materializing entities. They execute immediately and return affected row counts rather than waiting for SaveChanges. They bypass change tracking and SaveChanges interceptors, although database triggers and command interception can still apply. Automatic concurrency- checks are not included, so I add predicates and check counts when needed. Multiple calls need an explicit for shared atomicity, and tracked entities can become stale afterwards.

9.10What changed in EF Core 10 bulk update lambdas?Mid

setter builder بقى يقبل regular ب if؛ property expressions لسه لازم تتترجم.

الإجابة النموذجية (English)

In 10 I can build ExecuteUpdate setters with a regular , including a block and conditional statements. Earlier expression-only construction made conditional updates harder to compose. The important distinction is that this flexibility applies to building the setter list; expressions describing database values must still be translatable. The operation remains an immediate set-based update with the same tracking, and concurrency considerations, so the new syntax does not remove those responsibilities.

9.11How does optimistic concurrency work in EF Core?Mid

original في WHERE؛ صفر rows متوقعة يرمي DbUpdateConcurrencyException. Server rowversion مثال.

الإجابة النموذجية (English)

I configure a concurrency , such as Server rowversion or an application-managed version. remembers the original value and includes it with the key in update and delete predicates. If another writer changes that version, the expected row is not affected and EF raises DbUpdateConcurrencyException. I then return a conflict or reload and reconcile according to business rules. I do not blindly retry a user edit, because that could overwrite the newer decision.

9.12When would you use pessimistic locking or a conditional update?Mid

pessimistic وقت contention عالي ومع قصيرة؛ counter بسيط conditional UPDATE غالباً يكفي.

الإجابة النموذجية (English)

I consider pessimistic locking when contention is high and the operation needs a short read-modify-write sequence protected from competing writers. The syntax and lock behavior depend on the database, and blocking and are real costs. For a simple stock reservation I often prefer one conditional update that decrements stock only when enough remains. I validate positive quantity and inspect affected rows. More complex may still need a wider or stronger .

9.13How can you use raw SQL without SQL injection?Mid

FromSql و SqlQuery مع interpolation مباشرة بيعملوا parameters للـ values. identifiers لازم allowlist.

الإجابة النموذجية (English)

I pass interpolated directly to FromSql or SqlQuery so receives the format and values separately and creates parameters. I never concatenate untrusted values into a string sent to a raw . Database identifiers cannot generally be parameters, so dynamic column names come from a strict allowlist. I also check mapping completeness and composability: an entity query needs its mapped columns, and a SQL Server stored procedure cannot be composed like an ordinary SELECT.

9.14What are global query filters and EF Core 10 named filters?Mid

افتراضيات للـ soft delete والـ tenant. named filters تعطل واحد بس؛ مش حماية لكل raw command أو write.

الإجابة النموذجية (English)

I use global filters for default entity predicates such as soft deletion and tenant scope. 10 lets me name filters and selectively disable one, for example including deleted orders while keeping tenant filtering. Earlier versions commonly combined predicates in one expression. I treat filters as defense in depth rather than a complete security boundary, because bypass methods, raw commands and writes need separate controls. I also test required , whose joins can unexpectedly remove rows.

9.15What is the difference between owned and complex types?Mid

owned entity ليها identity و owner؛ complex value من غير identity. mapping حسب provider و version.

الإجابة النموذجية (English)

I use owned types when modeling an entity whose lifetime and identity depend on its owner, even if that identity is implicit. Complex types model values without entity identity, such as an address. 10 expands complex-type support, including mapping with compatible providers. I choose table columns or JSON according to query, indexing and update requirements. I would not assume that every provider supports identical mapping or that JSON automatically improves performance.

9.16How do you deploy migrations safely during rolling releases?Senior

script أو bundle متراجع؛ expand و retire old writers و backfill و switch و contract. locking لوحده مش compatibility.

الإجابة النموذجية (English)

I review a generated script or bundle and run it as a controlled deployment step with dedicated privileges. I test locking, duration, data conversion and recovery on realistic volumes. For incompatible changes I expand the , deploy compatible code, retire old writers, backfill safely, switch reads and contract later. Migration locking can reduce simultaneous execution, but does not make a destructive change compatible with old instances. I keep clear migration ownership and a forward-repair plan.

9.17How do explicit transactions interact with connection retries?Senior

كاملة جوه execution strategy. replay محتاج context نظيفة و side effects آمنة؛ commit ممكن نتيجته unknown.

الإجابة النموذجية (English)

I run the whole inside the execution strategy so a transient failure can replay a complete unit. I create fresh context state for each attempt and keep external side effects out of the retry , using an where needed. I also consider commit ambiguity: a connection failure during commit does not prove rollback. Operations need or outcome verification. I avoid broad retries for business conflicts and choose according to the actual .

9.18When are compiled queries and context pooling worthwhile?Senior

بعد القياس وإصلاح . compiled يقلل lookup، pooling setup؛ custom tenant state خطر لو مش متضبط.

الإجابة النموذجية (English)

I use them only when profiling shows the targeted overhead matters. already caches query shapes; compiled queries bypass cache lookup work but do not improve plans. Context pooling reuses initialized instances and differs from connection pooling. I check that the model is stable and that tenant identifiers or custom state are correctly assigned and reset for every lease. Neither optimization permits concurrent operations on one context, and both require representative benchmarks.

9.19Would you replace EF Core with Dapper to fix a slow endpoint?Senior

ابدأ بالـ والـ indexes والـ payload والـ trips. Dapper أداة لتحكم محدد مش علاج تلقائي.

الإجابة النموذجية (English)

I first identify whether the bottleneck is database execution, missing indexes, excessive data, queries or application overhead. A and a suitable index may solve it without changing tools. I use Dapper when handwritten gives clearer control for a specific query, then compare equivalent workloads. Replacing the while keeping the same poor SQL will not fix the cause. If both tools participate in one write operation, I explicitly coordinate their connection and .

9.20Why is upgrading EF6 to EF Core more than changing a package?Senior

إعادة تصميم مش version drop-in؛ والـ translation والـ loading والـ migrations والـ provider behaviors محتاجة مراجعة.

الإجابة النموذجية (English)

I treat as a redesigned rather than a drop-in EF6 package upgrade. Model configuration, query translation, lazy-loading behavior and provider capabilities differ, and designer workflows do not transfer directly. I inventory the existing mappings and queries, port a representative vertical slice and compare generated and behavior. I plan ownership and migration history carefully rather than recreating a production database. Integration tests against the real provider are essential for confidence.

اختبر نفسك

إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.

النتيجة: 0 / 0

Can two queries run concurrently on one DbContext?

tracking مش سبب منع التزامن؛ context نفسها مش thread-safe.

Which projection can still track an entity by default?

وجود entity جوه الناتج يسمح tracking ليها.

What can AsSplitQuery reduce?

بتفصل collections، بس غالباً queries أكتر.

After ExecuteUpdateAsync, an already tracked entity is...

الـ bulk operation مش بتزامن tracker.

What does EF Core 10 add to ExecuteUpdate setter construction?

الـ الجديدة لبناء setters؛ translation لسه مطلوبة.

A configured version changed since the entity was loaded. Saving normally raises...

original في WHERE ماعادش مطابق.

Where should an explicit transaction start when retries are enabled?

علشان الوحدة كلها تتعاد، مع side effects آمنة.

IgnoreQueryFilters(["SoftDelete"]) in EF Core 10...

named filters بتسمح تعطيل انتقائي.

Which raw SQL pattern parameterizes the input value?

FromSql يستقبل format و values، مش string مركبة مسبقاً.

What does DbContext pooling reuse?

مش هي connection pooling، والـ custom state مسؤوليتك.