0% found this document useful (0 votes)
2 views

ASP.NET WebAPI

The document outlines the differences between traditional web applications and client-side single-page applications (SPAs), emphasizing the role of Web APIs in serving data to these applications. It explains the HTTP protocol, its request and response structure, status codes, and introduces RESTful architecture as a guideline for API design. Additionally, it provides an overview of a Student Admission project using Angular and ASP.NET Core Web API, detailing the project flow and initial setup steps.

Uploaded by

Sanjeev Kumar
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

ASP.NET WebAPI

The document outlines the differences between traditional web applications and client-side single-page applications (SPAs), emphasizing the role of Web APIs in serving data to these applications. It explains the HTTP protocol, its request and response structure, status codes, and introduces RESTful architecture as a guideline for API design. Additionally, it provides an overview of a Student Admission project using Angular and ASP.NET Core Web API, detailing the project flow and initial setup steps.

Uploaded by

Sanjeev Kumar
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 24

//---------------------------------------------------------------- Web API ------------------------------------------------------------------//

Traditional web applications handle requests by returning HTML to the user, which is displayed in a web browser. You can easily build applications of this
nature using Razor Pages to generate HTML with Razor templates(the View in MVC). Client-side single-page applications (SPAs) have become popular in
recent years with the development of frameworks such as Angular, React, and Vue. These frameworks typically use JavaScript that runs in a user’s web
browser to generate the HTML they see and interact with. The server sends this initial JavaScript to the browser when the user first reaches the app. The
user’s browser loads the JavaScript and initializes the SPA before loading any application data from the server. Once the SPA is loaded in the browser,
communication with a server still occurs over HTTP, but instead of sending HTML directly to the browser in response to requests, the server-side application
sends data (normally in a format such as JSON) to the clientside application. The SPA then parses the data and generates the appropriate HTML to show to a
user. In other words, the browser then modifies the application’s UI depending on the data it receives. The server-side application endpoint that the client
communicates with is sometimes called a Web API.
// API : A Web API exposes multiple URLs that can be used to access or change data on a server. It’s typically accessed using HTTP. Web Browser/ Other
Applications/Mobile/Windows Applications can invoke (or consume) an API endpoint. API is responsible for getting a request from the client and then
sending a response to the client.
One of the selling points of using a Web API is that it can serve as a generalized backend for all your client applications. For example, you could start by
building a client-side application that uses a Web API. Later you could add a mobile app that uses the same Web API, with little or no modification required
to your ASP.NET Core code.
// ASP.NET Web API : The differentiation from traditional web applications/ASP.NET MVC is primarily in the view part of MVC. Instead of returning
HTML(View), they return data as JSON or XML, which client applications use to control their behavior or update. In ASP.NET Core, the MVC pattern applies
equally well when building a Web API, but the view part of MVC involves generating a machine-friendly response rather than a user-friendly response. WE
create Web API controllers in ASP.NET Core in the very same way we create traditional MVC controllers. The only thing that differentiates them from a
code perspective is the type of data they return—MVC controllers typically return a ViewResult; Web API controllers generally return raw .NET objects from
their action methods, or an IActionResult such as StatusCodeResult.

// These API services can be called by any technology, not just those under the Microsoft umbrella (Angular, React, Blazor, etc). Calls to a Web API service
are based on the core HTTP verbs (Get, Put, Post, Delete) through a uniform resource identifier (URI) such as the following:
http://www.skimedic.com:5001/api/cars
If this looks like a uniform resource locator (URL), that’s because it is! A URL is simply a URI that points to a physical resource on a network.
Calls to Web API use the Hypertext Transfer Protocol (HTTP) scheme on a particular host (in this example, www.skimedic.com) on a specific port (5001 in
the preceding example), followed by the path (api/ cars) and an optional query and fragment (not shown in this example). Web API calls can also include
text in the body of the message.
// ASP.NET Web API is also designed to be a service-based framework for building REpresentational State Transfer (RESTful) services.
//---------------------------------------------------------------- HTTP ------------------------------------------------------------------//

//Protocol : In networking, a protocol is a set of rules for formatting and processing data. Network protocols are like a common language for computers.
The computers within a network may use vastly different software and hardware; however, the use of protocols enables them to communicate with each
other regardless. Standardized protocols are like a common language that computers can use, similar to how two people from different parts of the world
may not understand each other's native languages, but they can communicate using a shared third language. If one computer uses the Internet Protocol
(IP) and a second computer does as well, they will be able to communicate. But if one computer uses IP and the other does not know this protocol, they will
be unable to communicate. On the Internet, there are different protocols for different types of processes. Protocols are often discussed in terms of which
OSI model layer they belong to.
// OSI : The Open Systems Interconnection (OSI) model is an abstract representation of how the Internet works. It contains 7 layers, with each layer
representing a different category of networking functions.

// Protocols make these networking functions possible. For


instance, the Internet Protocol (IP) is responsible for
routing data by indicating where data packets come from
and what their destination is. IP makes network-to-
network communications possible. Hence, IP is considered
a network layer (layer 3) protocol. As another example, the
Transmission Control Protocol (TCP) ensures that the
transportation of packets of data across networks goes
smoothly. Therefore, TCP is considered a transport layer
(layer 4) protocol.
A packet is a small segment of data; all data sent over a
network is divided into packets.
// IP is a network layer protocol responsible for routing.
But it is not the only network layer protocol – there is
Internet Protocol Security (IPsec), The Internet Control
Message Protocol (ICMP) ,etc.
// TCP is a transport layer protocol that ensures reliable
data delivery. TCP is meant to be used with IP, and the two
protocols are often referenced together as TCP/IP.
// HTTP: The Hypertext Transfer Protocol (HTTP) is the foundation of the World Wide Web, the Internet that most users interact with. It is used for
transferring data between devices. HTTP belongs to the application layer (layer 7), because it puts data into a format that applications (e.g. a browser) can
use directly, without further interpretation. The lower layers of the OSI model are handled by a computer's operating system, not applications. This protocol
is designed to transfer information between networked devices and runs on top of other layers of the network protocol stack. A typical flow over HTTP
involves a client machine making a request to a server, which then sends a response message.
The HTTP protocol is a stateless one. This means that every HTTP request the server receives is independent and does not relate to requests that came
prior to it, meaning that the server doesn't store information about previous requests and responses. In contrast, the FTP protocol is stateful.
//HTTPS: The problem with HTTP is that it is not encrypted — any attacker who intercepts an HTTP message can read it. HTTPS (HTTP Secure) corrects this
by encrypting HTTP messages.
//TLS/SSL: Transport Layer Security (TLS) is the protocol HTTPS uses for encryption. TLS used to be called Secure Sockets Layer (SSL).

// What is in an HTTP request?


An HTTP request is the way Internet communications platforms such as web browsers ask for the information they need to load a website. Each HTTP
request made across the Internet carries with it a series of encoded data that carries different types of information. A typical HTTP request contains:
1)HTTP version type: versions include HTTP/1.1, HTTP/2, and HTTP/3
2)a URL : uniform resource locator
3)an HTTP method : An HTTP method, sometimes referred to as an HTTP verb, indicates the action that the HTTP request expects from the queried server.
For example, two of the most common HTTP methods are ‘GET’ and ‘POST’; a ‘GET’ request expects information back in return (usually in the form of a
website), while a ‘POST’ request typically indicates that the client is submitting information to the web server (such as form information, e.g. a submitted
username and password).
4)HTTP request headers : HTTP headers contain text information (metadata) stored in key-value pairs, and they are included in every HTTP request (and
response). These headers communicate core information, such as what browser the client is using, what data is being requested, the content type, content
length, Authorization, etc.
5)Optional HTTP body: The body of a request is the part that contains the ‘body’ of information the request is transferring. The body of an HTTP request
contains any information being submitted to the web server, such as a username and password, or any other data entered into a form (in case of POST).
//What is in an HTTP response?

An HTTP response is what web clients (often browsers) receive from an Internet server in answer to an HTTP request. These responses communicate
valuable information based on what was asked for in the HTTP request. A typical HTTP response contains:
1) HTTP status code
2)HTTP response headers : Much like an HTTP request, an HTTP response comes with headers (metadata) that convey important information such as the
language and format of the data being sent in the response body, the size of the content, when is this invalid/expires,etc.
3)HTTP body (optional): Successful HTTP responses to ‘GET’ requests generally have a body which contains the requested information. In most web
requests, this is HTML data(or XML, JSON, BLOBS) that a web browser will translate into a webpage.

//HTTP status codes are 3-digit codes most often used to indicate whether an HTTP request has been successfully completed. Status codes are broken into
the following 5 blocks:
1xx : Informational, 2xx : Success , 3xx : Redirection, 4xx : Client Error, 5xx : Server Error
The “xx” refers to different numbers between 00 and 99.
//Status codes starting with the number ‘2’ indicate a success. For example, after a client requests a webpage, the most commonly seen responses have a
status code of ‘200 OK’, indicating that the request was properly completed.
// If the response starts with a ‘4’ or a ‘5’ that means there was an error and the webpage will not be displayed. A status code that begins with a ‘4’
indicates a client-side error (it is very common to encounter a ‘404 NOT FOUND’ status code when making a typo in a URL).
//A status code beginning in ‘5’ means something went wrong on the server side. Status codes can also begin with a ‘1’ or a ‘3’, which indicate an
informational response and a redirect, respectively.

//---------------------------------------------------------------- RESTful ------------------------------------------------------------------//

Representational State Transfer (REST) is a software architecture that imposes conditions on how an API should work. REST was initially created as
a guideline to manage communication on a complex network like the internet. You can use REST-based architecture to support high-performing and reliable
communication at scale. You can easily implement and modify it, bringing visibility and cross-platform portability to any API system.
API developers can design APIs using several different architectures. APIs that follow the REST architectural style are called REST APIs. Web services that
implement REST architecture are called RESTful web services. The term RESTful API generally refers to RESTful web APIs. However, you can use the terms
REST API and RESTful API interchangeably. REST is a set of architectural constraints, not a protocol or a standard. API developers can implement REST in a
variety of ways. When a client request is made via a RESTful API, it transfers a representation of the state of the resource to the requester or endpoint.
This information, or representation, is delivered in one of several formats via HTTP: JSON (Javascript Object Notation), HTML, XLT, Python, PHP, or plain
text. JSON is the most generally popular file format to use because, despite its name, it’s language-agnostic, as well as readable by both humans and
machines.

Though the REST API have few criteria to conform to, it is still considered easier to use than a prescribed protocol like SOAP (Simple Object Access Protocol),
which has specific requirements like XML messaging, and built-in security and transaction compliance that make it slower and heavier. In contrast, REST is a
set of guidelines that can be implemented as needed, making REST APIs faster and more lightweight, with increased scalablity—perfect for Internet of
Things (IoT) and mobile app development.

The following are some of the principles of the REST architectural style (A client-server architecture made up of clients, servers, and resources, with
requests managed through HTTP) :
1)Uniform interface : The uniform interface is fundamental to the design of any RESTful webservice. It indicates that the server transfers information in a
standard format. The formatted resource is called a representation in REST. This format can be different from the internal representation of the resource on
the server application. For example, the server can store data as text but send it in an HTML representation format.
Uniform interface imposes four architectural constraints:
a)Requests should identify resources. They do so by using a uniform resource identifier(URI). Thus resources requested are identifiable and separate from
the representations sent to the client.
b)Clients have enough information in the resource representation to modify or delete the resource if they want to. The server meets this condition by
sending metadata that describes the resource further.
c)Clients receive information about how to process the representation further. The server achieves this by sending self-descriptive messages that contain
metadata about how the client can best use them.
d)Clients receive information about all other related resources they need to complete a task. The server achieves this by sending hyperlinks/ hypermedia in
the representation so that clients can dynamically discover more resources, or, after accessing a resource the client should be able to use hyperlinks to find
all other currently available actions they can take.
2)Statelessness : In REST architecture, statelessness refers to a communication method in which the server completes every client request independently of
all previous requests. Clients can request resources in any order, and every request is stateless or isolated from other requests. This REST API design
constraint implies that the server can completely understand and fulfill the request every time. Stateless client-server communication, meaning no client
information is stored between get requests and each request is separate and unconnected.
3)Layered system: In a layered system architecture, the client can connect to other authorized intermediaries between the client and server, and it will still
receive responses from the server. Servers can also pass on requests to other servers. You can design your RESTful web service to run on several servers
with multiple layers such as security, application, and business logic, working together to fulfill client requests. These layers remain invisible to the client. In
short, this layered system organizes each type of server (those responsible for security, load-balancing, etc.) involved in the retrieval of requested
information into hierarchies, invisible to the client.
4)Cacheability: RESTful web services support caching, which is the process of storing some responses on the client or on an intermediary to improve server
response time. For example, suppose that you visit a website that has common header and footer images on every page. Every time you visit a new website
page, the server must resend the same images. To avoid this, the client caches or stores these images after the first response and then uses the images
directly from the cache. RESTful web services control caching by using API responses that define themselves as cacheable or noncacheable.
5)Code on demand (Optional): In REST architectural style, servers can temporarily extend or customize client functionality by transferring software
programming code to the client. For example, when you fill a registration form on any website, your browser immediately highlights any mistakes you
make, such as incorrect phone numbers. It can do this because of the code sent by the server. In short, it is the ability to send executable code from the
server to the client when requested, extending client functionality.

//----------------------------------------------------- Student Admission Project --------------------------------------------------------//

// Overall, Project flow using Angular and ASP.NET Core Web API:
1. Angular Service Calls API Endpoints (example, http://localhost:5000/api/students).
2.ASP.NET Core Processes Request (via StudentController).
3.Entity Framework Saves/Retrieves Data from SQL Server.
4.ASP.NET Core Sends Response (JSON).
5. Angular Updates UI with data.
// Create new Project in Visual Studio -> ASP.NET Web API -> .NET 8 -> Solution:CoachingWebApp -> Project : StudentAdmission (or StudentAdmissionAPI)
// Solution Explorer -> Create a folder Models -> Add Class Student.cs.
// Within Student.cs (contains properties representing a Student details during Admission; start from a very basic model):
namespace StudentAdmission.Models
{
public class Student // Whatever we return on API call, or get input in Admission form, will be based on this model
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Email { get; set; }
public DateOnly DateOfBirth { get; set; }
public string ContactNumber { get; set; }
public string EnrolledBatch { get; set; }
public string SchoolName { get; set; }
}
}
// Next, we create a controller StudentController, for containing the different enpoints and action methods. Controller is the keyword that must be present
in the classname(convention).
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using StudentAdmission.Models; //(1) Provides ControllerBase - contains methods for returning the data & users related to webapp
namespace StudentAdmission.Controllers
{
[Route("api/StudentList")] //(2)
//[Route("api/[controller]")] //(3)
[ApiController] // This attribute/annotation notifies the application that the following is an API controller
public class StudentController : ControllerBase
{
[HttpGet] // (4)
public IEnumerable<Student> GetStudents()
{
return new List<Student> // On calling the above action method, the following list is returned
{ // For initial testing ,we hardcode the Student data here
new Student{ Id=1, FirstName="Sanjeev",LastName="Kumar",Gender="Male",DateOfBirth=new
DateOnly(1991,4,6)}, // Read up on the different formats of DateOnly; here it is (int year, int month, int day)
new Student{ Id=2, FirstName="Puja",LastName="Singh",Gender="Female",DateOfBirth=new
DateOnly(1997,6,9)} // Note: Other fields have not been supplied – they will be set to their default values, like null
};
}
}
}
//(1) In pure MVC application, we derive only from Controller (not ControllerBase) - because Controller provides suupport for Views as well. But in this case
we don't need the overhead of maintaining or supporting Views.
//(2) We must define a route to the following controller below it. Otherwise, on running the application, an error is thrown from app.MapControllers()
within Program.cs. Action methods on controllers annotated with ApiControllerAttribute must be attribute routed. So use the Route attribute. Its argument
must be “api” followed by name of the controller without Controller part. Note that the name of the controller is just an endpoint to expose and need not
necessarily be the name of the controller class. For example I can have "api/StudentList" and everything still works fine.
//(3) This automatically places the following controller name in place of [controller] but it can create problems - if the endpoint is being used by many client
we wouuld have to notify all clients in case we change the name of the controller here down the line. But if we hardcode the name Student in place of
[controller], we need not worry however the name of the controller class changes.
//(4)Whenever we add an action method or endpoint within a controller, we must specify the HTTP verb of that endpoint through another annotation - at
method level. On missing this annotation, the UI side fails to understand whether this is GET or POST or PUT, etc. On running the app, the Swagger page will
open, but displays Error. Swagger documnetation provides us the error code 500
implying server side error – that endpoint “api/student” has something invalid due to
which it failed to load – we didn’t specify the type of HTTP method.

// If we put every attribute properly, our app runs properly and Swagger displays the
GET endpoint. There we can “Try it out” and “Execute” to see if the endpoint is
working properly or not. We will get successful reponse (Status Code 200) if
everything works properly.
Note that the url right now would be something like https://swagger....index.html, but the reponse
section will provide us with the exact endpoint (Request URL) that we hit on Execute. It is this URL or
endpoint that is determined by our backend Web
API controllers,etc.
//  Note the reponse body we receive on clicking
Execute in Swagger UI. But if we visit the
aforementioned URL, we get this JSON display 
// The Raw data will be an array of Student objects.

// In this last section, we have been using our


Student model on API calls, but this is nont desired
in production application. We must only expose a
certain part of data on the client side. For this, we
use Data Transfer Objects (DTOs) – they provide a
wrapper between the entity/database model and
what is being exposed from the api. This cann be just
another class – containing only some properties of
the original database model.
// We create a Dto folder within Models folder -> create a class file StudentDTO.cs:
namespace StudentAdmission.Models.Dto
{
public class StudentDTO
{
public int Id { get; set; }
public string FirstName { get; set; } // Note how we don’t specify the Contact, Gender,etc
public string LastName { get; set; }
public DateOnly DateOfBirth { get; set; }
public string EnrolledBatch { get; set; }
public string SchoolName { get; set; }
}
}
//Next, we need to modify our Action method signature, etc within StudentController. Just below [HttpGet]:
public IEnumerable<StudentDTO> GetStudents() // Note , we now have a StudentDTO type and its list is returned
{
return new List<StudentDTO>
{
new StudentDTO{ Id=1, FirstName…….. .",EnrolledBatch="XI",DateOfBirth=new DateOnly(1991,4,6)},
new StudentDTO{ Id=2, FirstName….. . .EnrolledBatch="XII",DateOfBirth=new DateOnly(1997,6,9)}
};
}
//We generally obtain the Student list from some database, rather than hardcoding it. In all such cases, the database part is kept in a separate folder. In
present case, we design a hardcoded list separately, rather than creating a List in the GetStudents method. (separation of concerns and single responsibility)
// Create a Data folder -> create a StudentRepo.cs -> within this we create our List<StudentDTO> :
using StudentAdmission.Models.Dto;
namespace StudentAdmission.Data
{
public static class StudentRepo // Static class,so we need not create another object for this.
{
public static List<StudentDTO> studentList = new List<StudentDTO>
{
new StudentDTO{ Id=1, FirstName…. . .. OfBirth=new DateOnly(1991,4,6)},
new StudentDTO{ Id=2, FirstNa… . . . . OfBirth=new DateOnly(1997,6,9)}
};
}
}
// Next, within GetStudent() , we now return directly the list: return StudentRepo.studentList;
// Next, the client may need to get only one Student based on Id. For this we will need another action method with id as parameter. Moreover, we must set
HttpGet attribute to annotate this method as a GET method. But there might be confusion with regard to which method is to be invoked on GET, so within
HttpGet attribute we must include a parameter for id, and optionally constrain it to be an integer using “:”.
// Within StudentController, we declare another action method below GetStudents() :
[HttpGet("{id:int}")] // no whitespaces in between id , int and colon:
public StudentDTO GetStudent(int id)
{
return StudentRepo.studentList.FirstOrDefault(u => u.Id == id); // LINQ Fluent API ; returns the first result…
} //….whose Id property value is same as ‘id’ passed.
//  This is the error shown if we don’t pass ‘id’ parameter within HttpGet
attribute. Ambiguous exception. Also note, that if no attribute is provided,
it is considered a GET method by default.
// For APIs rather than simply returning the list or student details we also
return the status code through ActionResult (derives from IActionResult),
we can use any return type we want; for successful operation we return an
Ok object and pass the result we want.
// ActionResult<T> is a generic return type that combines the flexibility of
IActionResult with the type safety of returning a specific data type (T). It
allows us to return either an ActionResult or a particular type (T) from our controller actions. That means we can return standard HTTP responses like
NotFound() or BadRequest() or a specific data type when the operation is successful.
public class StudentController : ControllerBase
{
[HttpGet] //The ProducesResponseType attribute in ASP.NET Core Web API specifies the type of response…
[ProducesResponseType(StatusCodes.Status200OK)] //…and the HTTP status code that a particular action method can return.
public ActionResult<IEnumerable<StudentDTO>> GetStudents() //This attribute is useful for documenting API responses…
{ return Ok(StudentRepo.studentList); } //….clearly and explicitly, making it easier for developers to understand…
[HttpGet("{id:int}")] //…what kind of responses to expect from an API endpoint.
[ProducesResponseType(200)] //The ProducesResponseType attribute can be applied multiple times to an action method to specify….
[ProducesResponseType(400)] //…different possible responses. We can hardcode the Status Codes as integer in arguments or as…
[ProducesResponseType(StatusCodes.Status404NotFound)] //…member fields of static class Status Codes : more descriptive.
//[ProducesResponseType(200, Type = typeof(StudentDTO))] // We can use this declaration in case we didn’t use the…
public ActionResult<StudentDTO> GetStudent(int id) //…generic type of ActionResult, specifying StudentDTO.
{ // If we omit StudentDTO either in ActionResult<T> or in ProducesResponseType, on running,Swagger won’t know & show which…
if (id == 0) //….schema to expect and hence won’t show any schema under response codes.
{ return BadRequest(); } // For each return type possibility we provide [ProducesResponseType]
var student = StudentRepo.studentList.FirstOrDefault(u => u.Id == id);
if (student == null)
{ return NotFound(); }
return Ok(student);
}
}
//When the ASP.NET Web API calls a method on a controller, it must set values for the parameters, a process called parameter binding. The [FromBody]
attribute in the CreateStudent method is used in ASP.NET Core MVC (part of ASP.NET Core) and is not an inbuilt feature of C# itself. It is provided by
ASP.NET Core MVC as part of model binding. When a POST request is sent with a JSON body like:
{
"name": "John Doe",
"age": 20
}
The ASP.NET Core model binding system looks at the [FromBody] attribute, reads the request body, and deserializes the JSON into the StudentDTO object. It
works with input formatters (like JSON, XML). Only one parameter per action can have [FromBody] because the request body can be read only once.
By default, Web API uses the following rules to bind parameters:
1) If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so
forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.
2)For complex types, Web API tries to read the value from the message body, using a media-type formatter.
So, if we want to override the above default behaviour and force Web API to read a complex type from the URI, add the [FromUri] attribute to the
parameter. To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter.

So, we need the [FromBody] and [FromUri] attributes in Web API simply to override, if necessary, the default behaviour as described above. Note that you
can use both attributes for a controller method, but only for different parameters.
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public ActionResult<Student> CreateStudent([FromBody]StudentDTO studentDTO) // [FromBody] tells the framework…
{ //...to deserialize the HTTP request body into the studentDTO object using the configured input formatters (like JSON, XML, etc.).
if (studentDTO == null) //Without [FromBody], the framework would try to bind studentDTO from query parameters, form data, ….
{ return BadRequest(studentDTO); } //…or route values, but not the request body.
if (studentDTO.Id > 0) // We must not supply the id, from Swagger. The client supplies other details, and the id is auto incremented…
{ return StatusCode(StatusCodes.Status500InternalServerError); } //…by reading the first id from…
studentDTO.Id = StudentRepo.studentList.OrderByDescending(u => u.Id).FirstOrDefault().Id + 1;
StudentRepo.studentList.Add(studentDTO); //….a descending list and then adding 1.
return Ok(studentDTO);
} // Note that the data is not yet persisted. As in any data posted through POST in Swagger is alive only during the current run/session. For persisting the
data to a database we would need a framework.
//Input Formatters : Input formatters are components in ASP.NET Core MVC that handle the deserialization of the request body into C# objects. When a
client (like Postman, Angular, React, Swagger UI, etc.) sends a POST request with a JSON body, the input formatter takes that JSON and converts it into a C#
object. The default input formatter in ASP.NET Core is System.Text.Json (by default) or Newtonsoft.Json (if configured). Input formatters work in conjunction
with the [FromBody] attribute. When [FromBody] is used, ASP.NET Core selects an input formatter based on the Content-Type header of the request.
If the request has:
Content-Type: application/json → The JSON input formatter deserializes the request body into studentDTO.
Content-Type: application/xml → The XML input formatter (if enabled) deserializes into studentDTO.
By default, ASP.NET Core supports: JSON format (application/json) , XML format (application/xml) (only if configured)

Where Does the JSON Body Appear From During a POST Request -- The JSON body comes from the HTTP request sent by the client. The JSON body
({ "name": "John Doe", "age": 20 }) is sent by the client application (Swagger, Postman, JavaScript frontend, etc.). The input formatter reads this JSON and
converts it into a C# object (StudentDTO).

Does Swagger Obtain the Schema from studentDTO -- Yes, Swagger (via Swashbuckle) automatically generates the schema from the StudentDTO class.
Swagger uses reflection to analyze the method parameters and their types. Since CreateStudent expects a StudentDTO, Swagger automatically generates a
JSON schema for it. When using Swashbuckle.AspNetCore, Swagger will show an input form based on the properties of StudentDTO.

Input formatters handle deserialization of the request body (JSON → C# object).


The JSON body appears from the client request, sent using Postman, Swagger, frontend apps, etc..
Swagger generates the schema automatically from the StudentDTO class using reflection.

// Serialization in C# is the process of bringing an object into a form that it can be written on stream. It's the process of converting the object into a form so
that it can be stored on a file, database, or memory; or, it can be transferred across the network. Its main purpose is to save the state of the object so that it
can be recreated when needed. Deserialization in C# is the reverse process of serialization. It is the process of getting back the serialized object so that it
can be loaded into memory. It resurrects the state of the object by setting properties, fields etc. Types of Serialization- Binary,XML,JSON.
//Action Result is a data type. When it is used with an action method, it is called return type. As you know, an action is referred to as a method of the
controller, the Action Result is the result of the action when it executes. Thus, Action Result is a return type. This return type has many other derived types.
First, look at the base and derived types of ActionResult. Action Result is a base data type whose derived types are HttpStatusCodeResult, JavaScriptResult,
FileResult, ContentResult, JsonResult, EmptyResult, RedirectResult, RedirectToRouteResult, ViewResult. And, there must be a helper method against each
data type (either base or derived type).
When we go into ActionResult, we see that it is an abstract class.
It is an abstract class because the actions of the controller can return different types of data at the same time. So, it has to be abstract so that every HTTP
request can be handled properly. As you can see, the same action method, “Index,” returns two different types named Content and View; if you want to
return multiple types, you have to use the base type as ActionResult.

The above concept also answers the question “When to choose base type ActionResult or derived
type?”
Choosing the derived type for a specific result is a good practice, but when you want your action
method should return multiple types, then you have to use base type ActionResult.

Now, the important concept comes. There are three categories for the derived types. 

//Polymorphism: If Y inherits from X, an object of type Y can be treated as an object of type X due to
polymorphism. This allows you to return an instance of Y while declaring the return type as X.
This is a common technique in method overriding, factory patterns, and dependency injection
where base class references hold derived class instances. this technique is primarily a form of
polymorphism, but more specifically, it is called "covariant return type" (or return type
covariance).
Explanation:
Polymorphism: The general concept where a base class reference can point to a derived class
object.
Return Type Covariance: A specific case where a method in a derived class can override a base class method and return a more derived type.
In your case, you're returning an instance of Y while declaring the return type as X, which is allowed because Y is-a X (inheritance relationship). This is a
fundamental feature of covariant return types.

// Sometimes we need to provide the URL where the actual resource(Student) is created. Then we can return a CreatedAtRoute instead of a status code.
It is overloaded , has different signatures; but in general the first argument (routeName) is the route name of the GET to the resource. Here you must use
the Name param on the HttpGet attribute for it to work. The next argument is the routeValues which is an object containing the values that will be passed
to the GET method at the named route. It will be used to return the created object. We can say it’s a subset of the object created itself. The last argument is
the object that was created. Calls to our POST method will not only return the new object (as JSON) , it will set the location header on the reponse to the
URI that would get that resource. Location Header is an Http Header that the client can decide to do something with like automatically redirect to it.
.......
[HttpGet("{id:int}", Name="GetStudent")]
.. . . . . . . .
public ActionResult<StudentDTO> GetStudent(int id). . . . . . .
.......
[HttpPost]
….....
public ActionResult<Student> CreateStudent([Fro. . . .
......
return CreatedAtRoute("GetStudent",new { id = studentDTO.Id}, studentDTO); // In place of return Ok(studentDTO)
.. . . . .
.....
// Validations : To include validations for our properties on our model class, we can use built in Data Annotations (provided by ASP.NET Core). We apply
these data annotations on the model class by simply adding attributes like [Required], [MaxLength(30)],etc, built-in to the ASP.NET Core MVC framework
— and they come from the System.ComponentModel.DataAnnotations namespace.
Within StudentDTO.cs:
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
namespace StudentAdmission.Models.Dto
{
public class StudentDTO
{
[Key]
public int Id { get; set; }
[Required] //Field must be provided (non-null/non-empty)
[MaxLength(30)] // Limit the FirstName field to a maximum of 30 characters.
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string Gender { get; set; }
public string Email { get; set; }
[Required(ErrorMessage ="Date of Birth is Compulsory")]
public DateOnly DateOfBirth { get; set; }
[Required]
public string ContactNumber { get; set; }
public string EnrolledBatch { get; set; }
public string SchoolName { get; set; }
}
}
//When we run our app and send data through Swagger or Postman, ASP.NET Core will
1. Try to bind the JSON to our StudentDTO.
2. Run validation against [Required], [MaxLength],etc.
3. Detect any invalid format. Any issues (like missing Required fields) are recorded in ModelState.
4. If any invalid format is detected, it automatically returns HTTP 400 Bad Request with a validation error response like : “The field is required”. And our
controller method will never even run.
//ModelState is a property of type ModelStateDictionary in ASP.NET Core MVC/Web API that keeps track of: Model binding results (like if the incoming
request data matched the expected shape or data types) & Validation errors (e.g., required fields missing, wrong formats, etc.). It’s part of ASP.NET Core
MVC/Web API framework. It returns the ModelStateDictionary that contains the state of the model and of model-binding validation.

Client : Any entity that sends an HTTP request to your web app; Swagger UI sends this request when we click Try it out , Execute.
↓ In production, clients can be browsers, mobile apps, or other services.
Kestrel Web Server : It is the built-in cross-platform web server in ASP.NET Core that listens for incoming HTTP requests from a client and passes…
↓ …them to your app. Like a gatekeeper – it also sends the HTTP response back to the client. It is set up automatically by default….
Middleware Pipeline (UseRouting, UseAuthorization, etc.) …. Kestrel starts listening here 
app.Run();
↓ Middleware is configured in Program.cs via app.Use... methods, for Logginng, HTTPS redirection,etc.
Routing : A key Middleware UseRouting() – figures out which endpoint to call. It determines which Controller/Action to invoke based on URL patterns
↓ …like ([Route("api/[controller]")], etc.). Maps request like GET /api/products/1 to ProductsController.Get(int id).
[Model Binding] → fills parameters : ASP.NET Core maps the incoming HTTP data (from query, body, route, headers, etc.) to the parameters
of….
↓ …your controller method or to a model class. Recall the controller method CreateStudent([FromBody]StudentDTO studentDTO). Here the…
…. studentDTO is filled with data from the JSON body.
[Model Validation] → checks attributes like [Required] : ASP.NET Core checks for data annotations like [Required], [Range], etc. All
validation…
↓ …. errors are stored in ModelState. . If [ApiController] is used: It automatically returns 400 Bad Request if validation fails — before your ….
Controller Action runs (if validation passed) : …
controller method runs.
↓ If and only if validation is passed, our action method/controller method (like CreateStudent) is called and runs .
Returns IActionResult (e.g., Ok(), BadRequest()) : Action method returns a result, which can be a custom response. Response goes
through…..
↓ ….Middleware (if needed). ASP.NET Core converts the return value into JSON response or, View (if you're using MVC with Razor),or a redirect, etc.
Response Sent to Client: Kestrel sends the HTTP response back to the client

// In absence of [ApiController] at the top of StudentController class, we will still get Model binding & Validation still happens, but...we are responsible for
checking if the model is valid. That means you must manually check using ModelState.IsValid property. If you don't check ModelState.IsValid, and the
model is invalid (e.g., bad email), the controller will: Still run the method, Possibly do unwanted things with invalid data, Not return a 400 automatically.
// Within StudentController.cs :
……….. . .
namespace StudentAdmission.Controllers
{
[Route("api/StudentList")] // [ApiController] is removed
public class StudentController : ControllerBase
{
. . . … …. . . . . .
public ActionResult<Student> CreateStudent([FromBody]StudentDTO studentDTO)
{
if (!ModelState.IsValid) // Manually check validation using ModelState.IsValid property
{ // Returns error list in case of invalid input
return BadRequest(ModelState);
}
if (studentDTO == null)
{
.. . . . ..

// Reminder : Model State Validation is always automatically done by ASP.NET Core


before actually entering the action method. With [ApiController], we rest assured
that the action method is run/executed only if ModelState is valid. (use breakpoint in
your app to verify this)
//For Custom Validation using ModelState (in presence of [ApiController]): Suppose we need to ensure that our name is unique, and the entered name
does not exist in our repo.
public ActionResult<Student> CreateStudent([FromBody]StudentDTO studentDTO)
{ // No need for ModelState.IsValid since [ApiController] does the job. // Before Custom Validation, we might like to Console write all students in our..
foreach (var s in StudentRepo.studentList) // list, when this create action method is called. This is obviously optional. In …
Console.WriteLine(s.FirstName); // case we use EF Core we would use _db.Students instead of StudentRepo.studentList.
if (_db.Students.FirstOrDefault(u => u.FirstName.ToLower() == studentDTO.FirstName.ToLower()) != null)
{ //(1) Just to show the variation with EF Core, note how the Students list is first accessed and then LINQ is run on it.
ModelState.AddModelError("FirstName", "Student already Enrolled!"); //(2)
return BadRequest(ModelState); //(3)
}
….. . . .. ……
//(1) _db.Students OR StudentRepo.studentList : This is a list (probably of type DbSet<Student> OR List<Student> ) containing existing student records.
Next, .FirstOrDefault(...) : This LINQ method looks for the first element in the list that matches a condition. If it finds one, it returns the matching element.
If it doesn't find one, it returns null. Next, the condition is a lambda expression to filter the list.
u => u.FirstName.ToLower() == studentDTO.FirstName.ToLower()
It says: For each student u in the list, check if the lowercase version of u.FirstName matches the lowercase version of studentDTO.FirstName. This makes the
comparison case-insensitive. Finally, the result of LINQ query is checked against null. ( ……. != null) , only after which the if block would be executed.
This checks if a matching student was found. If a match is found, the if condition becomes true.
//(2)ModelState holds the state of model binding and validation. It is an instance of the ModelStateDictionary class. There are many methods available on
ModelState. The method .AddModelError(string key, string errorMessage) takes two arguments and adds a validation error to a specific field (key) in the
model. The key in this case is “FirstName”. The custom error message for this case is “Student already Enrolled!”. We may attach other custom error
messages on this key in other action methods.
//(2) Other methods available are .Clear() : Clears all model state errors and values ; .Remove(string key) : Removes model state entry (including any errors)
for a field ; .ContainsKey() : Checks if the model state contains a particular field ; .Keys : Gets all the field names ; .Values : Gets all the model state entries
(which include errors, attempted values, etc.). ; .TryAddModelError(string key, string errorMessage) : Tries to add an error if one doesn't already exist for
the key. Returns true if successful ; .Count : Returns the number of entries (keys) in the model state.
//(3) There are various overloads of BadRequest(...) in ASP.NET Core. BadRequest() mainly returns an HTTP 400 Bad Request response, indicating the client
sent invalid data. Without any argument/content – it Returns a generic 400 response with no body.
return BadRequest("Invalid input provided.") : Returns 400 with a plain string message/custom message in the body.
return BadRequest(ModelState) : This returns all model errors as a structured JSON object i.e, returns 400 with validation errors from the model state.
return BadRequest(object error) : Returns 400 with a custom error object in the body. This custom error object may be created like : var error = new
{ ErrorCode = 123, Message = "Invalid student data.", Hint = "Check if FirstName is already in use." };

// We have been using StudentRepo so far, which is good for testing. But whenever we restart out application, the data goes away. In a real life scenario, we
need a database. Here, we will work with Entity Framework Core – an ORM that makes the management of data and CRUD operations simple.
// Install Microsoft.EntityFrameworkCore.SqlServer (has dependency on EFCore , which is automatically installed) followed by
Microsoft.EntityFrameworkCore.Tools
// Within our database, we want a table after our Student class, with columns for the properties.
// Start SSMS – to know the server name, etc.
// We add more properties in the Student and StudentDTO file, with proper data annotations. Within Student.cs:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace StudentAdmission.Models
{
public class Student // We want to create a table based on this class- with each property as the columns.
{ // First,we need an Id column as a Primary Key . It must be an Identity column, i.e, auto incremented by 1 when record is created.
[Key] // This annotation explicity denotes the following property as a Primary Key.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // This explicitly makes the following property an Identity…
public int Id { get; set; } //…column -- We don’t have to enter the Id anymore, it will be auto incremented in the database.
public string FirstName { get; set; }
public string LastName { get; set; } // We can add [Required] here as well but we already did that at DTO level.
public string Gender { get; set; } // This entire classs is now our entity model.
public string Email { get; set; }
public DateOnly DateOfBirth { get; set; }
public string ContactNumber { get; set; }
public string EnrolledBatch { get; set; }
public string SchoolName { get; set; }
public DateTime JoiningDate { get; set; } // We may choose not to expose the JoiningData and LeavingDate to client…
public DateTime LeavingDate { get; set; } //…..hence we may remove these properties from StudentDTO.
}
}
// On using EntityFramework , we don’t do anything in the Database directly, everything is done through code - Code first approach. We work with a
DbContext which manages all the entities in our app. A DbContext instance represents a session with the database and can be used to query and save
instances of your entities. DbContext is a combination of the Unit Of Work and Repository patterns.
// Within Data folder, Add a class AppDbContext.cs. Within AppDbContext.cs :
using Microsoft.EntityFrameworkCore;
using StudentAdmission.Models;
namespace StudentAdmission.Data
{
public class AppDbContext : DbContext // This inherits all the built in functionalities that is inside EF core.
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base (options) //(2)
{
}
public DbSet<Student> Students { get; set; } // (1)Whatever entity (Student) we manage, we must have a DbSet for it.
// Name of this DbSet<> property (Students, in this case) will be the name of the table in our database.
}
}
//(1)Typically you create a class that derives from DbContext and contains Microsoft.EntityFrameworkCore.DbSet properties for each entity in the model. If
the Microsoft.EntityFrameworkCore.DbSet properties have a public setter, they are automatically initialized when the instance of the derived context is
created.
//(2) We added this constructor later, after we had created DbSet<Student> variable and the connection string. To connect/link to the actual database (SQL
Server) , this AppDbContext must use a connection string, which are packed within options, of type DbContextOptions<ApppDbContext>. Thus, options
represents the options to be used by a Microsoft.EntityFrameworkCore.DbContext. You normally override
Microsoft.EntityFrameworkCore.DbContext.OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder) or use a
Microsoft.EntityFrameworkCore.DbContextOptionsBuilder to create instances of this class (DbContextOptions) and it is not designed to be directly
constructed in your application code. The DbContextOptions is a public abstract class within Microsoft.EntityFramework namespace, and implements /
inherits from IDbContextOptions. Note that for DbContextOptions<TContext> :- TContext refers to the type of the context these options apply to.

//(2) options : It stores configuration settings for the DbContext, such as: Connection string, Database provider (SQL Server, PostgreSQL, SQLite, etc.),
Logging settings, Lazy loading configurations, Query tracking behavior,etc. In following section, within Program.cs the snippet:
options => options.UseSqlServer(...) inside AddDbContext configures this.
//(2) We add a constructor where we expect a DbContextOptions and we pass that to the base class. These steps thus configure EF Core in our database.
These steps remains same in ASP.NET MVC, razor pages,etc.

//(3) Upto this point, we haven’t actually created the connection string. We simply configured what an instance of database connection expects as options.
This connection string is created using different parameters obtained from SSMS when we open it. Those details are then put within a section
“ConnectionStrings” of appSettings.json.

// Within appSettings.json :
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
} // Logginng and AllowedHosts section is already present within appSettings.json. We configure the section ConnectionStrings
},
"AllowedHosts": "*",
"ConnectionStrings": { //(3)
"DefaultSQLConnection": "Server=DESKTOP-HA6VMFQ; Database=StudentAdmission; Trusted_Connection=True;
TrustServerCertificate=True;"
} // DefaultSQLConnection is a name(key) to connect to the SQL server via the connection string (value).
}
//(3)ConnectionStrings is predefined block - there is a helper method that helps us retrieve connection string by default
// Next, we need to register the above connection string through dependency injection, so that every instance of our WebApplication can use this
connection string to connect to the database. This is done through the Services container of the WebApplicationBuilder. So, next we go to program.cs to
add any services to dependency injection container -> then we will need to pass the connection string to the DbContext as well to use its basic inbuilt
features.

// Within Program.cs :
using Microsoft.EntityFrameworkCore;
using StudentAdmission.Data;
namespace StudentAdmission
{
public class Program
{
public static void Main(string[] args)
{ //(5) WebApplication is an inbuilt type in ASP.NET Core.
var builder = WebApplication.CreateBuilder(args); //(6)
// (7) Add services to the container.
// (8) Next, we register the database context for dependency injection.
builder.Services.AddDbContext<AppDbContext>(option =>
{
option.UseSqlServer(builder.Configuration.GetConnectionString("DefaultSQLConnection"));
}); // (8)
builder.Services.AddControllers(); //(7) This registers MVC Controllers to handle API requests
...........
...........
}
}
//(5) More specifically, WebApplication is a public sealed class within Microsoft.AspNetCore.Builder namespace. This class implements many interfaces like
IHost, IDisposable, IApplicationBuilder, etc. It contains many properties like Configuration, Services, Environment,etc which are of some Interface type like
IConfiguration,etc. It also contains some static methods like Create, overloaded CreateBuilder , CreateEmptyBuilder,etc. It also provides some Tasks like
RunAsync, etc. It represents the web application that will handle HTTP requests.It combines the functionalities of IHost and IApplicationBuilder, simplifying
the setup. Introduced in ASP.NET Core 6, it replaces the older HostBuilder-based approach. It is defined in the Namespace: Microsoft.AspNetCore.Builder.
//(6) WebApplication.CreateBuilder(args) : This method,defined within WebApplication class has a return type of WebApplicationBuilder.
WebApplicationBuilder is a sealed class, within Microsoft.AspNetCore.Builder namespace, and implements IHostApplicationBuilder. Thus, calling the
CreateBuilder method initializes a WebApplicationBuilder instance, which is responsible for setting up the application's configuration, services, and
middleware pipeline. args typically comes from Main(string[] args), allowing command-line arguments to influence app configuration. Thus, builder is an
instance of WebApplicationBuilder.
//(5)There is a method Create within WebApplication class whose return type is the class WebApplication itself. It means Create is a factory method that
initializes and returns an object of WebApplication. It's common for factory methods to return an instance of the class they are defined in. We use a Factory
Method instead of new WebApplication() - The Create method probably performs additional setup (like configuring hosting, middleware, services, etc.)
before returning the WebApplication instance. This hides complexity and provides a cleaner API for developers. The method is defined in a linked external
library (like ASP.NET Core DLLs), where you only see the definition in IntelliSense of Visual Studio, but the implementation is inside Microsoft's source code.
On looking at the source code, we find that Create method does return a WebApplication instance, through WebApplicationBuilder’s method.
public static WebApplication Create(string[]? args = null) => new WebApplicationBuilder(new() { Args = args }).Build();

//(7) Services is a property of WebApplicationBuilder that returns an IServiceCollection (return type is IServiceCollection). In turn, IServiceCollection is a
built-in interface that represents a collection of services (dependency injection container), where services are registered. When we say “Add Services…” ,
we refer to Dependency Injection (DI), which is a core feature of ASP.NET Core. We register services like database contexts, authentication, logging,
repositories, and more in the DI container. These services are then automatically provided/injected in controllers, wherever and whenever they are needed
in the application.

//(8) AddDbContext<TContext>() is a built-in generic extension method provided by Entity Framework Core (EF Core). It is defined in the
Microsoft.Extensions.DependencyInjection namespace and is used to register a DbContext with the ASP.NET Core Dependency Injection (DI) container. The
generic type parameter TContext must be a class that inherits from DbContext. Here, this method takes a parameter option of type
DbContextOptionsBuilder. By default, this method also sets the contextLifeTime as Scoped. Note that, In order for the options to be passed into your
context, you need to expose a constructor on your context that takes Microsoft.EntityFrameworkCore.DbContextOptions instance and passes it to the base
constructor of Microsoft.EntityFrameworkCore.DbContext. (This is done within AppDbContext.cs file)

//(8) We can use UseSqlServer from our Nuget package EntityFrameworkCore.SqlServer. This is an extension method used to Configure the context to
connect to a SQL Server database. It returns the options builder (of type DbContextOptionsBuilder) so that further configuration can be chained. Within this
we have to provide the connection string - which is within appSettings.json. The builder of our to be app has a collection of Configuration providers for our
application to compose. Here Configuration is a type of ConfigurationManager, which in turn, is a sealed class, inheriting from IConfiguration. Finally, the
connection string is obtained with the helper/extension method GetConnectionString("name"). This, in fact, is a shorthand for
GetSections("ConnectionStrings")[name].
//(8) Now, we have Configured EF Core using the provided options (UseSqlServer in this case). Later, when a controller or service needs AppDbContext, DI
provides it with the configured options.
// Also note, that when we call:
builder.Services.AddDbContext<AppDbContext>(...);
It adds an entry for AppDbContext inside IServiceCollection. Internally, it be something like this:
services.Add(new ServiceDescriptor(typeof(AppDbContext), provider =>
{ var options = provider.GetRequiredService<DbContextOptions<AppDbContext>>();
return new AppDbContext(options);
}, ServiceLifetime.Scoped));
This registers AppDbContext as a Scoped service and ensures that the WebApplication gets the right DbContextOptions<AppDbContext> when instantiated.

// To apply all the Database Changes from the code we have to perform 'migrations'. Go to Tools -> Nuget Package Manager -> Package Manager Connsole
// We need a set of scripts which will tell the database the table we need to create. At PMC : add-migration AddStudentTable
// A new folder ‘Migrations’ is then created - On examining this folder, we will find a class containing LINQ statements for creating table – EF Core creates a
table automatically based on this query. This class will have an Up method – containing commands for what it needs to do(Create) ; while if it fails to do so,
it rolls back the changes using another ‘Down’ method.
// Now to successfully apply/persist the migration , run update-database at the PMC. (ensure TrustedServerCertificate = True ; otherwise throws error)

// Now check in SQL Server through SSMS - we would find the table “Students” there. Also there will be a Migrations table which correlates to our
migrations folder in ouur app. EF core keeps track of which migrations have been applied and which it needs to apply next.

// Next, to populate/seed the database with some initial data, we use AppDbContext’s method called OnModelCreating, which we can over ride, using the
ModelBuilder instance. We use the Entity<> within ModelBuilder, and call its HasData method with the new objects of type Student.
// Within AppDbContext.cs : (below DbSet<Student> declaration)
protected override void OnModelCreating(ModelBuilder modelBuilder) //(1)
{ // protected: meaning it is accessible only within the DbContext or derived classes.
modelBuilder.Entity<Student>().HasData(
new Student // This creates a Student object with predefined values. When you run Update-Database, EF Core…..
{ //…inserts this data into the Students table.
Id = 1, FirstName = "Sanjeev" , LastName = "Kumar", Gender = "Male",
ContactNumber = "8976456543", DateOfBirth = new (1991, 3, 23)
},
new Student
{ Id = 2, FirstName = "Puja", LastName = "Singh", Gender = "Female",
ContactNumber = "8943456543", DateOfBirth = new(1996, 3, 13)
},
new Student
{
Id = 3, FirstName = "Jishnu", LastName = "Sikdar", Gender = "Male",
ContactNumber = "8335647573", DateOfBirth = new(2020, 4, 6)
}
);
//base.OnModelCreating(modelBuilder);
}
}
//(1)DbContext has an internal virtual method called OnModelCreating. Override this method to further configure the model that was discovered by
convention from the entity types exposed in Microsoft.EntityFrameworkCore.DbSet properties on your derived context. The resulting model may be cached
and re-used for subsequent instances of your derived context. Simply, OnModelCreating is a method in Entity Framework Core that allows you to configure
the model before it is finalized. It is typically overridden in your DbContext class to define relationships, constraints, default values, and seed data.
//Parameters: modelBuilder:- ModelBuilder provides an API to configure the entity model. You use it to define database configurations, relationships, table
mappings, and data seeding. Thus, the modelBuilder instance refers to the builder being used to construct the model for this context. Databases (and other
extensions) typically define extension methods on this object that allow you to configure aspects of the model that are specific to a given database.
Remarks: If a model is explicitly set on the options for this context (via
Microsoft.EntityFrameworkCore.DbContextOptionsBuilder.UseModel(Microsoft.EntityFrameworkCore.Metadata.IModel)) then this method will not be run.
However, it will still run when creating a compiled model.
// To successfully apply the new changes we will need to again run add-migration migrationName at the PMC, followed by update-database.
//(2) OnModelCreating is called when the DbContext is initialized to apply model configurations. It is executed before EF Core runs migrations or queries.
The DbContext is initialized when an instance of your DbContext class is created. This usually happens in one of the following scenarios:
1. When we Instantiate DbContext in Code i.e, if we manually create an instance of our derived DbContext using new. As soon as new AppDbContext() is
called, DbContext initializes, and OnModelCreating executes before any queries are run.
2. When Dependency Injection (DI) Creates DbContext (ASP.NET Core): In ASP.NET Core, DbContext is typically registered with Services in Program.cs.
When the AppController is instantiated, it ( the controller or service) requests the derived DbContext (AppDbContext _db). The DI container creates an
instance of the derived DbContext ( context initializes), thus executing/triggering OnModelCreating.
3. When Running Migrations : When we run Entity Framework Core migrations, DbContext initializes to apply configurations, OnModelCreating runs to
configure entity relationships, constraints, and seed data.
4. During migration execution using Update-Database, EF Core creates a temporary DbContext instance to apply migrations. This instance runs
OnModelCreating to configure models.
OnModelCreating executes once per DbContext instance creation. It does not run every time you query the database—only when the DbContext initializes.
// The last step is to modify the API Controller (StudentController.cs) we had earlier designed. Now we will use dependency injection (through constructor)
to get an instance of AppDbContext and perform database operations.
// The overall execution flow would be almost like this:
Program.cs runs first, setting up dependency injection and registering AppDbContext.
A request comes to api/StudentList (GET, POST, etc.).
StudentsController is created, and AppDbContext is automatically injected via the constructor.
A database query is executed inside a controller action (e.g., GetStudents()).
The result is returned as JSON.

//Within StudentController.cs: (necessary changes)


using Microsoft.AspNetCore.Mvc;
.......
namespace StudentAdmission.Controllers
{
[Route("api/StudentList")]
...…….
public class StudentController : ControllerBase
{ // In EF Core, the DbContext is passed to the controller through Dependency Injection
private readonly AppDbContext _db; // Declare a private field for holding the database context (_db)
public StudentController(AppDbContext db) // The database context db is a dependency for StudentController, or,…
{ _db = db; } //…..StudentController has a dependency on AppDbContext.
. . .. . . … …. ….
public ActionResult<IEnumerable<StudentDTO>> GetStudents()
{
return Ok(_db.Students.ToList()); // We can access the Students of type DbSet<Student> through the context _db
//return Ok(StudentRepo.studentList); // Earlier,without database, we accessed students through StudentRepo.cs
}
[HttpGet("{id:int}", Name ="GetStudent")]
. .. … .. . . . … . ..
public ActionResult<StudentDTO> GetStudent(int id)
{
if (id == 0)
{ return BadRequest(); } // To get individual student, we can still run LINQ statements on DbSet<> object…
var student = _db.Students.FirstOrDefault(u => u.Id == id);
//var student = StudentRepo.studentList.FirstOrDefault(u => u.Id == id); //..which was earlier….
if (student == null) //….done on a list from StudentRepo.
{ return NotFound(); }
return Ok(student); // This return remains unchanged, since we are still returning a student.
}
[HttpPost]
. .. . ……. ……. .. ..
. . . . .. . … .. …… ……. ..
} // In POST operation, the id need not incremented by 1, since its automatically handled by EF Core….
//studentDTO.Id = StudentRepo.studentList.Order… . …..default().Id + 1; //…through annotation.
Student model = new() //We can only add an object of type Student to the DbSet<Student>, but our controller works with…
{ //….an input of type StudentDTO. So, we need to create a Student object (model) …..
FirstName = studentDTO.FirstName, //….with the data acquired from StudentDTO.
LastName = studentDTO.LastName,
Id = studentDTO.Id,
Gender = studentDTO.Gender,
DateOfBirth = studentDTO.DateOfBirth,
ContactNumber = studentDTO.ContactNumber
}; //(1) The following step adds the Student model to the DbSet<Student> but does not necessarily get…
_db.Students.Add(model); //….executed. We must run SaveChanges to make the database call to persist changes.
_db.SaveChanges(); // Note that Add method is called on DbSet<Student>, just like earlier it was called on studentList.
return CreatedAtRoute("GetStudent",new { id = studentDTO.Id}, studentDTO);
}
}
}
//(1) DbContext.SaveChanges() is one of two techniques for saving changes to the database with EF. With this method, you perform one or more tracked
changes (add, update, delete), and then apply those changes by calling the SaveChanges method. Use the DbSet<TEntity>.Add method to add new
instances of your entity classes. The data will be inserted into the database when you call DbContext.SaveChanges(). EF automatically detects changes made
to an existing entity that is tracked by the context. This includes entities that you load/query from the database, and entities that were previously added
and saved to the database. You can combine multiple Add/Update/Remove operations into a single call to SaveChanges. For most database providers,
SaveChanges is transactional. This means all the operations either succeed or fail and the operations are never left partially applied.

//Lets check the other HTTP methods while using EF Core. Within StudentController.cs (below the Get and Post methods):

[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpDelete("{id:int}", Name = "DeleteStudent")] //(2) An attribute to map HTTP DELETE requests to an action method.
public IActionResult DeleteStudent(int id) //(3)
{
if (id == 0)
{ return BadRequest(); }
var student = _db.Students.FirstOrDefault(u => u.Id == id);
if(student == null)
{ return NotFound(); }
_db.Students.Remove(student);
_db.SaveChanges(); // Must save changes to persist to the actual database.
return NoContent(); //Since we are not going to return anything after deleting, we use IActionResult.
}
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpPut("{id:int}", Name ="UpdateStudent")]
public IActionResult UpdateStudent(int id, [FromBody]StudentDTO studentDTO) //(4)[FromBody] tells…
{ //…ASP.NET Core to look at the body of the incoming HTTP request and deserialize it (usually from JSON) into the StudentDTO object.
if (studentDTO == null || id != studentDTO.Id) //(4) Note: Only one parameter in a method can be bound …
{ return BadRequest(); } //…from the body. That’s because the body can only be read once.
var student = _db.Students.FirstOrDefault(u => u.Id == id);
student.FirstName = studentDTO.FirstName;
student.LastName = studentDTO.LastName;
student.ContactNumber = studentDTO.ContactNumber;
student.Gender = studentDTO.Gender;
student.DateOfBirth = studentDTO.DateOfBirth;
student.Email= studentDTO.Email;
student.SchoolName = studentDTO.SchoolName;
_db.SaveChanges();
return NoContent();
}
//(2) "id": This is a route parameter. It must match a method parameter (here, “int id” of the following method).
//(2) :int: This is a route constraint. It ensures that the route only matches if the value is an integer.
So it maps a DELETE request like: DELETE /api/students/10 ..to this method, and passes 10 into the id parameter.
//(2) Name = "DeleteStudent" : This assigns a name to the route. Named routes can be useful when you want to generate URLs using Url.Link or
CreatedAtRoute. This helps when you want to refer to this specific route elsewhere in your app.
Other parameters you can use in route attributes:
Template: The actual route pattern, like "{id}". Name: As above, for naming the route. Order: Helps prioritize routes when multiple might match.

