Web Application with .NET Core and Entity Framework Code-First CRUD Operations
In this tutorial, we’ll walk through the process of creating a basic CRUD (Create, Read, Update, Delete) web application using .NET Core and Entity Framework with a Code-First approach. We’ll cover setting up the project, creating models, implementing the database context, and building views for listing, creating, editing, and deleting products.
You can also download the source code from: https://github.com/mcansener/CodeFirstCRUD
Prerequisites
Before getting started, ensure that you have the following installed:
- .NET Core SDK
- A code editor of your choice (e.g., Visual Studio Code, Visual Studio)
Step 1: Create a new .NET Core Web Application
Open a terminal and run the following command to create a new .NET Core web application:
dotnet new web -n CodeFirstCRUDChange into the project directory:
cd CodeFirstCRUDStep 2: Install Entity Framework
Install the Entity Framework tools using the following command:
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServerStep 3: Create Models
Create a Product model in the Models directory:
using System.ComponentModel.DataAnnotations;
namespace CodeFirstCRUD.Models
{
public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
}
}Step 4: Implement Database Context
Create a ProductContext class in the Data directory to define the database context:
using CodeFirstCRUD.Models;
using Microsoft.EntityFrameworkCore;
namespace CodeFirstCRUD.Context
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasColumnType("decimal(18, 2)");
base.OnModelCreating(modelBuilder);
}
}
}Step 5: Configure Connection String
In appsettings.json, configure the connection string for your database:
"ConnectionStrings": {
"DefaultConnection": "Server=(local);Database=CodeFirstCRUDDb;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;"
},Step 6: Register Database Context in Program.cs
In Program.cs, register the database context in the ConfigureServices method:
using CodeFirstCRUD.Context;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// Configure the database context and migrations
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
// Build the application
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// Migrate the database
using (var serviceScope = app.Services.CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<AppDbContext>();
context.Database.Migrate();
}
app.Run();Step 7: Create Database Migrations
Run the following command to create database migrations:
dotnet ef migrations add InitialCreateApply the migrations to create the database:
dotnet ef database updateStep 8: Create Controller
Create the related controller for actions :
using CodeFirstCRUD.Context;
using CodeFirstCRUD.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace CodeFirstCRUD.Controllers
{
public class ProductController : Controller
{
private readonly AppDbContext _context;
public ProductController(AppDbContext context)
{
_context = context;
}
public async Task<IActionResult> Index()
{
var products = await _context.Products.ToListAsync();
return View(products);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Product product)
{
ModelState.Remove("Id");
if (ModelState.IsValid)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(product);
}
public async Task<IActionResult> Edit(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
return View(product);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Product product)
{
if (id != product.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(product.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(product);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool ProductExists(int id)
{
return _context.Products.Any(e => e.Id == id);
}
}
}Step 9: Create Views
Create views for listing, creating, editing, and deleting products in the Views/Product directory.
Index.cshtml
@model List<Product>
<h1>Product List</h1>
<p>
<a asp-action="Create" class="btn btn-primary">Create New</a>
</p>
<table class="table table-striped">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.First().Name)
</th>
<th>
@Html.DisplayNameFor(model => model.First().Price)
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var product in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => product.Name)
</td>
<td>
@Html.DisplayFor(modelItem => product.Price)
</td>
<td>
<div class="btn-group" role="group">
<a asp-action="Edit" asp-route-id="@product.Id" class="btn btn-warning">Edit</a>
<form asp-action="Delete" asp-route-id="@product.Id" method="post" onsubmit="return confirm('Are you sure you want to delete this product?')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</td>
</tr>
}
</tbody>
</table>Create.cshtml
@model Product
<h1>Create Product</h1>
<div>
<form asp-action="Create" method="post">
@await Html.PartialAsync("_ProductForm", Model)
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>Edit.cshtml
@model Product
<h1>Edit Product</h1>
<div>
<form asp-action="Edit" method="post">
@await Html.PartialAsync("_ProductForm", Model)
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>_ProductForm.cshtml (Partial View)
@model Product
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<a asp-action="Index" class="btn btn-secondary">Back to List</a>Step 9: Layout
Our layout appears to be like this for proper styling :
_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - CodeFirstCRUD</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/CodeFirstCRUD.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8sh+Wy9HgQCF+6pB1Z+kmokEl4kgp5EqTqFZI" crossorigin="anonymous">
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">CodeFirstCRUD</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2024 - CodeFirstCRUD - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8sh+Wy9HgQCF+6pB1Z+kmokEl4kgp5EqTqFZI" crossorigin="anonymous"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>P.S
Please let me know if there is anything that seems “broken” in the article.
You can also download the source code from: https://github.com/mcansener/CodeFirstCRUD
Stackademic
Thank you for reading until the end. Before you go:
