تطوير الويب

EF Core 10 و.NET 10: vector وLeftJoin والفلاتر المُسمّاة

5 دقائق للقراءة

دروس السلسلة: C# 14 و.NET 10 · Worker Services .NET 10 · gRPC ASP.NET Core 10 · BenchmarkDotNet .NET 10

Entity Framework Core 10 رافق إصدار .NET 10 LTS في نوفمبر 2025. الإصدار المستقر في ربيع 2026 هو 10.0.8 (مايو 2026). هذا الإصدار يجلب ثلاثة تغييرات هيكلية للإنتاج: دعم أصلي لـ vector type (Azure SQL DB وSQL Server 2025) لأعباء AI والبحث الدلالي، operators LINQ LeftJoin وRightJoin من الدرجة الأولى، complex types مُحَسَّنة مع تحديث جزئي، وnamed query filters التي تحل أخيرًا محل filter global واحد مُقيِّد.

المتطلبات

  • .NET 10 LTS مُثبَّت (SDK 10.0.x)
  • SQL Server 2022+ (أو PostgreSQL 16+، MySQL 8.4+، SQLite، Azure Cosmos DB)
  • مشروع ASP.NET Core أو console مُنشأ
  • أساسيات C#
  • الوقت المُقدَّر: 90 دقيقة

الخطوة 1 — تثبيت EF Core 10 وإعلان DbContext

dotnet add package Microsoft.EntityFrameworkCore --version 10.0.8
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 10.0.8
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.8
dotnet tool install --global dotnet-ef --version 10.0.8

dotnet ef --version

إعلان DbContext يبقى كلاسيكيًا. EF Core 10 يحافظ على توافق رجعي شبه كامل مع EF Core 8/9.

using Microsoft.EntityFrameworkCore;

public class CatalogueContext : DbContext
{
    public CatalogueContext(DbContextOptions<CatalogueContext> options) : base(options) { }

    public DbSet<Produit> Produits => Set<Produit>();
    public DbSet<Commande> Commandes => Set<Commande>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Produit>(e =>
        {
            e.HasKey(p => p.Id);
            e.Property(p => p.Nom).HasMaxLength(200).IsRequired();
            e.HasIndex(p => p.Reference).IsUnique();
        });
    }
}

الخطوة 2 — تسجيل DbContext في ASP.NET Core 10

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<CatalogueContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("Catalogue"),
        sql =>
        {
            sql.EnableRetryOnFailure(maxRetryCount: 3);
            sql.CommandTimeout(30);
        })
    .EnableSensitiveDataLogging(builder.Environment.IsDevelopment())
    .UseSnakeCaseNamingConvention()
);

var app = builder.Build();

ثلاث ممارسات هيكلية. EnableRetryOnFailure يُفعّل retry policy المُدمَجة للأخطاء العابرة. EnableSensitiveDataLogging مُفعَّل في dev فقط. UseSnakeCaseNamingConvention() (extension من package EFCore.NamingConventions 10.0.1 — ليس built-in في EF Core) يُترجم تلقائيًا NomProduit إلى nom_produit على جانب القاعدة.

الخطوة 3 — Migrations مع dotnet ef

# Créer une migration
dotnet ef migrations add InitialCreate

# Appliquer à la base de dev
dotnet ef database update

# Générer un script SQL idempotent pour la production
dotnet ef migrations script --idempotent --output migrations.sql

# Rollback de la dernière migration (avant déploiement uniquement)
dotnet ef migrations remove

# Lister les migrations
dotnet ef migrations list

النمط في الإنتاج: نُولّد script SQL idempotent في CI، نُفحصه (revue PR على SQL المُولَّد)، ونُطبّقه في الإنتاج عبر pipeline النشر. هذا النهج يفصل نشر الكود عن migration الـ schema. flag --idempotent يجعل السكربت آمنًا لإعادة التشغيل.

الخطوة 4 — استعلامات LINQ مع LeftJoin وRightJoin

EF Core 10 يُقدّم operators LINQ LeftJoin وRightJoin من الدرجة الأولى، مُترجَمة إلى SQL LEFT JOIN / RIGHT JOIN أصليًا. قبل، كنا نُحاكي بـ GroupJoin + SelectMany + DefaultIfEmpty، صياغة مُسهَبة.

// Tous les clients avec leur dernière commande (LEFT JOIN moderne)
var resultats = context.Clients
    .LeftJoin(
        context.Commandes,
        c => c.Id,
        cmd => cmd.ClientId,
        (c, cmd) => new
        {
            Client       = c.Nom,
            DateCommande = (DateTime?)cmd.Date,
            Montant      = (decimal?)cmd.Montant
        }
    )
    .OrderBy(r => r.Client)
    .ToListAsync();

// Avec projection sur record (immutable)
public record CommandeResume(string Client, DateTime? Date, decimal? Total);

