Post

Step by step hand-on for ASP.net MVC app

This is a hands-on to practice an APS.net MVC app. It has functions to add, show, edit items for the Heart Rate data.

  1. Create a project as MVC project with .Net 7.0 on Visual Studio.
  2. install entityCore package (Dependencies → Mouse left button → Manage NuGet Packages)
    • Microsoft.EntityFrameworkCore - 7.0.16
    • Microsoft.EntityFrameworkCore.SqlServer - 7.0.16
    • Microsoft.EntityFrameworkCore.Tools - 7.0.16
  3. Make “Entities” folder
  4. add new class → HearRateMeasurment.cs
    • Add Properties (BPMValue, Date, Position, Id as primary key)
1
2
3
4
5
6
7
8
9
10
namespace HeartRateTracker.Entities
{
	public class HeartRateMeasurement
	{
        public int HeartRateMeasurementId { get; set; }
        public int? BPMValue { get; set; }
        public DateTime? MeasurementDate { get; set; }
        public string? Position { get; set; }
    }
}
  • Add validation annotation

It will show in <div class="text-danger" asp-validation-summary="All"></div>

1
2
3
4
5
6
        [Required(ErrorMessage = " BPM value is required")]
        [Range(30, 300, ErrorMessage = " BPM Value should be in range of 30 to 300")]
        public int? BPMValue { get; set; }

        [Required(ErrorMessage = "Messagement Date is required")]
        public DateTime? MeasurementDate { get; set; }
  1. Add DbConext
  • Add new class HeartRateDbContext.cs
  • inherit DbContext
1
2
3
4
5
6
7
using Microsoft.EntityFrameworkCore;
namespace HeartRateTracker.Entities
{
	public class HeartRateDbContext : DbContext
	{
	}
}
  • Add Constructor
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using Microsoft.EntityFrameworkCore;

namespace HeartRateTracker.Entities
{
	public class HeartRateDbContext : DbContext
	{
		public HeartRateDbContext(DbContextOptions<HeartRateDbContext> dbContextOptions) : base(dbContextOptions)
		{

		}
	}
}
  • Seed data
1
2
3
4
5
6
public DbSet<MyClass> MyClasses {get; set;}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
	modelBuilder.Entity<MyClass>().HasData(
		new MyClass() { ... },new MyClass() { ... },new MyClass() { ... }
	);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public DbSet<HeartRateMeasurement> HeartRateMeasurements { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
	modelBuilder.Entity<HeartRateMeasurement>().HasData(
		new HeartRateMeasurement()
		{
			HeartRateMeasurementId = 1,
			BPMValue = 148,
			MeasurementDate = DateTime.Now.AddDays(-5),
			Position = "Standing"
		},
		new HeartRateMeasurement()
		{
			HeartRateMeasurementId = 2,
			BPMValue = 148,
			MeasurementDate = DateTime.Now.AddDays(-1),
			Position = "Standing"
		},
		new HeartRateMeasurement()
		{
			HeartRateMeasurementId = 3,
			BPMValue = 148,
			MeasurementDate = DateTime.Now.AddDays(-4),
			Position = "Standing"
		}
	);
}
  1. Database Connection
  • ConnectionString on appsetting.json
1
2
3
4
5
6
7
8
9
// For Mac
"ConnectionStrings": {
    "HeartRateDb": "Server=tcp:127.0.0.1,1433;Database=DatabaseName;User Id=userId;Password=Password;Integrated security=False;MultipleActiveResultSets=true;TrustServerCertificate=true"
}

// Windows Authentication
"ConnectionStrings": {
        "BloodPressureConnectionString": "Server=localhost;Database=BPMeasurementsGLee6973;Trusted_Connection=True;TrustServerCertificate=True"
}
  • Add service for database server connection on program.cs
1
2
string connstr = builder.Configuration.GetConnectionString("HeartRateDb");
builder.Services.AddDbContext<HeartRateDbContext>(option => option.UseSqlServer(connstr));
  • database migration
1
2
3
4
5
6
7
PM> Add-Migration Initial
Migratrions
   + 20240217145919_Initial.cs
   + HeartRateDbContextModelSnapshot.cs

PM> Update-Database
Can find Database and table in Azure data stuio
  1. Add Controller
  • Controllers → Mouse left menu → Add classes → ASP.NET Core → Controller Class
1
2
Controllers
    + HeartRateController.cs
  • Add Constructor
1
2
3
4
5
6
7
8
9
10
11
using HeartRateTracker.Entities;
using Microsoft.AspNetCore.Mvc;