//(3) ActionResult vs IActionResult : Both are used to represent what type of response a controller method returns, but there is a subtle difference.
IActionResult is an interface. It represents any possible HTTP response. You can return things like Ok(), NotFound(), BadRequest(), etc. However,
ActionResult<T> is a generic class introduced in ASP.NET Core 2.1. It combines the flexibility of IActionResult with the benefits of returning a specific type.
Helps with automatic response formatting and Swagger/OpenAPI documentation.
As a thumb rule, use IActionResult when we want to return different HTTP responses (Ok(), NotFound(), etc.) and don't need type info in Swagger. But , if
we want to return a model and standard responses (and get better docs), use ActionResult.

//(4)ASP.NET Core has different sources from which it can bind data: Route values (like {id}), Query strings, Form data, Headers, Body. To avoid ambiguity,
we need to explicitly specify the source when it’s the request body — because a controller action can have multiple parameters from different places.

//A Note on Dependency Injection:

In general, to define what a “dependency” is, if some class A uses the functionality of a class B, then B is a dependency for A, or, in other words, A has a
dependency on B. Of course, this isn’t limited to classes and holds for functions too. In this case, the class Car has a dependency on the Engine class, or
Engine is a dependency of Car. Dependencies are simply variables, just like most things in programming. At its core, this is all dependency injection is — the
act of injecting (passing) a dependency into another class or function. Anything else involving the notion of dependency injection is simply a variation on this
fundamental and simple concept. Put trivially, dependency injection is a technique whereby an object receives other objects it depends on, called
dependencies, rather than creating them itself using new keyword.

I want to make it very, very clear that what you see above is the core notion of dependency injection. A Car, by itself, is not smart enough to know what
engine it needs. Only the engineers that construct the car understand the requirements for its engines and wheels. Thus, it makes sense that the people
who construct the car provide the specific engine required, rather than letting a Car itself pick whichever engine it wants to use.

I use the word “construct” specifically because you construct the car by calling the constructor, which is the place dependencies are injected. If the car also
created its own tires in addition to the engine, how do we know that the tires being used are safe to be spun at the max RPM the engine can output? For all
these reasons and more, it should make sense, perhaps intuitively, that Car should have nothing to do with deciding what Engine and what Wheels it uses.
They should be provided from some higher level of control.

In the latter example depicting dependency injection in action, if you imagine Engine to be an abstract class rather than a concrete one, this should make
even more sense — the car knows it needs an engine and it knows the engine has to have some basic functionality, but how that engine is managed and
what the specific implementation of it is is reserved for being decided and provided by the piece of code that creates (constructs) the car.

You might also like