Exercise: MVC with Dapper

Grow the BooksPlus website - Dapper and dependency injection, attribute routes, a paged grid of partial-view cards, a validated Add a Book form for administrators only, and recently viewed books in the session.

Journeyman · ASP.NET Core · C# · Updated

The BooksPlus website from Exercise: BooksPlus works, but it's a first draft: every controller builds its own DAO, the addresses come from class names, anyone can add a book, and nothing checks what they type. In this exercise you'll use everything from this chapter to make it a real site.

Before you start

The tests start your website and read your own BooksPlus database, so SQL Server must be running and BooksPlus created.

Get the exercise code

Open this lesson's GitHub repository, github.com/ask-codey/exercise-mvc-with-dapper, also linked from the Exercise box beside the lesson. Click Code, then Download ZIP, and unzip it into your bootcamp folder. Or clone it with Git. Then open BooksPlusWeb.slnx in Visual Studio.

It's the BooksPlus website as Exercise: BooksPlus left it, plus the Dapper package and a ready-made sign-in kit:

Ready to use What it is
Services/UserService.cs Two users: admin / Bookworm-Admin-2026 (role Admin) and clerk / Bookworm-Clerk-2026 (role Staff)
Controllers/AccountController.cs, Views/Account Sign in, sign out, and the access-denied page
Views/Shared/_LoginStatus.cshtml Hi, admin and Sign out, already in the layout
Views/Shared/_ValidationScriptsPartial.cshtml The client-side validation scripts

What to do

  1. Dapper. Rewrite BookDao with Dapper, and add four methods: Count, GetPage, GetByIds and TitleExists.
  2. Dependency injection. Register BookDao (scoped, built with the BooksPlus connection string) and UserService (singleton) in Program.cs, and let HomeController and BooksController ask for a BookDao.
  3. Sign-in and sessions. Turn on cookie authentication, with /Account/Login and /Account/Denied, and session state, in Program.cs.
  4. Addresses. Attribute routes for BooksController: /books, /books/{id} (ids only), /books/search?q=..., and /books/new for both the form and its POST.
  5. The list. Twelve books a page, by title, each a Bootstrap card from a _BookCard partial view, in a grid of 1, 2 or 3 columns, with a pager. A page past the last is Not Found.
  6. Validation. Rules on Book, with the exact messages in the README: title required and at most 255 characters, category required, a price from $0.01 to $500, and no title that already exists. Show the messages, and turn on client-side validation.
  7. Admins only. Only the Admin role may open or post Add a Book.
  8. Recently viewed. The last three books a visitor opened, most recent first, at the top of /books, kept in the session.

Run the website with Ctrl+F5, and the tests with Test > Run All Tests.

Hints

  • Steps 1 to 3 first. Until Program.cs registers the services, no page that needs a BookDao opens, and most tests fail with Unable to resolve service.
  • Old addresses stop working once BooksController has attribute routes. That's expected; the tag helpers in the views write the new ones.
  • Paging: OFFSET @Skip ROWS FETCH NEXT @PageSize ROWS ONLY, after an ORDER BY. 111 books make 10 pages.
  • An unchosen category: make CategoryId an int?, add a first option with value="", and [Required] catches it.
  • The session holds text: store the ids as JSON with JsonSerializer, as in Session State.
  • Try it as each user: sign in as clerk and open Add a Book to see the access-denied page, then as admin.

When you're done

Every test passes. Open the site on a phone-sized window (F12, then the device button) and check the cards stack and the menu folds.

Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

  1. On GitHub, click +, then New repository. Name it (for example exercise-mvc-with-dapper), choose Public, and click Create repository. Leave out the README: your folder already has one.

  2. In a terminal in your exercise folder (Git Bash on Windows), send your work to your new repo. If you downloaded the ZIP:

    git init -b main
    git add .
    git commit -m "My solution"
    git remote add origin https://github.com/your-name/exercise-mvc-with-dapper.git
    git push -u origin main
    

    If you cloned the starter instead, point your copy at your own repo, then commit and push:

    git remote set-url origin https://github.com/your-name/exercise-mvc-with-dapper.git
    git add .
    git commit -m "My solution"
    git push -u origin main
    
  3. Paste your repo's address, such as https://github.com/your-name/exercise-mvc-with-dapper, into Submit your exercise on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

So what

That's a complete MVC application: data through Dapper, services through dependency injection, readable addresses, validated input, sign-in with roles, session state and reusable partial views. It's the same structure as professional .NET web apps, and the base for the Web APIs in the next module.

Key terms

  • Dapper
  • Dependency Injection
  • Attribute Routing
  • [Authorize]

Related