# Exercise: BooksPlus

Turn a new ASP.NET Core MVC project into a small bookstore website for BooksPlus, with featured books, a list, details, search and a form that adds a book, and run tests that request every page.

Journeyman · ASP.NET Core · Razor · C# · Updated October 30, 2026

The BooksPlus bookstore wants its first website. The data access is ready, from a DAO much like the ones you wrote in *Exercise: Connecting an App*. The pages are yours to build: controllers, views and forms.

## Before you start

The tests start your website and read **your own BooksPlus database**, so SQL Server must be running, and BooksPlus created. If you haven't created it yet, run `BooksPlus.sql` from the course's sample databases in SSMS.

## Get the exercise code

Open this lesson's GitHub repository, [github.com/ask-codey/exercise-booksplus](https://github.com/ask-codey/exercise-booksplus), 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.

| Folder | What's in it |
|---|---|
| `BooksPlusWeb` | The project `dotnet new mvc` makes, with Bootstrap 5.3 loaded from a CDN. `HomeController` and its views are still the template's. |
| `BooksPlusWeb/Models` | `Book` and `Category`, ready to use |
| `BooksPlusWeb/Data/BookDao.cs` | Every query the site needs, ready to use |
| `BooksPlusWeb.Tests` | Tests that request your pages the way a browser would |

`BookDao` has these methods; the comment above each says what it returns:

| Method | Returns |
|---|---|
| `GetFeatured(int count)` | The newest books, newest first |
| `GetAll()` | Every book, by title |
| `GetById(int bookId)` | One book, or `null` |
| `SearchByTitle(string text)` | The books whose titles contain the text |
| `Add(Book book)` | Adds a book and returns its new `BookId` |
| `GetCategories()` | Every category, by name |

## What to do

1. **Home page.** Change `HomeController.Index` to pass the five newest books to its view. Under a **Featured Books** heading, show them in a Bootstrap **carousel**: each slide shows the title, the price and a link to the book's details page.
2. **Books.** Add a `BooksController`. `/Books` shows every book in a table, each title a link to its details page.
3. **Details.** `/Books/Details/{id}` shows one book: its title, publish date, price as money (`$36.58`) and summary. A book that doesn't exist is **Not Found**.
4. **Search.** `/Books/Search?title=SQL` shows a search box that sends a GET (an input named `title`, so the action's parameter is `string? title`), and under it the books whose titles contain the text.
5. **Add a book.** `/Books/Create` shows a form for a new book's title, category (a drop-down of every category), publish date, price and summary. Posting it adds the book and redirects to the new book's details page.
6. **Menu.** Add **Books**, **Search** and **Add a Book** links to the menu in `_Layout.cshtml`.

Run the website with **Ctrl+F5**, and try every page in the browser as you go. Then run the tests with **Test > Run All Tests** (or `dotnet test`): each page you finish turns its tests green.

## Hints

- **The connection string.** Take `IConfiguration` in each controller's constructor, and create the DAO with `new BookDao(configuration.GetConnectionString("BooksPlus")!)`.
- **One step at a time.** Write the action, run it, see the "view was not found" error, add the view, run it again.
- **The carousel.** Bootstrap's documentation has a carousel to copy. Give the first slide the `active` class: `class="carousel-item @(i == 0 ? "active" : "")"` in a `@for` loop does it.
- **Money and dates.** `@book.Price?.ToString("C")` and `@book.PublishDate.ToString("MMMM d, yyyy")`.
- **Links** with tag helpers: `asp-controller="Books" asp-action="Details" asp-route-id="@book.BookId"`.
- **The form.** `asp-for` on every field, a POST form tag helper, `[HttpGet]` and `[HttpPost]` on the two `Create` actions, and `[ValidateAntiForgeryToken]` on the POST. A date field: `<input asp-for="PublishDate" type="date">`.
- The test that adds a book deletes it again afterward, so your database stays as it was.

## When you're done

Every test is green, and every page works in the browser. Try the **Add a Book** form with an empty title: what happens? Add a `ModelState.IsValid` check, as in *Model Binding a Form*, so the form comes back instead.

## 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-booksplus`), 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:

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

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

   ```bash
   git remote set-url origin https://github.com/your-name/exercise-booksplus.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-booksplus`, 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 real database-backed website: a home page, a list, details, search and a form, each a controller action and a view. Every MVC app is more of the same, and in *ASP.NET Core MVC in Depth* you'll add routing, validation and sign-in to it.

## Key terms

- Controller
- Action Method
- Tag Helper
- Model Binding

## Related

- [Controllers and Actions](https://www.ask-codey.com/lessons/controllers-and-actions)
- [Razor Syntax and Tag Helpers](https://www.ask-codey.com/lessons/razor-views-razor-syntax-and-tag-helpers)
- [Model Binding a Form](https://www.ask-codey.com/lessons/mvc-forms-model-binding-a-form)

Source: https://www.ask-codey.com/lessons/exercise-booksplus
