###### tags: `Back-end`,`Restful API`,`ASP.NET CORE`,`AON` # Week 02 - OOP & ASP.NET Core Welcome to today's session, where we will dive into the core principles of Object-Oriented Programming (OOP) in C# In this session, we will explore how OOP introduces powerful design concepts such as class, abstraction, encapsulation, inheritance, and polymorphism. By learning these, you will be better equipped to design structured, efficient, and modular applications in C#. "On Day 02, we will dive into ASP.NET Core Web API, starting with creating a new project. We'll explore the project structure and cover key fundamentals, including endpoints, controllers, actions, and the HTTP protocol." ## Session Objectives - The Fundamentals of Object-Oriented Programming (OOP) - Comprehend the concepts of classes and objects and their roles in representing real-world entities and modeling software systems. - Explore the concept of constructors and their importance in initializing object instances with default or custom values. - Explain the concepts of Interface, Inheritance, and Polymorphism, and how they enhance flexibility and reuse in software design. ## What is OOP? Object-Oriented Programming (OOP) is a way of thinking about software that models real-world entities as objects. An object is an instance of a class, which can have properties (data) and behaviors (functions or methods). ## Class A **class** in OOP serves as a blueprint to create objects. You can think of a class as a **custom data type** that defines the structure of an object. Let's dive into OOP concepts gradually, using a real-world example and then building on top of it. A class defines a **template** for objects. It contains: - **Properties**: Data related to the object. - **Methods**: Functions that define behaviors or actions for the object. **Example: Trainer Class** Imagine a **User** in a freelancing website. The User has properties like `Name` and `Name`, and a method `PrintInfo` that prints out the details. ```csharp public class User { public int Id { get; set; } public string Name { get; set; } // Method to print information about the User public void PrintInfo() { Console.WriteLine($"User Name: {Name}"); } } ``` Class usage: ```csharp public class Program { public static void Main() { // Creating an instance of User class (Object) User user = new User(); user.Name = "Ali Ahmed"; user.PrintInfo(); // Output: User Name: Ali Ahmed } } ``` ### Constructor A constructor is a special method in a class that is called when an object is instantiated. It allows you to initialize properties when creating an object. ```csharp public class User { public string Name { get; set; } public int Id { get; set; } // Constructor to initialize properties public User(string name, int Id) { Id = Id; Name = name; } public void PrintInfo() { Console.WriteLine($"User Name: {Name}, Id: {Id}"); } } class Program { static void Main() { // Creating an instance using the constructor User user = new User("Fadi Ramzi", 1); user.PrintInfo(); // Output: User Name: Fadi Ramzi, Id: 30 } } ``` ## Inheritance and Abstraction Now, let’s introduce another class called **Freelancer**, which also has the same properties (`Name` and `Id`) and a similar `PrintInfo` method, and the classification of this entity is relate to a human user!, But instead of duplicating the same code, we can use **inheritance**. We create a base class called **User**, which has the common properties and method, and `Freelancer` will inherit from it. ```csharp public class User { public string Name { get; set; } public int Id { get; set; } // Virtual method to be overridden by child classes public virtual void PrintInfo() { Console.WriteLine($"Name: {Name}, Id: {Id}"); } } ``` Inherit from base class ```csharp public class Freelancer : User { public string Skills {get; set;} public override void PrintInfo() { Console.WriteLine($"Freelancer Name: {Name}, Id: {Id}"); } } ``` - **User** is the base class that `Freelancer` inherit from. - `PrintInfo` is **overridden** in each subclass to give customized behavior for printing. Usage in main class: ```csharp class Program { static void Main() { User user = new User { Name = "Saif Ahmed", Id = 35 }; user.PrintInfo(); // Output: Trainer Name: Saif Ahmed, Id: 35 Freelancer freelancer = new Freelancer { Name = "Marwan", Id = 22 }; freelancer.PrintInfo(); // Output: Freelancer Name: Marwan, Age: 22 } } ``` what if we need to implement other user types that have the same shared properties and methods? - Client: who is looking for a freelancer in the system - Admin: who is owning the system and have full priviliges ```csharp public class Client:User { public string ContactNumber { get; set; } } ``` ```csharp public class Admin:User { public string Privileges { get; set; } public List<string> ManagedSections { get; set; } } ``` ## Abstract Classes and Methods Sometimes you want to ensure that a base class cannot be instantiated on its own. Instead, it only serves as a template for derived classes. In this case, you can make the base class **abstract**. - An **abstract class** cannot be instantiated. - It can contain **abstract methods**, which are methods without a body that must be implemented by derived classes. ```csharp public abstract class User { public string Name { get; set; } public int Id { get; set; } // Virtual method to be overridden by child classes public abstract void PrintInfo(); } ``` Override User with implement PrintInfo ```csharp public class Freelancer : User { public string Skills {get; set;} public override void PrintInfo() { Console.WriteLine($"Freelancer Name: {Name}, Id: {Id}"); } } ``` - The `User` class is now **abstract** and cannot be instantiated. - `PrintInfo` is an abstract method, so each subclass (`Freelancer`,`Admin`,and `Client`) must provide its own implementation. ## Interfaces and Polymorphism An interface defines a contract that a class must follow. Unlike classes, interfaces cannot contain code. They only specify the methods or properties a class must implement. This IUserOperations interface defines methods for basic user actions: Login(), Logout(), and DisplayProfile(). ```csharp public interface IUserOperations { void Login(); void Logout(); void DisplayProfile(); } ``` The abstract class User will implement some shared properties and methods. The derived classes will be responsible for implementing their own specific logic for the methods defined by the interface. ```csharp public abstract class User : IUserOperations { public int Id { get; set; } public string Name { get; set; } // Abstract methods that derived classes will implement public abstract void Login(); public abstract void Logout(); public abstract void DisplayProfile(); } ``` in the above implementation, I decided to allow the derived classes (Admin, Client, Freelancer) to implement their own login, logout, and displayProfile. so in the derived class, the implementation should be: ```csharp public class Freelancer : User { public override void Login() { // Freelancer-specific login logic (if different) Console.WriteLine("Freelancer logged in."); } public override void Logout() { // Freelancer-specific logout logic (if different) Console.WriteLine("Freelancer logged out."); } public override void DisplayProfile() { Console.WriteLine("Freelancer's profile is displayed."); } } ``` If the implementation of login, logout for all type of user have the same logic and functionality, then we can implement them in the abstract class and let derived class to implement only `DisplayProfile` ```csharp public abstract class User : IUserOperations { public int Id { get; set; } public string Name { get; set; } // Centralized login logic for all user types public virtual void Login() { // Issue JWT token, generate refresh token, etc. Console.WriteLine($"{UserName} logged in and received a JWT token."); } // Centralized logout logic public virtual void Logout() { // Invalidate JWT token, handle logout events, etc. Console.WriteLine($"{UserName} logged out and JWT token invalidated."); } // Abstract method for each user type to display their profile public abstract void DisplayProfile(); } ``` ## Polymorphism Using the Interface and Abstract Class Now, let's see polymorphism in action. Polymorphism allows different types of objects (Freelancers, Clients, Admins) to be treated as instances of the base type (User) or interface (IUserOperations), while still calling their specific implementations of the methods. ```csharp public class Program { public static void Main(string[] args) { // Create a list of users with different types List<IUserOperations> users = new List<IUserOperations> { new Freelancer { UserName = "JohnDoe", Skills = "Web Development", PortfolioUrl = "www.johndoe.com" }, new Client { UserName = "JaneSmith", CompanyName = "Acme Corp", ContactNumber = "123-456-7890" }, new Admin { UserName = "SuperAdmin", Privileges = "Full Access", ManagedSections = new List<string> { "Users", "Projects" } } }; // Loop through the users and perform actions (polymorphism) foreach (var user in users) { user.Login(); // Calls the respective Login() method of Freelancer, Client, or Admin user.DisplayProfile(); // Calls the respective DisplayProfile() method user.Logout(); // Calls the respective Logout() method Console.WriteLine(); } } } ``` Output: ``` JohnDoe (Freelancer) has logged in. Freelancer Profile: Name: JohnDoe Skills: Web Development JohnDoe (Freelancer) has logged out. JaneSmith (Client) has logged in. Client Profile: Name: JaneSmith Company: Acme Corp JaneSmith (Client) has logged out. SuperAdmin (Admin) has logged in. Admin Profile: Name: SuperAdmin Privileges: Full Access SuperAdmin (Admin) has logged out. ``` ## Getting Started With ASP.NET Core ### Objectives * Understand the fundamentals of ASP.NET Core framework as applied to RESTful API development. * Learn how to create a new ASP.NET Core API project tailored for RESTful services. * Explore the project structure optimized for RESTful API applications. * Gain hands-on experience in building, running, and testing ASP.NET Core API projects for RESTful services. ### Introduction to ASP.Net Core Framework ASP.NET Core is an open-source, cross-platform framework for building modern, cloud-based, and internet-connected applications, including web applications, APIs, microservices, and server-side applications. It is a reimagined version of the older ASP.NET framework, with improvements in performance, flexibility, and ease of deployment. The key difference is that ASP.NET Core is designed to run on both Windows and other platforms, such as Linux and macOS, using .NET Core runtime, offering developers the ability to deploy and run applications across a broader range of environments. ### Key Features * Cross-platform: Unlike the classic ASP.NET framework, ASP.NET Core runs on Windows, Linux, and macOS, allowing developers to build apps that can be hosted on different operating systems. * Unified framework: ASP.NET Core unifies the ASP.NET MVC and Web API frameworks, enabling developers to create web apps and RESTful services using a consistent development model. * Performance: ASP.NET Core is designed to deliver superior performance through optimizations like modularity, reduced memory footprint, and faster request processing. * Dependency Injection (DI): Built-in support for dependency injection promotes loosely coupled architectures and enables easier unit testing. * Middleware pipeline: The framework uses a request pipeline called middleware, which allows developers to control how requests and responses are handled in a more granular way. * Modular framework: ASP.NET Core uses a lightweight, modular architecture that allows developers to bring in only the features they need, reducing the app's overhead and making it more efficient. * Hosting flexibility: ASP.NET Core can be hosted on any web server like IIS, Nginx, Apache, or even in containers like Docker. * Integrated with modern frameworks: ASP.NET Core works seamlessly with modern front-end frameworks like Angular, React, Vue.js, etc. ### Web API and RESTful API in ASP.NET Core ![image](https://hackmd.io/_uploads/SkAVWh1kyl.png) Web API in ASP.NET Core allows developers to build RESTful services, which expose data and functionality over the web using HTTP. RESTful APIs are based on REST (Representational State Transfer) principles, which rely on stateless, client-server communication where resources are represented in a structured format like JSON or XML. #### HTTP as the Client-Server Communication Protocol In a RESTful API, communication between the client (e.g., a web browser, mobile app) and the server happens through HTTP. The client makes an HTTP request, and the server sends back an HTTP response. The main HTTP methods (verbs) used in REST APIs are: * GET: Fetches data from the server. * POST: Sends data to the server to create a new resource. * PUT: Updates an existing resource on the server. * DELETE: Removes a resource from the server. #### Basic HTTP Request Structure * HTTP Method (Verb): Specifies the action (GET, POST, PUT, DELETE). * URL (Endpoint): The address of the resource (e.g., /api/products). * Headers: Provide metadata (e.g., Content-Type, Authorization). * Body: Contains the data (usually in JSON) for methods like POST or PUT. #### HTTP Response Structure * Status Code: Indicates the result of the request. * Headers: Provide metadata (e.g., Content-Type, Cache-Control). * Body: Contains the response data, often in JSON. #### Common HTTP Status Codes 200 OK: Request was successful (common for GET). 201 Created: Resource successfully created (POST). 400 Bad Request: Client sent an invalid request. 401 Unauthorized: Authentication is required. 404 Not Found: Resource not found. 500 Internal Server Error: Server encountered an error. ![WhatsApp Image 2024-10-11 at 10.05.41 PM](https://hackmd.io/_uploads/SkNIPeDJke.jpg) ### Example: Making an HTTP Request Client Request: ``` GET /api/products HTTP/1.1 Host: example.com ``` Server Response ``` HTTP/1.1 200 OK Content-Type: application/json [ { "id": 1, "name": "Product A" }, { "id": 2, "name": "Product B" } ] ``` In this example, the client requests a list of products, and the server responds with a JSON array of product objects. ### Endpoint ![1_XZ_C8CgA3v9HCZFLsQiyMQ](https://hackmd.io/_uploads/S1QNQ2Jykg.png) #### Steps of How an HTTP Request is Made and the Response is Received Client Sends an HTTP Request: * The client (e.g., a web browser, mobile app, or API client like Postman) initiates an HTTP request to a specific URL (endpoint) on the server. * The request includes: * HTTP method (e.g., GET, POST). * Headers (e.g., Content-Type, Authorization). * Optional Body (for methods like POST/PUT that send data). Example Request: ``` GET /api/products HTTP/1.1 Host: example.com ``` * DNS Lookup: The client performs a DNS lookup to resolve the server's domain name (e.g., example.com) to its IP address. * TCP/IP Connection: The client establishes a TCP connection to the server using the IP address and port number (typically port 80 for HTTP or port 443 for HTTPS). If the request is over HTTPS, the connection is secured using SSL/TLS. * Server Receives and Processes the Request: * The server (hosting the web application or API) listens for incoming HTTP requests. * Once the request is received, the server processes it by: * Parsing the HTTP method and URL. * Verifying any authentication or authorization details. * Fetching data or performing actions based on the request (e.g., retrieving data from a database). * Server Prepares an HTTP Response: The server processes the request and generates a response, which includes: * Status Code (e.g., 200 OK, 404 Not Found). * Response Headers (e.g., Content-Type, Cache-Control). * Response Body (e.g., the requested data in JSON format). Example Response: ```HTTP/1.1 200 OK Content-Type: application/json [ { "id": 1, "name": "Product A" }, { "id": 2, "name": "Product B" } ] ``` * Server Sends the HTTP Response: The server sends the response back over the established TCP connection to the client. * Client Receives the HTTP Response: * The client receives the response from the server. * The client reads the status code to determine if the request was successful and processes the response body (e.g., rendering data on the web page, updating an app's UI). * Connection Closure: Depending on the HTTP version and connection type, the TCP connection may either be closed immediately (HTTP/1.1) or kept alive for reuse (HTTP/2, HTTP/3). ### Create New Web API Project follow the same steps we mentioned in the last lectures. #### Project Structure #### High-Level Overview of ASP.NET Core Framework ![image](https://hackmd.io/_uploads/r19FH3ky1x.png) ### **Request Handling:** In ASP.NET Core, HTTP requests from the client (browser, API client, etc.) are handled by the application through a request pipeline. The request starts at the web server (e.g., Kestrel or IIS), then flows through the middleware pipeline, where various operations such as authentication, logging, and routing are processed, before being handed to a controller for the actual processing of the request. After processing, the server sends a response back to the client, often in formats like JSON or HTML. ### Middleware Pipeline: Middleware is a series of components that handle requests and responses. Each component in the pipeline can either process the request, modify it, or pass it to the next component. Examples of middleware include authentication, logging, error handling, and static file serving. Middleware is registered in the Startup.cs file, specifically in the Configure method. Each request passes through the pipeline in sequence, with middleware determining how it’s handled. ``` app.UseAuthentication(); app.UseAuthorization(); app.UseRouting(); app.UseEndpoints(...); ``` ### Routing: Routing maps incoming requests (URLs) to specific controller actions. ASP.NET Core uses attribute-based or convention-based routing to determine how requests should be directed to the correct part of the application. In `Startup.cs`, routes are configured with `app.UseRouting()` and mapped with `app.UseEndpoints()`. ``` [HttpGet("api/freelancers")] public IActionResult GetAllFreelancers() { ... } ``` ### Controllers: Controllers are responsible for handling incoming HTTP requests, processing them, and returning a response. In a Web API, controllers are classes that are decorated with the [ApiController] attribute and contain action methods (e.g., GET, POST, PUT). Each action in a controller corresponds to an endpoint or URL, handling requests like reading data, adding new data, or updating/deleting resources. ``` [ApiController] [Route("api/[controller]")] public class FreelancersController : ControllerBase { [HttpGet] public IActionResult GetFreelancers() { ... } } ``` ### Models and Data Serialization: * Models represent the structure of the data your application works with (e.g., Freelancer, Client). They are used to represent the state of data exchanged between the server and client. * Serialization refers to the process of converting these models into formats like JSON or XML for transmission to the client. * ASP.NET Core automatically handles JSON serialization using System.Text.Json, making it easy to send or receive data from clients in a structured format. Example Model: ```csharp public class User { public int Id { get; set; } public string Name { get; set; } } ``` ```json { "id": 1, "name": "Fadi.R" } ``` ## Pre-requisities for next implementations - You should understood the previous session about OOP. - You should understood how we designed and implemented User abstract class, Freelancer, Client, and SystemUser models. **Note:** in the next topic, we will use the implementation of the previous lesson. ## Implementing the api/v1/projects Endpoint in ASP.NET Core We’ll create a basic API to handle projects using an in-memory list to store data. The endpoint will support the POST method to allow creating new projects. **Steps:** * Create the Project Model: Define the Project class with properties of different data types (string, int, DateTime, etc.). * Set up the Controller: * Implement the `/v1/projects` endpoint using in-memory storage (a static list) to store projects. * Create the POST action for adding a new project via a JSON payload. ```csharp public class Project { public int Id { get; set; } // Integer property to represent a unique ID public string Title { get; set; } // String for the trainer's name public int Description { get; set; } // Integer for agel } ``` Controller: ```csharp using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; namespace YourNamespace.Controllers { [ApiController] [Route("v1/[controller]")] public class ProjectsController : ControllerBase { // In-memory list to store trainers private static List<Project> projects = new List<Project>(); private static int nextId = 1; // For auto-incrementing the ID // POST /v1/projects [HttpPost] public IActionResult CreateProject([FromBody] Project project) { if (project == null) { return BadRequest("Invalid data."); } // Assign a new ID and add the trainer to the list project.Id = nextId++; projects.Add(project); // Return 201 Created status with the new trainer's details return CreatedAtAction(nameof(CreaterProject), new { id = project.Id }, project); } } } ``` When making a POST request to /v1/projects, send the following JSON body: ```json { "title": "Landing Page website", "description": "descr." } ``` Response: ```json { "title": "Landing Page website", "description": "descr." } ``` **IActionResult** IActionResult is an interface in ASP.NET Core that represents the result of an action method in a controller. It allows you to return different types of responses (status codes, data, etc.) in a flexible way. **Why Use IActionResult?** * Flexibility: Instead of returning a specific type (e.g., string or JsonResult), you return IActionResult to allow for different types of responses, such as Ok(), BadRequest(), NotFound(), Created(), etc. * Customization: It allows action methods to return different results based on conditions, like returning BadRequest if input validation fails, or Ok if the operation is successful. **Examples of Return Types:** * Ok() – Returns HTTP 200 (OK) with optional content. * BadRequest() – Returns HTTP 400 (Bad Request) when something is wrong with the input. * CreatedAtAction() – Returns HTTP 201 (Created) when a new resource is created **CreatedAtAction** CreatedAtAction is a helper method that returns a 201 Created status along with the location of the newly created resource. It also allows you to return the resource itself in the response body. **Parameters:** * The first parameter (nameof(CreateProject)) specifies the name of the action that was used to create the resource (useful for returning the correct URL for the created resource). * The second parameter (new { id = project.Id }) specifies the route values for the newly created resource (usually the ID). * The third parameter (project) is the content returned in the response body, which is typically the newly created resource. **Why Use It?:** When a resource (like a new project) is created, the correct HTTP status is 201 Created. Along with this status, the API should also provide a Location header that tells the client where the new resource can be accessed. CreatedAtAction automatically generates this response, including the location of the new resource. `nameof` **Function** The nameof function is a C# feature that returns the name of a variable, type, or member as a string. **Why Use nameof?** Refactoring Safety: Instead of hardcoding the method name ("CreateProject"), nameof(CreateProject) automatically uses the method name. If you rename the method later, nameof will automatically update to reflect the change. Code Maintenance: It reduces errors, as string-based method names can be prone to typos, and makes code easier to maintain. Here, nameof(CreateProject) will return the string "CreateProject". If you later rename the method, using nameof ensures that the reference is still correct. **`ApiController` Attribute** * **The [ApiController] attribute** is a key feature in ASP.NET Core that enables attribute routing and simplifies the development of RESTful APIs. When you apply this attribute to a controller, it provides several benefits: * Automatic Model Validation: Automatically validates the model state before executing the action. If the model is invalid, the framework returns a 400 Bad Request response. * **Inferred Route Handling:** It enables automatic inference of the route from the controller's name and actions. For example, a controller named TrainersController will handle requests to /v1/projects by default if you set up routing accordingly. * **Parameter Binding:** The attribute improves model binding by automatically binding parameters from the request body, query string, or route data based on the action's method signature. **How to Map Endpoints** When you define a controller action in ASP.NET Core, such as: ```csharp [ApiController] [Route("v1/[controller]")] public class ProjectssController : ControllerBase { [HttpPost] public IActionResult CreateProject([FromBody] Project project) { // Implementation } } ``` Route Configuration in `Program.cs` or `Startup.cs` In your `Program.cs` (or Startup.cs in earlier versions of ASP.NET Core), you typically define the routing configuration in the Configure method. The routing system processes incoming requests and maps them to the appropriate controller actions based on defined routes. For example, in `Program.cs`: ```csharp using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Add controllers to the service container var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseAuthorization(); // Map controller endpoints app.MapControllers(); // Enables attribute routing app.Run(); ``` **How It All Connects** * Controller Definition: The TrainersController class, decorated with [ApiController], defines the route for the API ([Route("v1/[controller]")]). This automatically sets up the base path for all actions in the controller. * Routing with MapControllers(): The call to app.MapControllers() in Program.cs tells the ASP.NET Core routing system to look for controller classes with [ApiController] attributes and map their actions to HTTP endpoints based on their routing attributes. * Action Method Execution: When a request is made to /v1/projects with the HTTP POST method, the ASP.NET Core framework uses the route defined in the ProjectsController to invoke the CreateProject action method. ## Running the Application and Explore Swagger Swagger is an open-source toolset that helps developers design, build, document, and test RESTful APIs. It provides a user-friendly interface (called Swagger UI) that automatically generates from your API code and annotations, making it easy to visualize and interact with your API during development. When integrated into an ASP.NET Core project, Swagger generates documentation for your API and allows you to test your endpoints directly in the browser, without the need for external tools like Postman. **How Swagger is Useful for Testing During Development** * Interactive API Documentation: Swagger UI displays all your API endpoints with detailed information on their HTTP methods, parameters, and response types. This is auto-generated based on the annotations in your code. * Ease of Testing: You can test API calls directly from the Swagger interface. This makes it easy to check if your endpoints are working as expected during development without needing to write separate client-side code or use external tools. * Consistency: Swagger ensures that your documentation stays in sync with your code because it is generated dynamically. * Support for Different HTTP Methods: It supports GET, POST, PUT, DELETE, etc., allowing you to easily test all aspects of your API. ## Trainer CRUD operations ### Get All Projects ```csharp [HttpGet] public IActionResult GetAllProjects() { return Ok(projects); } ``` ### Get Project by ID This will allow us to retrieve a single project by their ID. ```csharp [HttpGet("{id}")] public IActionResult GetProjectById(int id) { var project = trainers.FirstOrDefault(t => t.Id == id); if (project == null) { return NotFound(); } return Ok(project); } ``` `FirstOrDefault` is a LINQ method in C# that is used to find the first element in a collection that satisfies a given condition, or return a default value (which is null for reference types) if no such element exists. How it Works: Collection: It operates on collections like lists or arrays. Condition: You provide a condition (called a predicate) that specifies the criteria for selecting an item. Result: If an element that matches the condition is found, FirstOrDefault returns the first one. If no element matches the condition, it returns the default value, which for reference types (like objects) is null. * projects is the list we are searching through. * t => t.Id == id is the condition, where we are looking for a trainer whose Id matches the id provided. * If a matching project is found, it's stored in the trainer variable. If not, projects will be null. ### Update (PUT) Operation This will allow us to update an existing project's information by their ID. ```csharp [HttpPut("{id}")] public IActionResult UpdateProject(int id, [FromBody] Trainer updatedProject) { var project = projects.FirstOrDefault(t => t.Id == id); if (project == null) { return NotFound(); } // Update the trainer's details project.Title = updatedProject.Title; project.Description = updatedProject.Description; return NoContent(); // 204 No Content indicates a successful update } ``` **Updating the Project without Resetting the List** **How it works:** Since trainer is a reference to the object in the list, any changes made to its properties (like Title, Description, etc.) will automatically update the object within the list. You don’t need to reset the list or replace the object explicitly. The list already holds a reference to this trainer object, so when you change its properties, the changes are reflected in the list. ### Delete (DELETE) Operation This will allow us to delete a trainer from the list by their ID. ```csharp [HttpDelete("{id}")] public IActionResult DeleteProject(int id) { var project = projects.FirstOrDefault(t => t.Id == id); if (project == null) { return NotFound(); } projects.Remove(project); return NoContent(); // 204 No Content to indicate successful deletion } ``` `projects.Remove(project)` This line of code removes the project object from the in-memory list of trainers: ```csharp projects.Remove(project); ``` **How it works:** * The Remove method removes the specified item from the list. In this case, it removes the project object that was previously found using FirstOrDefault. * Effect: Once the object is removed, it no longer exists in the list, meaning future queries to the list won’t find this trainer anymore. # Task 02 1. Create ASP.NET Core Web API project and name it "Aon-Freelance" 2. Init Git for the project and push it initally to GitHub 3. Create Model called "Project" which has the following props: - Id - Title 4. Implement the following endpoints: - /api/v1/projects GET (To Get All projects) - /api/v1/projects POST (to store new project) - /api/v1/projects/{id} GET (to get specific project by ID) - /api/v1/projects/{id} DELETE (to delete specific project) 5. Commit your changes and push them on the Repo on GitHub 6. Submit the form