0% found this document useful (0 votes)
22 views13 pages

INF3003W Cheat Sheet

Uploaded by

khmaponya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views13 pages

INF3003W Cheat Sheet

Uploaded by

khmaponya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 13

Code and Documentation

Code-first approaches to managing the DbContext connection:

○ Constructor approach

public class SchoolContext : DbContext

public SchoolContext() :
base("Server=localhost;Database=SchoolDB;Trusted_Connection=True;")

public DbSet<Student> Students { get; set; }

public DbSet<Course> Courses { get; set; }

● Explanation: In this example, the connection string


"Server=localhost;Database=SchoolDB;Trusted_Connection=True;" is hard-
coded into the constructor of SchoolContext.
● The connection string is defined directly in the constructor of your
derived DbContext class.
● Every time you create an instance of this DbContext, it uses the
connection string you provided.
● Description: The connection string is directly specified in the constructor of your
derived DbContext class.
○ DbContext constructor without a parameter
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
}

● Description: Entity Framework will search for a connection string in the


configuration file (like appsettings.json in ASP.NET Core) that matches the name of
the derived DbContext class.
● In this case, EF will look for a connection string named SchoolContext in the
configuration file. No connection string is specified in the constructor.
● Configuration (appsettings.json):

"ConnectionStrings": {

"SchoolContext":
"Server=localhost;Database=SchoolDB;Trusted_Connection=True;"

● Startup.cs Configuration:

public void ConfigureServices(IServiceCollection services)

services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")))
;
}

○ Configuring the connection string externally


● Description: The connection string is stored in an external configuration
file, like appsettings.json, and injected into DbContext using
dependency injection.

public class SchoolContext : DbContext

private readonly string _connectionString;

public SchoolContext(string connectionString)

_connectionString = connectionString;

protected override void


OnConfiguring(DbContextOptionsBuilder optionsBuilder)

optionsBuilder.UseSqlServer(_connectionString);

public DbSet<Student> Students { get; set; }

public DbSet<Course> Courses { get; set; }

● Injecting the Connection String (in Startup.cs):

public void ConfigureServices(IServiceCollection services)

var connectionString =
Configuration.GetConnectionString("SchoolContext");

services.AddDbContext<SchoolContext>(options =>

options.UseSqlServer(connectionString));

● Explanation: The connection string is stored externally and injected into SchoolContext through
dependency injection, providing flexibility and making it easier to manage connection strings in
different environments (e.g., development, testing, production).
RazorPages:
● ASP.NET Identity Authentication:
○ Update the database:
■ update-database
○ Configure the identity services:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");


builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)


.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;

// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;

// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});

var app = builder.Build();

○ Scaffold Register, Login, LogOut, and RegisterConfirmation


○ Disable default account verification
■ Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs

public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)


{
if (email == null)
{
return RedirectToPage("/Index");
}

var user = await _userManager.FindByEmailAsync(email);


if (user == null)
{
return NotFound($"Unable to load user with email '{email}'.");
}

Email = email;
// Once you add a real email sender, you should remove this code that lets you confirm the
account
DisplayConfirmAccountLink = false;
if (DisplayConfirmAccountLink)
{
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
EmailConfirmationUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
}

return Page();
}
}
● Add the Products and OrderItem links to the _Layout.cshtml page

<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>

● Creation of the domain classes:



using System.ComponentModel.DataAnnotations;

public class Product


{
// Primary Key
[Key]
public int ID { get; set; }

// The name of the artwork


[Required]
[StringLength(100, ErrorMessage = "Name cannot be longer than 100 characters.")]
public string Name { get; set; }

// A detailed description of the artwork


[StringLength(1000, ErrorMessage = "Description cannot be longer than 1000
characters.")]
public string Description { get; set; }

// The price of the artwork


[Required]
[Range(0.01, 10000.00, ErrorMessage = "Price must be between 0.01 and 10,000.00.")]
public decimal Price { get; set; }

// The name of the artist who created the artwork


[Required]
[StringLength(100, ErrorMessage = "Artist name cannot be longer than 100 characters.")]
public string ArtistName { get; set; }

// A URL linking to an image of the artwork

// The number of units available in stock


[Required]
[Range(0, int.MaxValue, ErrorMessage = "Stock quantity cannot be negative.")]
public int StockQuantity { get; set; }
}

● Sorting functionality:
○ How to edit page to remove the productID
○ Pages/Products/index.cshtml
○ Comment out the @Html.DisplayFor or @Html.DisplayNameFor related to productID.

● Paging & Sorting functionality:


○ Pages/Products/Index.cs
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using MPNKAB004_NovExam.Data;
using MPNKAB004_NovExam.Model;

namespace MPNKAB004_NovExam.Pages.Products
{
public class IndexModel : PageModel
{

public int CurrentPage { get; set; }


// Default page size (10 items per page)

public int PageSize { get; set; } = 10;


// Total number of products
public int ProductCount { get; set; }

public IList<Product> Product { get; set; } = default!;

private readonly MPNKAB004_NovExam.Data.ApplicationDbContext _context;


public IndexModel(MPNKAB004_NovExam.Data.ApplicationDbContext context)
{
_context = context;
}

public async Task OnGetAsync(int? pageNumber, string sortOrder)


{
// Default to page 1 if no pageNumber is provided
CurrentPage = pageNumber ?? 1;
// Get the total number of products
ProductCount = await _context.Product.CountAsync();

// Fetch products for the current page


Product = await _context.Product
// Skip previous pages
.Skip((CurrentPage - 1) * PageSize)
// Take the next 10 items (PageSize)
.Take(PageSize)
.ToListAsync();

ViewData["NameSortParm"] = string.IsNullOrEmpty(sortOrder) ? "name_desc" : "";


ViewData["PriceSortParm"] = sortOrder == "Price" ? "price_desc" : "Price";

CurrentPage = pageNumber ?? 1;
IQueryable<Product> productsQuery = _context.Product;

// Apply sorting
switch (sortOrder)
{
case "name_desc":
productsQuery = productsQuery.OrderByDescending(p => p.productName);
break;
case "Price":
productsQuery = productsQuery.OrderBy(p => p.productPrice);
break;
case "price_desc":
productsQuery = productsQuery.OrderByDescending(p => p.productPrice);
break;
default:
productsQuery = productsQuery.OrderBy(p => p.productName);
break;
}

// Apply paging
Product = await productsQuery
.Skip((CurrentPage - 1) * PageSize)
.Take(PageSize)
.ToListAsync();

○ Pages/Products/Index.cshtml
○ Below the </tbody> and </table>

<nav aria-label="Page navigation">

<ul class="pagination">

<!-- Previous Page Link -->

<li class="page-item @(Model.CurrentPage == 1 ? "disabled" : "")">

<a class="page-link" asp-page="./Index" asp-route-pageNumber="@(Model.CurrentPage -


1)">Previous</a>

</li>

<!-- Page numbers (calculated based on ProductsCount and PageSize) -->


@for (int i = 1; i <= (Model.ProductCount / Model.PageSize) + 1; i++)

<li class="page-item @(Model.CurrentPage == i ? "active" : "")">

<a class="page-link" asp-page="./Index" asp-route-pageNumber="@i">@i</a>


</li>

<!-- Next Page Link -->

<li class="page-item @(Model.CurrentPage >= ((Model.ProductCount / Model.PageSize) + 1) ?


"disabled" : "")">

<a class="page-link" asp-page="./Index" asp-route-pageNumber="@(Model.CurrentPage +


1)">Next</a>

</li>

</ul>

</nav>

● Implementation of role-based authorization


○ The app needs to cater for two roles, namely Manager and Customer. The only
manager is Thabo, whereas all new registrations by default needs to be
Customers.
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

// Configure database and Identity services


var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})

// Add roles to the Identity system


.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

var app = builder.Build();

// Initialize roles and assign Manager


using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
await InitializeRolesAsync(services);
}

app.UseHttpsRedirection();

app.Run();

// Method to initialize roles and assign Manager


async Task InitializeRolesAsync(IServiceProvider serviceProvider)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();

// Create roles if they do not exist


if (!await roleManager.RoleExistsAsync("Manager"))
{
await roleManager.CreateAsync(new IdentityRole("Manager"));
}
if (!await roleManager.RoleExistsAsync("Customer"))
{
await roleManager.CreateAsync(new IdentityRole("Customer"));
}

// Check if the Manager user exists


var managerUser = await userManager.FindByEmailAsync("thabo@example.com");
if (managerUser == null)
{
// Create Thabo as the Manager
managerUser = new IdentityUser { UserName = "thabo@example.com", Email = "thabo@example.com" };
await userManager.CreateAsync(managerUser, "Thabo@123"); // Use a strong password
await userManager.AddToRoleAsync(managerUser, "Manager");
}
}
Model-View-Controller:
● Registering school context in the startup.cs:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ContosoUniversity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)


{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddDatabaseDeveloperPageExceptionFilter();

services.AddControllersWithViews();
}
}

● Implementing the database context:


○ Adding a connection string in the appsetting.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\
mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",

// SQL Logging of Entity Framework Core