public class HeartRateController : Controller
    {
        private HeartRateDbContext _heartRateDbContext;

        public HeartRateController(HeartRateDbContext heartRateDbContext)
        {
            _heartRateDbContext = heartRateDbContext;
        }
  • Add GetAllMeasurement action
1
2
3
4
5
6
7
8
9
10
11
// http://localhost:5271/heart-rate-measurements
[HttpGet("/heart-rate-measurements")]
  public IActionResult GetAllMeasurement()
  {
      //_heartRateDbContext.HeartRateMeasurements.OrderBy(a => a.MeasurementDate).ToList(); // ascending order
      var measurements = _heartRateDbContext
												.HeartRateMeasurements
												.OrderByDescending(a => a.MeasurementDate)
												.ToList();
      return View("Items", measurements); // Items pages and object to pass to it.
  }
  1. Add Items.cshtml on Views folder
1
2
3
Views
   + HeartRate
			+ Items.cshtml
  • _ViewImports.cshtml
1
2
@using HeartRateTracker @using HeartRateTracker.Models @using
HeartRateTracker.Entities; @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
  • Items.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@model List<HeartRateMeasurement>
  @{ ViewData["Title"] = "All Heart rate measurements"; }

  <a asp-controller="HeartRate" asp-action="GetAddMeasurementRequest"
    >Add new heart rate measurement</a
  >
  <table class="table table-bordered table-striped">
    <thead>
      <tr>
        <th>BPM</th>
        <th>Date of Measurement</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      @foreach (HeartRateMeasurement msmt in Model) {
      <tr>
        <td>@msmt.BPMValue</td>
        <td>@msmt.MeasurementDate?.ToString("d")</td>
        <td>
          <a
            asp-controller="HeartRate"
            asp-action="GetMeasurementById"
            asp-route-id="@msmt.HeartRateMeasurementId"
            >Details</a
          >
          <span class="mx-1">|</span>
          <a
            asp-controller="HeartRate"
            asp-action="GetEditMeasurementRequestById"
            asp-route-id="@msmt.HeartRateMeasurementId"
            >Edit</a
          >
        </td>
      </tr>
      }
    </tbody>
  </table>
  1. Add a link to Items page
1
Views + Home + Index.cshtml
1
2
3
4
<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <a asp-controller="HeartRate" asp-action="GetAllMeasurement">HRM list</a>
</div>

image

  1. Add GetAddMeasurement actions (HeartRateController.cs)
  • GetAddMeasurementRequest() as HttpGet to call Add page
1
2
3
4
5
[HttpGet("/heart-rate-measurements/add-request")]
public IActionResult GetAddMeasurementRequest()
{
    return View("Add", new HeartRateMeasurement());
}
  • AddNewMeasurement as HttpPost to request update database.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[HttpPost("/heart-rate-measurements")]
public IActionResult AddNewMeasurement(HeartRateMeasurement measurement)
{
    if (ModelState.IsValid) // <-
    {
        _heartRateDbContext.HeartRateMeasurements.Add(measurement);  // <-
        _heartRateDbContext.SaveChanges();  // <-
        TempData["LastActionMessage"] = "Creation was successuful";  // <-

        return RedirectToAction("GetAllMeasurement");
        // == return View("Item", measurement);
    }
    else
    {
        return View("Add", measurement);
    }
}
  • Add Add.cshtml (Views → HeartRate → Add.cshtml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@model HeartRateMeasurement @{ ViewData["Title"] = "Add heart rate measement"; }

<form
  asp-controller="HeartRate"
  asp-action="AddNewMeasurement"
  method="post"
  enctype="application/x-www-form-urlencoded">
  <div class="text-danger" asp-validation-summary="All"></div>

  <div class="form-group">
    <label asp-for="BPMValue">BPM</label>
    <input type="text" asp-for="BPMValue" class="form-control" />
  </div>

  <div class="form-group">
    <label asp-for="MeasurementDate">Date of Measurement</label>
    <input type="datetime" asp-for="MeasurementDate" class="form-control"
    value="@(Model.MeasurementDate?.ToString("d"))" />
  </div>

  <div class="form-group">
    <label asp-for="Position">Position</label>
    <input type="text" asp-for="Position" class="form-control" />
  </div>

  <button type="submit" class="btn btn-primary">Add</button>
  <a
    asp-controller="HeartRate"
    asp-action="GetAllMeasurements"
    class="btn btn-primary"
    >Cancel</a>
</form>

image

  1. Add Action, GetMeasurementById for Item Detail
1
2
3
4
5
6
7
[HttpGet("/heart-rate-measurement/{id}")]
public IActionResult GetMeasurementById(int id)
{
    HeartRateMeasurement msmt = _heartRateDbContext.HeartRateMeasurements.Find(id);

    return View("Item", msmt);
}
  • item.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@model HeartRateMeasurement @{ ViewData["Title"] = "Detailed view"; }

<div>
  <a asp-controller="HeartRate" asp-action="GetAllMeasurements">
    All measurements</a>
  <span class="mx-1">|</span>
  <a asp-controller="HeartRate" asp-action="GetAddMeasurementRequest">
    Add a new measurement</a>
  <span class="mx-1">|</span>
  <a
    asp-controller="HeartRate"
    asp-action="GetEditMeasurementRequestById"
    asp-route-id="@Model.HeartRateMeasurementId"
    >Edit this measurement</a>
  <p class="mt-3">Value: @Model.BPMValue</p>
  <p>Date taken: @Model.MeasurementDate?.ToString("d")</p>
  @if (!string.IsNullOrEmpty(Model.Position)) {
  <p>Position: @Model.Position</p>
  }
</div>

image

  1. Add Edit Action
  • GetEditMeasurementRequestById
1
2
3
4
5
6
[HttpGet("/heart-rate-measurement/{id}/edit-request")]
public IActionResult GetEditMeasurementRequestById(int id)
{
    HeartRateMeasurement msmt = _heartRateDbContext.HeartRateMeasurements.Find(id);
    return View("Edit", msmt);
}
  • edit.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@model HeartRateMeasurement @{ ViewData["Title"] = "Edit heart rate measement";
}

<form
  asp-controller="HeartRate"
  asp-action="ProcessEditMeasurementRequest"
  asp-route-id="@Model.HeartRateMeasurementId"
  method="post"
  enctype="application/x-www-form-urlencoded">
  <div class="text-danger" asp-validation-summary="All"></div>

  <div class="form-group">
    <label asp-for="BPMValue">BPM</label>
    <input type="text" asp-for="BPMValue" class="form-control" />
  </div>

  <div class="form-group">
    <label asp-for="MeasurementDate">Date of Measurement</label>
    <input type="datetime" asp-for="MeasurementDate" class="form-control"
    value="@(Model.MeasurementDate?.ToString("d"))" />
  </div>

  <div class="form-group">
    <label asp-for="Position">Position</label>
    <input type="text" asp-for="Position" class="form-control" />
  </div>

  <input type="hidden" asp-for="HeartRateMeasurementId" />

  <button type="submit" class="btn btn-primary">Edit</button>
  <a
    asp-controller="HeartRate"
    asp-action="GetAllMeasurements"
    class="btn btn-primary">Cancel</a>
</form>
  • ProcessEditMeasurementRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[HttpPost("/heart-rate-measurement/{id}/edit-request")]
public IActionResult ProcessEditMeasurementRequest(int id,
			HeartRateMeasurement measurement)
{
    if (id != measurement.HeartRateMeasurementId)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        _heartRateDbContext.HeartRateMeasurements.Update(measurement);
        _heartRateDbContext.SaveChanges();
        TempData["LastActionMessage"] =
				$"The Heart Rate measurement \"{measurement.BPMValue}\"
				has been updated successfully";

        return RedirectToAction("GetAllMeasurement", "HeartRate");
        // == return View("Item", measurement);
    }
    else
    {
        return View("Edit", measurement);
    }
}

image

  1. add TempData to show a notice on _layout.cshtml
1
2
3
TempData["LastActionMessage"] =
				$"The Heart Rate measurement \"{measurement.BPMValue}\"
				has been updated successfully";
1
2
3
4
5
6
7
8
9
10
11
12
<h1 class="display-6">@ViewData["Title"]</h1>

@if (TempData.ContainsKey("LastActionMessage")) {
<div class="alert alert-success alert-dismissible fade show" role="alert">
  @TempData["LastActionMessage"]
  <button
    type="button"
    class="btn-close"
    data-bs-dismiss="alert"
    aria-label="Close"></button>
</div>
}

image

  1. Add calendar by JQuery
  • Add client side library (wwwroot → lib → mouse left button → client side library)
  • install jqueryui@1.13.2

image

  • add script
1
2
3
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jqueryui/jquery-ui.min.js"></script>
// <- must below the jquery.min.js because it need
  • add site.js
1
2
3
4
5
6
7
8
$(document).ready(function () {
  $("input[type=datetime]").datepicker({
    dateFormat: "m/d/yy",
    changeMonth: true,
    changeYear: true,
    yearRange: "-60:+0",
  });
});

image

  • calendar decoration

site.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/* Customize datepicker calendar */
.ui-datepicker {
    font-family: Arial, sans-serif;
    background-color: #ffffff;
}

.ui-datepicker-header {
    background-color: #337ab7;
    color: #ffffff;
}

.ui-datepicker-title {
    font-weight: bold;
}

.ui-datepicker-prev, .ui-datepicker-next {
    cursor: pointer;
    margin-left: 2px;
    margin-right: 2px;
}

.ui-datepicker-calendar {
    border: 1px solid #cccccc;
}

.ui-datepicker-calendar th {
    background-color: #f5f5f5;
}

.ui-datepicker-calendar td {
    padding: 5px;
}

.ui-datepicker-calendar td a {
    color: #333333;
}

.ui-datepicker-calendar td a:hover {
    background-color: #eeeeee;
}

.ui-datepicker-current-day {
    background-color: #337ab7;
    color: #ffffff;
}

.ui-datepicker-unselectable {
    color: #999999;
}

.ui-datepicker a, .ui-datepicker a:visited, .ui-datepicker a:hover, .ui-datepicker a:focus {
    color: #ffffff;
    text-decoration: none;
}

image

This post is licensed under CC BY 4.0 by the author.