Overview
A small e-commerce site built on ASP.NET Core MVC. Public visitors browse a seeded product catalog by category, add items to a session-scoped cart, and adjust quantities; an admin area sitting behind ASP.NET Identity lets a signed-in admin create, edit, and delete products against a SQL Server database via Entity Framework Core. Originally deployed to Azure App Service against an Azure SQL backend.
Why I built it
This was a learning project — the goal was to walk the entire ASP.NET Core MVC happy path end-to-end rather than read about it. Routing, model binding, EF Core migrations, Identity scaffolding, Razor view composition, Areas, partial views, session state, deployment to Azure: each one is straightforward in isolation, but the friction lives in how they fit together. Building a CRUD app where every layer is touched was the fastest way to make that fit muscle-memory.
How it's put together
Two route templates are registered in
Program.cs. The Areas route ({area:exists}/{controller=Products}/{action=Index}/{id?}) routes/Admin/Products/...to the admin product CRUD controller; the default route handles the public site. Areas keep the admin views, layout, and notifications partial isolated from the storefront.ProductsControllerqueriesDataContext.Productsthrough EF Core, eager-loading theCategorynavigation for the listing view. The DbContext is registered withUseSqlServeragainst a connection string read from configuration; on startup,SeedData.SeedDatabasepopulates categories and products if the tables are empty.CartControllerstores aList<CartItem>inHttpContext.Sessionvia a small JSON serialization extension. Add, decrease, and remove actions mutate the list and write it back; the small-cart partial in the layout reads the same key on every render. No cart row ever touches SQL — the cart is per-session and disappears when it expires.Areas/Admin/Controllers/ProductsControllercovers create, edit, and delete. Identity is wired up inProgram.cswithAddIdentity<AppUser, IdentityRole>().AddEntityFrameworkStoresso the user store, role store, and login flow all live in the same SQL Server database as the catalog.
Stack and shape
The deliberate choices are mostly framework defaults, which is the point
of the exercise. SQL Server because it's the path of least resistance with
EF Core on .NET; Azure App Service + Azure SQL because the deployment
story is a few clicks from Visual Studio. Bootstrap and jQuery on the
client because the MVC scaffolding ships with them and the project
wasn't an excuse to relitigate frontend stacks. Razor views compose
through _Layout.cshtml and a handful of partials — _NotificationPartial
renders TempData["Success"] flashes after every cart mutation, the
small-cart partial in the layout shows the current item count.
The awkward parts were the ones that always are. Session-stored carts
need a JSON helper because ISession only stores byte arrays and
strings. Identity password rules had to be relaxed in Program.cs so the
demo seed user could sign in with a short password. The seeded product
images live under wwwroot/media/products and the upload path on the
admin form had to resolve to that same folder so newly created products
picked up valid image URLs.
What I'd change
Picking this up four years on, the changes I'd make are about boundaries — the original was correct for the framework but loose about what should and shouldn't share a database.
- Move the cart out of session, or commit to it. Session storage is fine for a demo but loses the cart on idle timeout (20 minutes here). Either persist carts per-user once they sign in, or be explicit in the UI about the impermanence.
- Validate inputs on the server, not just via Bootstrap. The
current admin form leans on client-side validation; the controller
should re-check
ModelStateand return 400-shaped responses rather than relying on the view to gate bad data. - Add a real test layer. Even one xUnit test per controller that
exercises the happy path through
WebApplicationFactorywould catch the class of bug where a route or DI registration silently breaks. - Treat the seeded admin credentials as a secret. They were hard-coded into the seed for demo convenience — fine for a learning project, not fine for anything else.
Repo
Source on GitHub. MIT.