Skip to main content

đŸŽ€ Questions techniques pour entretiens/missions

Q1 : Qu'est-ce qu'Entity Framework Core et pourquoi l'utiliser ?​

Réponse attendue :

  • EF Core : ORM (Object-Relational Mapper) qui mappe les objets C# vers des tables SQL
    • Avantages : DĂ©veloppement rapide, LINQ, migrations automatiques, change tracking
    • InconvĂ©nients : Courbe d'apprentissage, peut gĂ©nĂ©rer du SQL non optimal

RĂ©ponse courte : "EF Core transforme vos classes C# en tables SQL et gĂ©nĂšre les requĂȘtes automatiquement"


Q2 : Quelle est la diffĂ©rence entre DbContext et DbSet ?​

Réponse attendue :

public class ShopContext : DbContext  // Le contexte = la connexion Ă  la DB
{
public DbSet<Product> Products { get; set; } // DbSet = une table
public DbSet<Category> Categories { get; set; }
}
  • DbContext : ReprĂ©sente une session avec la base de donnĂ©es, gĂšre les connexions et transactions
  • DbSet : ReprĂ©sente une collection (table) d'entitĂ©s, permet de faire des requĂȘtes LINQ

Réponse courte : "DbContext = la connexion, DbSet = une table"


Q3 : Comment fonctionnent les migrations EF Core ?​

Réponse attendue :

# Créer une migration (snapshot des changements)
dotnet ef migrations add AddProductTable

# Appliquer les migrations (créer/modifier la DB)
dotnet ef database update

# Supprimer la derniĂšre migration
dotnet ef migrations remove

Explication : Les migrations créent des fichiers C# qui décrivent les changements de schéma. EF Core peut alors créer ou mettre à jour la base de données automatiquement.

Réponse courte : "Les migrations génÚrent et appliquent automatiquement les changements de schéma de base de données"


Q4 : Quelle est la diffĂ©rence entre Include et ThenInclude ?​

Réponse attendue :

// Include : charger la relation directe
var blogs = await context.Blogs
.Include(b => b.Posts) // Charger les posts du blog
.ToListAsync();

// ThenInclude : charger une relation imbriquée
var blogs = await context.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments) // Charger les commentaires des posts
.ToListAsync();

Réponse courte : "Include pour les relations directes, ThenInclude pour les relations imbriquées"


Q5 : Qu'est-ce que le lazy loading et pourquoi l'Ă©viter ?​

Réponse attendue :

Lazy Loading :

// Avec lazy loading activé
var blog = await context.Blogs.FirstAsync();
var posts = blog.Posts; // RequĂȘte SQL automatique en arriĂšre-plan

ProblĂšme : Peut gĂ©nĂ©rer des centaines de requĂȘtes SQL sans qu'on s'en rende compte (problĂšme N+1)

Solution : Eager Loading avec Include

var blog = await context.Blogs
.Include(b => b.Posts) // Tout charger en une seule requĂȘte
.FirstAsync();

Réponse courte : "Lazy loading charge les données à la demande, mais peut créer des problÚmes de performance (N+1)"


Q6 : Quelle est la diffĂ©rence entre Add, Attach, et Update ?​

Réponse attendue :

// Add : Nouvelle entité à insérer
var product = new Product { Name = "New Product", Price = 99 };
context.Products.Add(product);
await context.SaveChangesAsync(); // INSERT

// Attach : Entité existante non trackée
var product = new Product { Id = 5, Name = "Updated", Price = 150 };
context.Products.Attach(product); // EF Core commence Ă  tracker
context.Entry(product).State = EntityState.Modified;
await context.SaveChangesAsync(); // UPDATE

// Update : Marque toute l'entité comme modifiée
var product = new Product { Id = 5, Name = "Updated", Price = 150 };
context.Products.Update(product);
await context.SaveChangesAsync(); // UPDATE tous les champs

Réponse courte : "Add = INSERT, Attach = tracker une entité, Update = UPDATE tous les champs"


Q7 : Comment Ă©viter le problĂšme N+1 avec EF Core ?​

Réponse attendue :

❌ Problùme N+1 :

var categories = await context.Categories.ToListAsync(); // 1 requĂȘte
foreach (var category in categories)
{
var products = category.Products.ToList(); // N requĂȘtes !
}

