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

Exceptions, Logging, and DI

These three topics decide how maintainable an MVC codebase is five years in. They're also where line-of-business apps accumulate the worst habits β€” swallowed exceptions, string.Format logging, and controllers that new up their own dependencies.

An exception strategy you can defend

The rule: catch an exception only where you can do something about it. For most code that means don't catch β€” let it bubble up to the app's exception-handling middleware (UseExceptionHandler("/Error") in Program.cs), which logs it and shows a friendly error page. Legitimate local catches are the boundary cases: retrying a transient SQL error, translating a third-party exception into a domain one (throw new OrderNotFoundException(id, ex) β€” keep the inner exception), or a top-level catch in a background job.

The anti-patterns to hunt in review: catch (Exception) { } (swallowing β€” the bug still happens, now invisibly), catch { return null; } (moves the crash somewhere confusing), and throw ex; instead of throw; (rethrowing with ex resets the stack trace and destroys the evidence). Use exceptions for exceptional states, not flow control β€” "user not found" on a lookup screen is a normal result, not a throw.

Logging that answers questions

The test of a log entry: can you diagnose a production incident from it without reproducing the bug? That requires context, and context is why structured logging won. Instead of baking values into a string, you log named properties:

_logger.LogWarning("Payment declined for {OrderId} amount {Amount}", orderId, amount);

The values are stored as fields, so you can query "all events where OrderId = 4412" across services. ILogger<T> is built in β€” inject it anywhere; teams typically plug Serilog in as the sink for files, Seq, or Application Insights. Practical levels: Error = a request failed, Warning = something's off but handled, Information = business milestones, Debug = noise you enable temporarily (per-namespace, in appsettings.json). Never log passwords, connection strings, or personal data β€” in a public-sector agency, logs are discoverable records.

Dependency injection: the shape of the whole app

Constructor injection is the pattern underneath everything else in this course: a controller declares what it needs (public OrdersController(IOrderService orders) β€” a natural fit for C# 12 primary constructors) and the container supplies it. Benefits: dependencies are visible in the signature, and tests can pass fakes.

The container is built in β€” you register services in Program.cs (builder.Services.AddScoped<IOrderService, OrderService>()). The vocabulary that matters is lifetimes: transient (new instance every resolve), scoped (one per HTTP request β€” the right home for an EF DbContext), and singleton (one for the app; must be thread-safe and must never capture a scoped service). The classic lifetime bug is a singleton holding a DbContext: it works on your machine and corrupts state under concurrent load. When in doubt, register scoped.

🧠 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.