Capstone — Building a Small Project
Sixteen chapters, two courses — this capstone builds one small, real library loan tracker touching almost all of it: records for the data model, a generic repository queried with LINQ, an interface with a default method, an extension method, an async availability check, and an event for overdue notifications.
Designing the Data Model
Book and Loan are csharp2-5 records — one line each generates the constructor, equality, and formatting the whole rest of this capstone relies on.
A Custom Exception
A plain Exception subclass — csharp1-7 established there's no checked/unchecked split to navigate here, unlike Java's own capstone.
A Generic Repository, Queried with LINQ
One Repository<T> class serves both Repository<Book> and Repository<Loan>. Its own Where method just forwards to LINQ's real .Where() — csharp2-7's own reveal, still an extension method underneath, even when wrapped in a method that merely delegates to it.
Interfaces & Default Methods
Extension Methods for Convenience
loan.IsOverdue() reads like a real instance method, but Loan is an immutable record — this extension method adds behavior without ever touching the type itself.
Async Availability Checks
Events for Overdue Notifications
Putting It Together — A Small Run
Chapter Attribution
| Capstone piece | Chapter |
|---|---|
| Book / Loan records | csharp2-5 |
| BookNotAvailableException (unchecked) | csharp1-7 |
| Repository<T> generics | csharp2-1 |
| Repository.Where() forwarding to LINQ | csharp2-2, csharp2-7 |
| ILoanNotifier interface & default method | csharp1-6 |
| IsOverdue() extension method | csharp2-7 |
| CheckAvailabilityAsync + property pattern | csharp2-4, csharp2-5 |
| event LoanOverdue + ?.Invoke() guard | csharp2-3, csharp1-3, csharp2-6 |
What's Still Out of Scope
Honestly: Repository<T>'s internal List<T> isn't thread-safe — csharp2-4's own async work never guaranteed thread-safety, only non-blocking suspension, and this capstone has no genuine concurrent access to worry about. No real persistence — everything is in-memory. No real network call — CheckAvailabilityAsync simulates one with Task.Delay. No automated tests. This capstone proves the pieces fit together, not that the result is production-ready.
Coding Challenges
Add a Return(Loan loan) method to Repository<Loan>-backed code that removes a loan using LINQ to find it first, and demonstrate it removing the correct loan from a repository holding several.
📄 View solutionWrite a LINQ query over a Repository<Loan> that returns only overdue loans, using the IsOverdue() extension method inside the Where() predicate, and print the results using a record's own ToString().
📄 View solutionWrite a short paragraph (as a comment) explaining what would need to change in this capstone's Repository<T> to make it safe for concurrent access from multiple threads, referencing java2-8's own equivalent challenge and explaining any real differences the C# tools bring.
📄 View solutionChapter 8 Quick Reference — C# Track Complete
- Records model the domain with free equality, formatting, and deconstruction (csharp2-5)
- A generic Repository<T> serves every entity type, backed by real reified generics (csharp2-1)
- LINQ queries the repository declaratively (csharp2-2), itself powered by extension methods (csharp2-7)
- Interfaces with default methods and a genuinely unchecked custom exception round out the OOP core (csharp1-6, csharp1-7)
- async/await handles simulated I/O without blocking (csharp2-4); events with a nullable-safe ?.Invoke() guard handle notifications (csharp2-3, csharp2-6)
- Concurrency safety, persistence, and testing are honestly named as still out of scope
- Both C# courses are now complete — 16 chapters total, framed against Java, Kotlin, and TypeScript throughout.