الـ nullables الصريحة (DateTime?) و(decimal?) تُشير للمُجمِّع أن الجانب الأيمن من LEFT JOIN قد يكون غائبًا. EF Core 10 يُترجم ذلك إلى T-SQL idiomatique مع OUTER APPLY أو LEFT JOIN حسب الخطة الأمثل. للتحقق، استخدم .ToQueryString().

الخطوة 5 — Complex types وتحديث جزئي

[ComplexType]
public record Adresse(string Rue, string Ville, string CodePostal, string Pays);

public class Client
{
    public Guid Id { get; set; }
    public string Nom { get; set; } = "";
    public Adresse AdresseFacturation { get; set; } = new("","","","");
    public Adresse AdresseLivraison { get; set; } = new("","","","");
}

// Dans OnModelCreating
modelBuilder.Entity<Client>().ComplexProperty(c => c.AdresseFacturation);
modelBuilder.Entity<Client>().ComplexProperty(c => c.AdresseLivraison);

complex types مُخزَّنة في نفس جدول entity الأب (لا جدول منفصل كما مع owned entities)، مما يتجنّب joins مكلفة. EF Core 10 يُصحّح عدة قيود من الإصدارات السابقة: يمكن إعادة استخدامها في عدة entities، تدعم وراثة محدودة، تُدير قيم null افتراضيًا.

الخطوة 6 — Named query filters (فلاتر مُسمّاة)

قبل EF Core 10، كنا نستطيع تعريف filter global واحد فقط لكل entity. EF Core 10 يُقدّم filters مُسمّاة قابلة للتفعيل/التعطيل مستقلًا.

modelBuilder.Entity<Article>()
    .HasQueryFilter("SoftDelete", a => !a.EstSupprime)
    .HasQueryFilter("Tenant", a => a.TenantId == _tenantProvider.CurrentTenantId);

// Désactiver un filtre spécifique pour une requête admin
var tous = await context.Articles
    .IgnoreQueryFilters("SoftDelete")   // garde le filtre Tenant
    .ToListAsync();

// Désactiver tous les filtres (audit)
var audit = await context.Articles
    .IgnoreQueryFilters()
    .ToListAsync();

هذا التغيير يُحرّر أنماط multi-tenant أنظف بكثير: filter لكل بُعد عزل، مُفعَّل/مُعطَّل حسب سياق الاستدعاء.

الخطوة 7 — دعم vector للبحث الدلالي

public class Document
{
    public Guid Id { get; set; }
    public string Contenu { get; set; } = "";

    [Column(TypeName = "vector(1536)")]   // dimension OpenAI text-embedding
    public float[] Embedding { get; set; } = [];
}

// Recherche par similarité cosinus
public async Task<List<Document>> ChercherSimilaires(float[] requete, int top = 10)
{
    return await context.Documents
        .OrderBy(d => EF.Functions.VectorDistance("cosine", d.Embedding, requete))
        .Take(top)
        .ToListAsync();
}

للمشاريع التي تجمع بيانات الأعمال الكلاسيكية وembeddings AI، الاحتفاظ بكل شيء في نفس القاعدة يتجنّب sync بين Postgres + Pinecone/Qdrant. SQL Server 2025 وAzure SQL DB يدعمان فهارس HNSW على هذه أعمدة vectorielle.

أخطاء شائعة

العَرَض السبب الحل
InvalidOperationException عند البدء DbContext غير مُسجَّل في DI تحقّق من AddDbContext في Program.cs
Migration تفشل: « object already exists » migration غير مُزامَنة استخدم script --idempotent
استعلام بطيء، plan SQL مليء joins lazy loading غير مرغوب عطّل lazy loading، استخدم Include صريح
N+1 query problem iteration على collection غير مُحمَّلة .Include(x => x.Sous) أو projection صريحة
Memory تنمو في service طويل العمل DbContext singleton دائمًا scoped (لكل HTTP request)
UseSnakeCaseNamingConvention غير معروفة package EFCore.NamingConventions مفقود dotnet add package EFCore.NamingConventions

الأسئلة الشائعة

EF Core 10 أم Dapper؟
EF Core 10 للمشاريع CRUD النموذجية. Dapper لاستعلامات SQL معقدة. الاثنان يتعايشان غالبًا.

SQL Server، PostgreSQL أم SQLite؟
PostgreSQL في الإنتاج cloud (مجاني، أغنى وظيفيًا). SQL Server إذا منظومة Microsoft. SQLite في dev local والاختبارات.

كيف ندقّق الاستعلامات البطيئة؟
EnableSensitiveDataLogging في dev + LogTo + فلتر على المدة. في الإنتاج: Application Insights أو OpenTelemetry instrumentation EF Core.

Compiled queries أم Async؟
دائمًا async للـ APIs الويب. Compiled queries عبر EF.CompileAsyncQuery على الاستعلامات الأكثر تنفيذًا (مكاسب 20-30%).

الترحيل من EF6 إلى EF Core؟
ترحيل غير تافه (APIs مختلفة). Microsoft يُقدّم دليلًا رسميًا. احسب 2-5 أيام لكل مشروع حسب التعقيد.

مقالات ذات صلة

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité