المفاهيم
هنفهم إيه؟ هنتابع البيانات بين كائنات 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) DbSet query root ومكان إضافة entities، مش شرط يطابق جدول واحد في كل mapping.
- (2) AddDbContext بيسجّل scoped context؛ ماتشاركوش بين threads ولا تعمل عمليتين متزامنين عليه. background jobs ممكن تستخدم IDbContextFactory وتعمل dispose لكل context.
- (3) unit of work بيحفظ التغييرات المتتبعة. في relational provider، SaveChanges واحدة افتراضياً لو فيه حاجة محتاجة atomicity.
| المكسب | التكلفة |
|---|---|
| LINQ و mapping و tracking و migrations | materialization و tracking overhead |
| parameterization للـ values | raw 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) key ثابت للهوية؛ unique business rules ليها unique constraints منفصلة.
- (2) طول الاسم و precision للسعر بدل defaults غير مقصودة. validation في التطبيق برضه مطلوبة.
- (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
- صفحة الطلب تحتاج رقم الطلب واسم العميل وعدد البنود؛ مش محتاجة تفاصيل كل بند. العلاقة Order.Lines اسمها : خاصية تمثل بيانات مرتبطة بالطلب.
- Eager loading يعني تطلب العلاقات المطلوبة مقدمًا؛ explicit loading يعني تطلب علاقة بعينها لاحقًا بوضوح؛ lazy loading قد يطلبها تلقائيًا عندما تقرأ الخاصية.
- يعني ترسم شكل الرد باستخدام 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) eager loading بـ Include؛ ThenInclude لمستوى تاني. يقدر يستخدم query واحدة أو split.
- (2) explicit loading: request إضافي مقصود لعلاقة؛ Collection للـ collections.
- (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) tracked entity بتبقى Unchanged وبعد اكتشاف التعديل Modified؛ فيه كمان Added و Deleted و Detached. SaveChanges بيعمل DetectChanges افتراضياً.
- (2) full entities من غير attachment للـ context؛ تعديلها مش هيتحفظ تلقائي.
- (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
- افترض عرض 10 طلبات مع عدد بنود كل طلب، مع lazy loading مفعّل. أول استعلام يجيب الطلبات، ثم قراءة Lines لكل طلب تطلق استعلامًا إضافيًا.
- الإجمالي 1 + 10 = 11 استعلامًا. N هو عدد الطلبات؛ من هنا جاء الاسم . مع 100 طلب قد يصبحوا 101 رحلة بدل 11.
- لو المطلوب العدد فقط، Select يحسب Count في قابل للترجمة ويعيد الأعداد. أما cartesian explosion فمشكلة أخرى: علاقات متعددة تكرر الصفوف داخل نتيجة كبيرة، حتى لو عدد الاستعلامات قليل.
endpoint ممكن يبقى بطيء من عدد الرحلات أو من حجم query واحدة. دول مش نفس المشكلة.
var orders = await db.Orders.ToListAsync(ct);
foreach (var order in orders)
Console.WriteLine(order.Lines.Count);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) 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) 10 يقبل regular ببلوك و if لبناء setters. تعبيرات properties نفسها لسه لازم تتترجم لـ SQL.
- (2) delete فوري، مش مستني SaveChanges. الاتنين بيرجعوا affected rows.
| قبل EF 10 | EF 10 |
|---|---|
| expression lambda بس للـ setter builder؛ الشرطية محتاجة تركيب expression | regular lambda تسهّل conditional setters |
#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) عنده query cache أصلاً. compiled بيتخطى lookup والمقارنة، مش بيحوّل query وحشة لـ SQL أحسن. المثال point lookup؛ queries اللي محتاجة cancellation أو model مختلف تتصمم حسب overload المناسب.
- (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 أو صلاحيات التطبيق أو كل أخطار .
- Expand: ضيف MobileNumber nullable وسيب Phone.
- انشر كود متوافق. طول ما فيه writers قديمة، القديم هو المصدر المعتمد أو استخدم آلية sync موثوقة.
- بعد خروج writers القديمة، اكتب في الاتنين واعمل backfill على batches مع معالجة concurrent writes.
- اتحقق من اكتمال النقل، حوّل القراءة للجديد، وراقب.
- 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) rowversion database-generated في Server؛ مش date أو time. provider تاني ممكن يحتاج application-managed بـ IsConcurrencyToken.
- (2) UPDATE أو DELETE فيها key و original version في WHERE؛ لو expected row مش اتأثر يرمي exception.
- (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) الـ كلها قابلة لإعادة التنفيذ؛ context جديد لكل محاولة يمنع tracked state من محاولة فاشلة.
- (2) transaction جوه strategy، مش العكس؛ disposal قبل commit ناجح يسيبها من غير commit ويعمل rollback حسب provider.
- (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) FromSql تستقبل interpolated FormattableString مباشرة وتحول minimum لـ parameter. entity query لازم ترجع الأعمدة المطلوبة للـ mapping؛ المثال Product البسيط.
- (2) SqlQuery لنتائج scalar أو unmapped types؛ Value alias مفيد لو هتركّب فوق scalar query. stored procedures في SQL Server مش composable زي SELECT.
- (3) named filters في 10؛ قبلها ادمج الشروط بـ && في filter واحدة.
- (4) بيعطّل soft delete بس وبيسيب Tenant. IgnoreQueryFilters من غير أسماء بيعطّل الكل.
#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) 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 Core | Dapper | |
|---|---|---|
| Query | وترجمة provider؛ raw متاح | SQL بإيدك و object mapping بسيط |
| Writes | tracking و unit of work و migrations | تدير SQL والـ والـ concurrency بنفسك |
| إمتى | business وتطوير model | query محددة ومعقدة وتحكم SQL مطلوب |
| EF6 | EF Core 10 | |
|---|---|---|
| المنصة | أقدم غالباً في تطبيقات legacy؛ بعض إصداراته تدعم modern | ORM معاد تصميمه للـ modern .NET |
| Model | Code First و designer workflows | code 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.
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.
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.
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.
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.
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 أهم من اسم الطريقة.
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 واحدة.
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 تلقائي.
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 مش تلقائي.
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 لسه لازم تتترجم.
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 مثال.
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 غالباً يكفي.
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.
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.
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.
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.
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.
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 خطر لو مش متضبط.
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 أداة لتحكم محدد مش علاج تلقائي.
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 محتاجة مراجعة.
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.
اختبر نفسك
إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.
Can two queries run concurrently on one DbContext?
Which projection can still track an entity by default?
What can AsSplitQuery reduce?
After ExecuteUpdateAsync, an already tracked entity is...
What does EF Core 10 add to ExecuteUpdate setter construction?
A configured version changed since the entity was loaded. Saving normally raises...
Where should an explicit transaction start when retries are enabled?
IgnoreQueryFilters(["SoftDelete"]) in EF Core 10...
Which raw SQL pattern parameterizes the input value?
What does DbContext pooling reuse?