"Microsoft.EntityFrameworkCore.Database.Command": "Information"

}
},
"AllowedHosts": "*"
}

○ Creating the database context:


using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}

public DbSet<Course> Courses { get; set; }


public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
}
//overridding default behaviour by specifying singular names in

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}

● Implementing CRUD functionality:


○ Different approaches to HttpPost Delete:
■ The read first approach
● The read-first approach first retrieves the entity from the
database using FindAsync or FirstOrDefaultAsync, checks if the
entity exists, and then deletes it.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return RedirectToAction(nameof(Index));
}

try
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
}
}

■ The create and attach approach


● The create-and-attach approach avoids the extra database
query by creating a new instance of the entity with only the
primary key and then marking its state as Deleted.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
try
{
Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
}}

○ Recommended HttpPost Edit code: Read and update



[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);

// Asynchronously retrieve the student with the given ID from the database.
// "FirstOrDefaultAsync" returns the first matching student or null if no match is found.

if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))

// "TryUpdateModelAsync" attempts to update the student entity with values from the form submission.
// Only specified properties (FirstMidName, LastName, EnrollmentDate) are updated to prevent overposting.

{
try
{
await _context.SaveChangesAsync();
// Save the changes to the database asynchronously.
return RedirectToAction(nameof(Index));
// Redirect the user back to the "Index" action if the update is successful.
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View(studentToUpdate);
}

● User Interface design:


○ Sorting:
■ Add sorting functionality to StudentController:

public async Task<IActionResult> Index(string sortOrder)

//// Set up the ViewData for sorting parameters


// "NameSortParm" is used to toggle between ascending and descending sorting for the LastName
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";

// "DateSortParm" toggles between ascending and descending sorting for EnrollmentDate


ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";

// Initial link query to fetch all students


var students = from s in _context.Students
select s;

switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default:
students = students.OrderBy(s => s.LastName);
break;
}
return View(await students.AsNoTracking().ToListAsync());
}

■ Add column heading hyperlinks to the Student view



@model IEnumerable<ContosoUniversity.Models.Student>

@{
ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
<a asp-action="Index" asp-route-
sortOrder="@ViewData["NameSortParm"]">@Html.DisplayNameFor(model =>
model.LastName)</a>
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
<a asp-action="Index" asp-route-
sortOrder="@ViewData["DateSortParm"]">@Html.DisplayNameFor(model =>
model.EnrollmentDate)</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>

○ Filtering:
■ Adding filtering functionality in the studentcontroller:

//added the string searchString parameter to the index method
public async Task<IActionResult> Index(string sortOrder, string searchString)
{
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
ViewData["CurrentFilter"] = searchString;

var students = from s in _context.Students


select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default:
students = students.OrderBy(s => s.LastName);
break;
}
return View(await students.AsNoTracking().ToListAsync());
}

■ Adding a search box to the student index view:


<p>
<a asp-action="Create">Create New</a>
</p>

<form asp-action="Index" method="get">


<div class="form-actions no-color">
<p>
<label>Find by name: <input type="text" name="SearchString"
value="@ViewData["CurrentFilter"]" /></label>
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>

<table class="table">

○ Paging:
■ Creating and adding a paginatedlist class to the project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }

public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)


{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);

this.AddRange(items);
}

public bool HasPreviousPage => PageIndex > 1;

public bool HasNextPage => PageIndex < TotalPages;

public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source,


int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageIndex - 1) *
pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
}
■ Adding paging to studentcontroller index:

//adds a page number parameter, a current sort order parameter, and a current filter parameter to the
index method
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";

if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}

ViewData["CurrentFilter"] = searchString;

var students = from s in _context.Students


select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default:
students = students.OrderBy(s => s.LastName);
break;
}
int pageSize = 3;
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(),
pageNumber ?? 1, pageSize));
}
■ Adding paging link to student index view:

//specifies that the view now gets a PaginatedList<T> object instead of a List<T>
object.
@model PaginatedList<ContosoUniversity.Models.Student>

@{
ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
<a asp-action="Create">Create New</a>
</p>

<form asp-action="Index" method="get">


<div class="form-actions no-color">
<p>
<label>Find by name: <input type="text" name="SearchString"
value="@ViewData["CurrentFilter"]" /></label>
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>

<table class="table">
<thead>
<tr>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]"
asp-route-currentFilter="@ViewData["CurrentFilter"]">Last Name</a>
</th>
<th>
First Name
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-
route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>

@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}

<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
Previous
</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex + 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @nextDisabled">
Next
</a>

1. Documentation
● Guidelines and Rules for each diagram:

● Class Diagram
● Activity Diagram
● Use case Diagram & Use case Narrative
● Sequence Diagram
● State Machine Diagram
● Package Diagram

Reflection Essay

1. Learning New Technologies

● What?
During the course of the project, I encountered several challenges while learning new technologies, such as
WordPress, PHP, FileZilla, and responsive design. Although WordPress was user-friendly and easy to grasp, PHP
presented a steep learning curve. Our entire team struggled with understanding and implementing PHP,
especially database interactions, as none of us had prior experience. This lack of familiarity led to frequent
coding errors, numerous late nights troubleshooting in the labs, and an overwhelming sense of frustration.
Responsive design, although not entirely new, also proved challenging as I faced difficulties ensuring the website
functioned smoothly across various screen sizes. I often had to revisit and tweak the code to maintain
consistency.
● So What?
Initially, I felt overwhelmed and under immense pressure to learn these new skills under tight deadlines. This
experience forced me to adopt a more structured approach to self-learning and shifted my perception of what it
takes to acquire technical expertise in a fast-paced, practical environment. I realized that persistence and
resilience are essential when faced with setbacks. I had to reassess and refine my strategies for self-teaching,
acknowledging that consistent effort and practice are critical for success.
● Now What?
I now understand that learning is a continuous process that involves embracing mistakes and using them as
opportunities for growth. Since this experience, I have adopted a more patient and methodical approach to skill
acquisition. I have learned to appreciate the importance of consistent practice and have become more willing to
embrace challenges, recognizing that genuine learning often comes from repeated trial and error. This
experience has also given me a clearer view of the professional world and the level of adaptability required to
succeed.

2. Team Dynamics

● What?
At the beginning of the year, our team of 5 or 6 students started off with promising dynamics. Everyone appeared
eager to collaborate and get to know each other. However, as time went on, the dynamics began to deteriorate.
Some members became passive-aggressive and dismissive, openly criticizing my suggestions. The group
atmosphere grew hostile, with frequent disagreements, particularly over my ideas. One member would even
interrupt and speak over me, contributing to the growing tension. Despite my attempts to reconcile by reaching
out to certain members and suggesting compromises, my efforts were often met with indifference. Meetings felt
awkward and unproductive, and our progress stalled. Ironically, we produced high-quality deliverables, such as a
report that earned a 90%, but the disjointed group environment left me feeling frustrated and discouraged.
● So What?
At the time, I felt a mix of frustration and helplessness. Despite trying to promote better communication and
collaboration, I couldn’t shake the feeling that I was the source of the conflict. I noticed that the other team
members had good relationships among themselves, which intensified my sense of isolation. To manage the
tension, I suggested moving meetings online to avoid the discomfort of in-person interactions. Reflecting now, I
realize I could have approached the situation differently. I was too passive in confronting issues early on and
hesitant to insist on clearer communication and mutual respect. I have also come to understand that some
members may have been dealing with personal or academic struggles that influenced their behavior.
● Now What?
Moving forward, I recognize the need to be more proactive in addressing conflicts. In future team settings, I
would address interpersonal issues directly and constructively, possibly through one-on-one conversations.
Avoiding the problems only exacerbated the tension. Additionally, I intend to learn conflict resolution strategies
to better handle challenging group dynamics. I also recognize the importance of empathy and understanding
that others may be facing challenges I am unaware of.

3. Presentations and Feedback

● What?
Last week, our team presented our website to our mentor, sponsor, and key stakeholders at a crucial milestone
on campus at Leslie Commerce. This presentation was an opportunity to showcase our work, receive feedback,
and ensure alignment with our stakeholders' expectations. Balancing the pressure of delivering a polished
presentation with the anticipation of constructive criticism made this experience both nerve-wracking and
valuable.
● So What?
Initially, I was a bundle of nerves, worried about whether we had met all the expectations. Presenting under
pressure felt daunting, but the constructive feedback we received from our lecturers was reassuring. This
feedback shifted my perspective, helping me realize the value of critique in professional growth. Reflecting on
this, I see that my initial fears were misplaced. The experience highlighted the importance of confidence,
preparation, and the willingness to learn from feedback. It was an essential step in my development as a software
developer and a valuable learning moment for the team.
● Now What?
This experience taught me the importance of clear communication with stakeholders and staying composed
under pressure. I will focus on being more proactive and confident in my role, making an effort to enjoy the
process rather than fixating solely on deliverables. Additionally, I aim to embrace feedback as a tool for growth,
using it to refine my skills and contribute more effectively. My advice to future students is to view these
challenging moments as opportunities for learning and self-improvement, as they often bring out your true
potential.

You might also like