LyraLearn AI Learning Platform
Exams
← Module 1 Β· Modern C# for MVC Developers
🎧 Listen

The C# You Actually Use

C# has grown a huge surface area, but the working MVC developer leans on a small, stable core every day: classes, properties, nullability discipline, and string handling. This lesson covers that daily-driver subset as it looks on the job today: ASP.NET Core on .NET 8+, with C# 12 as the floor β€” not the ceiling. Real enterprise portfolios are mixed: older services sit on C# 12 while newer ones ride the latest compiler (C# 13 brought params collections and a dedicated Lock type; C# 14 added the field keyword, extension members, and null-conditional assignment). The habit that serves you: write shared code to the floor, adopt the newest features freely where a project's target framework allows.

Types, classes, and records

Most of your types will be plain classes with auto-properties: public string Name { get; set; }. In MVC apps these show up as three flavors that look alike but have different jobs β€” entities (EF-mapped, mutable), ViewModels (shaped for one screen), and DTOs (shaped for one API call). Keeping them separate is the single most important habit in this course; AutoMapper exists to move data between them.

Records give you value-based equality and concise immutable types:

public record CustomerDto(int Id, string Name);

They're the default choice for DTOs and lookup results. Three more modern idioms worth making reflexes: file-scoped namespaces (namespace Orders.Web; β€” one line, no indent pyramid), primary constructors (C# 12 lets classes take constructor parameters in the declaration β€” public class OrdersController(IOrderService orders) β€” which erases most DI boilerplate), and collection expressions (int[] ids = [1, 2, 3];). You'll see all three constantly in current codebases and samples.

Nullability: let the compiler carry it

New .NET projects enable nullable reference types (<Nullable>enable</Nullable>) by default: string means "never null" and string? means "might be null," enforced by compiler warnings. Lean on it β€” annotate honestly instead of silencing warnings with !. The defensive operators still matter at the edges: ?. (null-conditional), ?? (null-coalescing), and guard clauses at method entry. The pragmatic rule: treat anything that crossed a boundary (model binding, database, session, config) as possibly null until you've checked it. Model binding in particular will happily hand your action a null property. Legacy note: if you ever touch an old .NET Framework codebase, assume nothing is annotated and everything can be null.

var, strings, and everyday idioms

Use var when the type is obvious from the right-hand side (var list = new List<Order>();) and spell the type out when it isn't β€” especially with LINQ queries whose element type matters. This is a readability decision, not a performance one. Pattern matching (if (result is Order { Status: "Open" } order)) is the modern replacement for cast-and-check chains.

For strings: string interpolation ($"Order {id} saved") beats string.Format and + concatenation for readability. Use StringBuilder only in real loops building large text. Compare with string.Equals(a, b, StringComparison.OrdinalIgnoreCase) rather than .ToLower() β€” it's faster and culture-safe, which matters when your app runs on a server with a different locale than your laptop. And prefer string.IsNullOrWhiteSpace() as the standard "did the user actually type something" check; it handles null, empty, and spaces in one call.

Everything else β€” LINQ, async, DI β€” gets its own lesson next.

🧠 Quiz yourself on this lesson →

Ask the AI Tutor

Grounded in the course lessons β€” it cites its sources and says when it doesn't know.