Welcome Everyone,
In this blog, We'll learn how to create a CRUD API using asp.net core web API and Entity framework core. Here we'll connect with a database to perform the Create, Read, Update, and Delete (CRUD) operations.
We've already discussed how to create a Web API using scaffolding. In this blog, We'll use the existing Employee Management project used in the previous blog. There we've already created the Controller using scaffolding. Here we'll connect that to the database and perform CRUD operation on Employee. Check out the blog if you want to know How to create a Web API using Asp.net core step by step.
Tools used to create a web API using ASP.NET Core and Entity Framework Core
- Microsoft Visual Studio 2022 17.8.3
- .NET 8.0 (From .NET 5.0, the term Core is dropped to emphasize the main implementation of .NET. It supports more features than .Net Core.)
- Microsoft SQL Server 2019
Create SQL Server Database
![]() |
1: Add SQL Server Instance |
![]() |
2. Login to SQL Server |
![]() |
3. SQL Server View. List all databases present. |
Configure Entity Framework Core
Introduction
Entity Framework is an object-relational mapping developed by Microsoft for .NET Applications. It eliminates the use of database code used to work with data. It enables us to work with data using objects without worrying about the database code. Using entity framework we can create, maintain, and perform any operation at a higher level with less code than traditional applications.
In simple terms, Entity Framework helps us perform any operation on the database without using the SQL query. Let's see the magic.
Entity Framework Core is the modern cross-platform ORM specifically designed for .net core and latest versions.
Install Entity Framework Core for our project
First of all, we have to install the entity framework core. To install the Entity framework core, go to package manager TOOLS > NuGet Package Manager > Manage NuGet Packages For Solutions.
![]() |
4. Open NuGet Package Manager |
Install the following packages :
- Microsoft.EntityFrameworkCore.Tools
- Microsoft.EntityFrameworkCore.SqlServer - Since we're using SQL server as our database server, we've installed the Microsoft.EntityFrameworkCore.SqlServer Nuget package. If we use different database platform, we must install the respective NuGet Package for that platform.
![]() |
5. Manage Packages for Solution. |
Entity Framework Development Approaches
As we're using Entity Framework as our data access technology, we can use any of the two approaches below:
- Database first approach: In this approach, we create the Tables first inside the Database and after that, we generate the columns using EF Core Scaffold-DbContext Command. It is used where the database schema already exists.
- Code first approach: In this approach, we define model classes in the application, and EF core will update the database using the Add Migration and Update Database command, It'll update the database i.e. create the tables as defined in the models.
Create a Model
Here we'll create a folder named Models. Inside the model, we'll keep the application's business logic, validation logic, and database access code.
We'll create a model class named as EmployeeModel inside the Models folder. Our main focus is to Create, Read, Update, and Delete Employee records. So, we'll add some properties inside our model class.
These fields will be automatically created as columns into our Database table, when we'll add migration using EF Core.
![]() |
6. Employee.cs file |
Why do we use models?
Models are used to represent the data and behavior of an entity in a structured way. Inside the model, we can add validation rules, data mapping annotations, etc.
If we add validation rules inside the model for properties and assign an invalid value to a property, It will not allow us to add that value to that property.
Models encapsulate data (in the form of properties) and behavior (in the form of methods) related to a particular entity. This encapsulation ensures that the internal state of the model remains consistent and that access to that state is controlled through well-defined interfaces.
We can reuse the model in the entire application.
Here, EmpolyeeModel.cs is our model class. Here, the structure of model class will be same as the Database table structure, which we'll use to store the Employees data. In simple terms, We'll use this model to save and retrieve employee records from database with the help of database context class in EF Core.
In our model, we have added an [Key] annotation, which will identify the property Id as Primary Key. When we migrate the changes to the database, initially it will add all the columns with all the constraints that are defined for that column inside our model class. It always checks the difference between the model properties and database table columns. If any changes are found, it will apply them to the respective table.
Create DB Context Class
The model is created, which maps the entities and relationships that are defined in the model to a database table. But to interact with the database we need a DBContext class. Our model class will be associated with the DBContext class. Once we associate the DBContext to a model we can write and execute the query, bind the query results into entity objects, track the changes made to the object, etc.
Here is the recommended way to create a DBContext class.
We've to define a class that derives from DBContext and has to expose the DbSet properties. The DbSet properties will hold the collection of specific entities in a context.
![]() |
7. DbContext File. |
We can see that there is a DbSet<EmployeeModel> property present in our DbContext file. This will represent the table inside our database. If the table is not present it will create the table with the name of the DbSet<T>.
Here in our case, it is Employees. So, it will create a table named Employees inside our database when we apply the EF migrations.
This DbSet<T> is the entry point to Database table data. Through the DbSet we can do all the CRUD operation into our respective tables inside database. If we have multiple tables inside a Database, we have to create multiple DbSet<T> properties, which will represent their respective table.
Adding a Database Connection String
We've to add the Database ConnectionString to our project, which will be used to connect to the database and execute operations such as inserting, updating, deleting, etc. with EF Core.
![]() |
8. Add the db connection string in appSetting.json file |
Register DbContext Class in Service Container
![]() |
9. Register DbContext in program.cs file |
Add Migration to Database
![]() |
10. Prepare DB migration |
![]() |
11. Update database using EF Core |
![]() |
12. Employees Table. |
Implementing Repository Pattern for Data Access in our Web API
What is Repository Patten?
To make our code more readable and maintainable. we'll use the repository pattern here. Repository pattern will create an abstraction layer between the business logic and data access layer. We shouldn't write all of our business logic inside a controller action method. In simple terms, We'll not call the data access layer directly from our Controller. Rather, we should create a separate class which will handle the business logic of the application.
Also, it is recommended that, the controller action method should not be fat. We should strive for skinny controller action method.
There are lots of advantages of Repository pattern. One of them is code resuablity, maintainablity and also Testing will be easier. We'll discuss more about Repository pattern separately. So let's implement the Repository pattern here.
First of all, we'll create a folder named Repository. There we'll add an Interface named IEmployeeService and a class named EmployeeService.
![]() |
13. IEmployeeService.cs file |
![]() |
14. EmployeeService class implements IEmployeeService interface. |
In the IEmployeeService we've added all the method declarations that will be used in our CRUD application. And will define all the methods one by one with demo. Here we've already declared the methods in the interface as we know that we need only four methods for CRUD. If your business logic demands more interface methods, you can declare them during development. It depends on the business logic of the application, and how many methods you need.
In EmployeeService Class, we've registered the EmployeeDBContext. Now, all the DB Operation will happen through EmployeeService not directly through Controller.
Also, for now we have implemented the IEmployeeService interface inside our EmployeeService class. All the methods inside the IEmployeeService interface are added in EmployeeService class. Currently, they are not defined. We'll define all of them one by one with demo.
Also, we've to inject the EmployeeService into the EmployeeController.
Now, the time has came to function those API. So, we'll start with Creating a record into the database through the POST API. As we know, we don't have any records in our database tables. So, first of all we need to create them. That's why we'll go with creating the record first.
First of all we need to inject the IEmployeeService interface to EmployeeController constructor using dependency injection. Here EmployeeController will not directly depend on EmployeeService. That's why we use dependency injection to create loosely coupled components.
Register IEmployeeService
interface to EmployeeService
in service container.
Register the IEmployeeService in the service container in the program.cs file. This will resolve the service in the runtime and controller can use it for every request. Here, we've Added it's lifetime as Transient, it will create the service each time it will be requested form controller.
![]() |
15. Registering the EmployeeService class in Program.cs file. |
Now, inject the IEmployeeService interface inside the EmployeeController using constructor injection.
![]() |
16. Dependency Injection (DI) in EmployeesController. |
Integration of Controller and Service class for individual HTTP Methods
POST Method
The service method AddEmployee(EmployeeModel employee) present in EmployeeService is responsible for creating an employee record.
The action method having HttpPost as HttpVerb in EmployeesController will call the above method to create an Employee record.
So, let's first define the AddEmployee(EmployeeModel employee) in EmployeeSevice class and call this method from POST action method from EmployeesController. Also, there will be slight changes in our action method definition as well.
Let's implement them.
public async Task<employeemodel> AddEmployee(EmployeeModel employee)
{
var result = await employeeDBContext.Employees.AddAsync(employee);
await employeeDBContext.SaveChangesAsync();
return result.Entity;
}
Here is the definition for the POST action method inside EmployeesController
[HttpPost]
public async Task<IActionResult> Post([FromBody] EmployeeModel employee)
{
var result = await _employeeService.AddEmployee(employee);
return CreatedAtAction(nameof(Post), result);
}
Now. let's run the project. Open the swagger UI.
We can see the list of API's for Employee. But for now, we've only implemented the POST /api/Employees.
So, let's test the endpoint by creating an employee record.
![]() |
17. Create Employee record. |
Here we've passed the employee details that are needed to create an employee. After clicking on the Execute button, it gives us the following response.
![]() |
18. POST /api/Employees response. |
You can see the response status is 201 with the Employee response. That means the employee is created in DB. Hurray! We've created our first employee record.
Now, let's verify that the Employee record is actually created in our Employees table in EmployeeDB.
Expand EmployeeDB and then select the Employees table in SQL Server Object Explorer. Then right-click and select View Data. It will list out all the data present in Employees table.
![]() |
19. Employee record added to in Employees Table. |
GET Method
Endpoint - GET api/employee/{id}
Now, the time has come to get the employee details we've just created. When we'll pass the employee id, it should return the employee details matching with the employee ID.
To get an employee details, the action method having route as HttpGet("{id}") will be responsible in the EmployeesController.
The service method GetEmployeeById having int id as method parameter is responsible for getting the data from DB and return it to controller.
Let's implement both the methods.
Here is the service method GetEmployeeById implementation.
public async Task<EmployeeModel> GetEmployeeById(int id)
{
return await employeeDBContext.Employees.FindAsync(id);
}
Controller Action method having route HttpGet("id") will now look like this
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var result = await _employeeService.GetEmployeeById(id);
if(result == null)
return NotFound();
return Ok(result);
}
Let's again run the project. Go to Swagger UI and pass the id of the employee that is recently created. In our case the employee id is 1.
![]() |
20. GET /api/Employees/1 response. |
We can see, it is returning the exact information for that employee id = 1, that we've recently created.
It will return 404 Not Found, if we pass an id that doesn't exists in the database. We only have one record in our database, so if we pass an non existing id, then it will return 404 StatusCode with response message Not Found.
![]() |
21. Not Found, when emploee record doesn't exists. |
PUT Method
Endpoint - PUT api/employee/{id}
The action method having route HttpPut("{id}") will be used for updating an employee record.
The Service method UpdateEmployee(int id, EmployeeModel employee) will be responsible for updating an employee record. Here is the implementation for both the methods.
public async Task<EmployeeModel> UpdateEmployee(int id, EmployeeModel employee)
{
var employeeBId = await employeeDBContext.Employees.FindAsync(id);
if (employeeBId == null)
return null;
employeeDBContext.Entry(employeeBId).CurrentValues.SetValues(employee);
await employeeDBContext.SaveChangesAsync();
return employee;
}
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, [FromBody] EmployeeModel employee)
{
if(id != employee.Id)
return BadRequest();
var result = await _employeeService.UpdateEmployee(id, employee);
if (result == null)
return NotFound(result);
return Ok(result);
}
Now let's run the project. We'll try to update the employee, that we've created. Let's see what happens. Below image, we've passed updated request by changing the Department from IT to HR.
![]() |
22. Update Employee record. |
![]() |
23. Verify updated Employee. |
Hurray! That Employee record is updated.
GET Method (Get all Employee records)
Now, we'll see how to retrieve all the employees that are present in our database. Controller method having route as [HttpGet] without any route parameter will be called to get all the records.
Service method GetAllEmployees will retrieve all the employees and will return its to the Controller action method.
Here are both service and action method definitions for the above.
public async Task<IEnumerable<EmployeeModel>> GetAllEmployees()
{
return await employeeDBContext.Employees.ToListAsync();
}
[HttpGet]
public async Task<IActionResult> Get()
{
var results = await _employeeService.GetAllEmployees();
return Ok(results);
}
We've added some employees to our Employees table in the EmployeeDB database to verify that it is returning the list of all employees. Here is the list of employees that are added.
![]() |
24. Manually added some Employee records. |
Now, let's run the project. Execute the endpoint in swagger
![]() |
25. GET /api/Employees response. Get all Employees records. |
It is returning the list of all employees. Hurray! Our API is working fine.
DELETE Method
Endpoint - DELETE api/Employee/{id}
Now, only deleting an employee record is left, The action method that has the route as [HttpDelete("{id}")] will be used to delete an employee record. So, let's implement that.
public async Task<EmployeeModel> DeleteEmployee(int id)
{
var employeeDetails = await GetEmployeeById(id);
if (employeeDetails == null)
return null;
employeeDBContext.Employees.Remove(employeeDetails);
await employeeDBContext.SaveChangesAsync();
return employeeDetails;
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var result = await _employeeService.DeleteEmployee(id);
if (result == null)
return NotFound();
return Ok();
}
It's time to execute. Run the project and project and pass an id to DELETE api/Employee/{id}. We've pass id as 3.
![]() |
26. DELETE /api/Employee/3 response. |
We can see that the response status code is 200. Which indicates that the employee record having id as 3 has been deleted.
Let's verify it is actually deleted. Execute the GET api/Employee. It'll return all the employees present in the database. Here is that.
![]() |
27. List all employees after delete. |
It is verified that the employee record with id = 3 is not present, Which means it is deleted.
Conclusion
Creating a web API using ASP.NET Core and Entity Framework Core offers a powerful and efficient way of building robust and scalable applications. Throughout this process, we have explored the fundamentals of ASP.NET Core and EF Core, including Repository pattern and integration with SQL Server. Now, using this approach, we as a developer, can design any type of Web APIs with the above tools. We can do much more with the ASP.NET Core Web API. Just stay tuned.
Share your thoughts and feedback regarding this article in comments.
Comments
Post a Comment