✅ Solution 1 : Include

var categories = await context.Categories
.Include(c => c.Products) // 1 seule requĂȘte avec JOIN
.ToListAsync();

✅ Solution 2 : Select

var data = await context.Categories
.Select(c => new
{
Category = c,
Products = c.Products
})
.ToListAsync();

RĂ©ponse courte : "Utiliser Include ou Select pour charger toutes les donnĂ©es en une seule requĂȘte"


Q8 : Qu'est-ce qu'AsNoTracking et quand l'utiliser ?​

Réponse attendue :

// Sans AsNoTracking (par défaut)
var products = await context.Products.ToListAsync();
// EF Core track les changements → plus lent

// Avec AsNoTracking
var products = await context.Products
.AsNoTracking() // Plus rapide !
.ToListAsync();
// EF Core ne track pas → idĂ©al pour la lecture seule

Quand utiliser :

  • ✅ Affichage de donnĂ©es (read-only)
  • ✅ APIs GET qui retournent des donnĂ©es
  • ✅ Rapports et exports
  • ❌ Quand vous devez modifier les donnĂ©es aprĂšs

Réponse courte : "AsNoTracking désactive le change tracking pour améliorer les performances en lecture seule"


Q9 : Comment gĂ©rer les transactions avec EF Core ?​

Réponse attendue :

using var transaction = await context.Database.BeginTransactionAsync();

try
{
// Opération 1
var order = new Order { CustomerId = 1, Total = 150 };
context.Orders.Add(order);
await context.SaveChangesAsync();

// Opération 2
var payment = new Payment { OrderId = order.Id, Amount = 150 };
context.Payments.Add(payment);
await context.SaveChangesAsync();

// Tout a réussi
await transaction.CommitAsync();
}
catch
{
// Erreur : annuler tout
await transaction.RollbackAsync();
throw;
}

Réponse courte : "BeginTransaction, opérations + SaveChanges, puis Commit ou Rollback"


Q10 : Quelle est la diffĂ©rence entre Find et FirstOrDefault ?​

Réponse attendue :

// Find : cherche par clé primaire, check d'abord le cache
var product = await context.Products.FindAsync(5);
// Avantage : Si l'entitĂ© est dĂ©jĂ  trackĂ©e, pas de requĂȘte SQL

// FirstOrDefault : toujours fait une requĂȘte SQL
var product = await context.Products
.FirstOrDefaultAsync(p => p.Id == 5);
// Avantage : Permet des conditions complexes

Quand utiliser quoi :

  • Find : Recherche simple par ID, peut Ă©viter une requĂȘte SQL
  • FirstOrDefault : Recherche avec conditions complexes ou Include

⚠ Attention avec les soft deletes : FindAsync n'applique PAS les QueryFilters ! Si vous utilisez des soft deletes, prĂ©fĂ©rez FirstOrDefaultAsync.

// Avec soft delete + QueryFilter
var product = await context.Products.FindAsync(5);
// ⚠ Peut retourner un produit supprimĂ© (IsDeleted = true) depuis le cache !

var product = await context.Products.FirstOrDefaultAsync(p => p.Id == 5);
// ✅ Respecte le QueryFilter, ne retourne pas les supprimĂ©s

RĂ©ponse courte : "Find cherche par ID et utilise le cache, FirstOrDefault fait toujours une requĂȘte SQL"


Q11 : Comment configurer une relation many-to-many en EF Core ?​

Réponse attendue :

Approche moderne (EF Core 5+) :

public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public List<Course> Courses { get; set; }
}

public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public List<Student> Students { get; set; }
}

// EF Core crée automatiquement la table de jointure !

Approche explicite (avec table de jointure) :

public class StudentCourse
{
public int StudentId { get; set; }
public Student Student { get; set; }

public int CourseId { get; set; }
public Course Course { get; set; }

public DateTime EnrolledDate { get; set; } // Données supplémentaires
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentCourse>()
.HasKey(sc => new { sc.StudentId, sc.CourseId });
}

Réponse courte : "EF Core 5+ gÚre automatiquement les many-to-many, sinon créer une entité de jointure"


