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.
- Create a project as MVC project with .Net 7.0 on Visual Studio.
- 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
- Make “Entities” folder
- 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; }
|
- 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
{
}
}
|
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)
{
}
}
}
|
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"
}
);
}
|
- 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));
|
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
|
- Add Controller
- Controllers → Mouse left menu → Add classes → ASP.NET Core → Controller Class
1
2
| Controllers
+ HeartRateController.cs
|
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.
}
|
- Add Items.cshtml on Views folder
1
2
3
| Views
+ HeartRate
+ Items.cshtml
|
1
2
| @using HeartRateTracker @using HeartRateTracker.Models @using
HeartRateTracker.Entities; @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
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>
|
- 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>
|
- 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>
|
- 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);
}
|
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>
|
- 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);
}
|
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);
}
}
|
- 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>
}
|
- Add calendar by JQuery
- Add client side library (wwwroot → lib → mouse left button → client side library)
- install jqueryui@1.13.2
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
|
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",
});
});
|
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;
}
|