Q12 : Comment optimiser une requĂȘte EF Core lente ?​

Réponse pratique :

1. Vérifier le SQL généré

// Activer le logging
optionsBuilder
.UseSqlServer(connectionString)
.LogTo(Console.WriteLine, LogLevel.Information);

// Voir le SQL dans les logs
var products = await context.Products.Where(p => p.Price > 100).ToListAsync();

2. Optimisations courantes

// ❌ Mauvais : rĂ©cupĂ©rer toutes les colonnes
var products = await context.Products.ToListAsync();

// ✅ Bon : projection (Select) pour rĂ©cupĂ©rer uniquement ce qui est nĂ©cessaire
var products = await context.Products
.Select(p => new { p.Id, p.Name, p.Price })
.ToListAsync();

// ✅ Bon : AsNoTracking si lecture seule
var products = await context.Products
.AsNoTracking()
.ToListAsync();

// ✅ Bon : Pagination
var products = await context.Products
.Skip(20)
.Take(10)
.ToListAsync();

3. Éviter les problĂšmes frĂ©quents

  • Pas d'Include inutiles
  • Pas de requĂȘtes en boucle
  • Utiliser des index sur les colonnes filtrĂ©es

Réponse courte : "Logger le SQL, utiliser Select/AsNoTracking/Pagination, éviter les Include inutiles"


Q13 : Quelle est la diffĂ©rence entre SaveChanges et SaveChangesAsync ?​

Réponse attendue :

// SaveChanges : synchrone (bloque le thread)
context.Products.Add(product);
context.SaveChanges(); // Bloque jusqu'à ce que la DB réponde

// SaveChangesAsync : asynchrone (libĂšre le thread)
context.Products.Add(product);
await context.SaveChangesAsync(); // Le thread peut faire autre chose

Quand utiliser quoi :

  • SaveChangesAsync : Toujours dans les applications web/API (meilleure scalabilitĂ©)
  • SaveChanges : Applications console simples ou scripts

Réponse courte : "SaveChangesAsync est asynchrone et préférable pour les apps web (meilleure performance)"


Q14 : Comment gĂ©rer les soft deletes avec EF Core ?​

Réponse attendue :

Approche 1 : Propriété IsDeleted

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; } // Soft delete
}

// Global query filter
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.HasQueryFilter(p => !p.IsDeleted); // Filtrer automatiquement
}

// Utilisation
product.IsDeleted = true;
await context.SaveChangesAsync(); // UPDATE, pas DELETE

// Ignorer le filtre si besoin
var allProducts = await context.Products
.IgnoreQueryFilters() // Inclure les supprimés
.ToListAsync();

Réponse courte : "Ajouter une propriété IsDeleted + QueryFilter global pour filtrer automatiquement"


Q15 : Comment tester du code utilisant EF Core ?​

Réponse attendue :

Approche 1 : InMemory Database

var options = new DbContextOptionsBuilder<ShopContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;

await using var context = new ShopContext(options);

// Ajouter des données de test
context.Products.Add(new Product { Name = "Test", Price = 100 });
await context.SaveChangesAsync();

// Tester
var service = new ProductService(context);
var result = await service.GetExpensiveProductsAsync(50);

Assert.AreEqual(1, result.Count);

Approche 2 : SQLite en mémoire (plus réaliste)

var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();

var options = new DbContextOptionsBuilder<ShopContext>()
.UseSqlite(connection)
.Options;

await using var context = new ShopContext(options);
await context.Database.EnsureCreatedAsync();

Réponse courte : "InMemory pour les tests rapides, SQLite en mémoire pour plus de réalisme"


📋 Checklist de prĂ©paration​

Avant un entretien/mission sur EF Core, assurez-vous de pouvoir :

  • Expliquer la diffĂ©rence DbContext vs DbSet
  • CrĂ©er et appliquer des migrations
  • Utiliser Include et ThenInclude correctement
  • Expliquer et Ă©viter le problĂšme N+1
  • Utiliser AsNoTracking quand c'est appropriĂ©
  • GĂ©rer les transactions
  • Configurer des relations (1-n, n-n)
  • Optimiser une requĂȘte lente
  • Tester du code avec EF Core
  • Expliquer Find vs FirstOrDefault