Introduction
In many applications, the API layer needs to communicate with a relational database to create, retrieve, update, and delete data.
In this article, we will build a simple ASP.NET Core Web API that performs CRUD operations on a Department table using Dapper and SQL Server Stored Procedures.
The example uses a simple layered structure:
Controller
↓
Service
↓
Repository
↓
Dapper
↓
SQL Server
The application will provide APIs to:
Get all departments.
Get a department by ID.
Create a department.
Update a department.
Soft-delete a department.
The goal is to demonstrate how Dapper can be used with stored procedures in an ASP.NET Core Web API.
Technologies Used
The example uses:
ASP.NET Core Web API
C#
Dapper
SQL Server
SQL Server Stored Procedures
Repository Pattern
Service Layer
Dependency Injection
Database Setup
First, create a database in SQL Server and create the Department table.
Create the Department Table
CREATE TABLE Department (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100),
Location VARCHAR(100),
IsActive BIT DEFAULT 1
);
The table contains four columns:
Column | Type | Description |
|---|---|---|
ID | INT | Primary key generated automatically |
Name | VARCHAR(100) | Department name |
Location | VARCHAR(100) | Department location |
IsActive | BIT | Indicates whether the department is active |
The IsActive column will be used for a soft delete instead of physically removing the record.
Create Stored Procedures
The application will use stored procedures for database operations.
1. GetDepartments
The following stored procedure retrieves all departments or a specific department when an ID is supplied.
CREATE PROCEDURE [dbo].[GetDepartments]
@ID INT = NULL
AS
BEGIN
SELECT
ID,
Name,
Location,
IsActive
FROM Department
WHERE (@ID IS NULL OR ID = @ID);
END
When @ID is NULL, all departments are returned.
When an ID is provided, only the matching department is returned.
2. Create or Update Department
The usp_CreateUpdateDepartment procedure handles both insert and update operations.
CREATE PROCEDURE [dbo].[usp_CreateUpdateDepartment]
@id INT = NULL,
@name VARCHAR(100) = NULL,
@location VARCHAR(100) = NULL,
@isactive BIT = NULL
AS
BEGIN
IF EXISTS (SELECT 1 FROM Department WHERE ID = @id)
BEGIN
UPDATE Department
SET
Location = @location,
Name = @name,
IsActive = @isactive
WHERE ID = @id;
SELECT @id;
END
ELSE
BEGIN
INSERT INTO Department (Name, Location, IsActive)
VALUES (@name, @location, 1);
SELECT CAST(SCOPE_IDENTITY() AS INT);
END
END
SCOPE_IDENTITY() is used instead of @@IDENTITY because it returns the identity value generated in the current scope.
3. Delete Department
The delete operation performs a soft delete by changing IsActive to 0.
CREATE PROCEDURE [dbo].[usp_deleteDepartment]
@id INT
AS
BEGIN
UPDATE Department
SET IsActive = 0
WHERE ID = @id;
SELECT @id;
END
The record remains in the database, but it is marked as inactive.
Create the ASP.NET Core Web API Project
Create a new ASP.NET Core Web API project using Visual Studio or the .NET CLI.
After creating the project, install the following NuGet packages:
Dapper
Microsoft.Data.SqlClient
Dapper provides lightweight object mapping and database access functionality.
Microsoft.Data.SqlClient provides SQL Server connectivity.
Create the Department Model
Create a class named Department.cs.
public class Department
{
public int ID { get; set; }
public string? Name { get; set; }
public string? Location { get; set; }
public bool IsActive { get; set; }
}
This class represents the Department table and is also used by Dapper to map query results.
Create the API Response Class
Create a class named APIResponse.cs to provide a consistent response structure.
using System.Net;
public class APIResponse<T>
{
public HttpStatusCode StatusCode { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public string ErrorMsg { get; set; }
public APIResponse(
HttpStatusCode statusCode,
string message,
T data)
{
StatusCode = statusCode;
Message = message;
Data = data;
ErrorMsg = null;
}
public APIResponse(
HttpStatusCode statusCode,
string message,
string error)
{
StatusCode = statusCode;
Message = message;
Data = default;
ErrorMsg = error;
}
public static APIResponse<T> Success(
HttpStatusCode statusCode,
string message,
T data)
{
return new APIResponse<T>(
statusCode,
message,
data);
}
public static APIResponse<T> Errors(
HttpStatusCode statusCode,
string message,
string error)
{
return new APIResponse<T>(
statusCode,
message,
error);
}
}
This allows the API to return information such as status, message, data, and error details in a common structure.
Create the Repository Interface
Create IDepartmentRepository.cs.
public interface IDepartmentRepository
{
List<Department> GetAll();
Department GetByID(int id);
int Create(Department department);
int Update(Department department);
int Delete(int id);
}
The repository interface defines the database operations required by the application.
Create the Service Interface
Create IDepartmentService.cs.
public interface IDepartmentService
{
List<Department> GetAll();
Department GetByID(int id);
int Create(Department department);
int Update(Department department);
int Delete(int id);
}
The service interface defines the operations exposed by the service layer.
Create DepartmentService.cs
The service layer communicates with the repository.
public class DepartmentService : IDepartmentService
{
private readonly IDepartmentRepository _departmentRepository;
public DepartmentService(
IDepartmentRepository departmentRepository)
{
_departmentRepository = departmentRepository;
}
public int Create(Department department)
{
return _departmentRepository.Create(department);
}
public int Delete(int id)
{
return _departmentRepository.Delete(id);
}
public List<Department> GetAll()
{
return _departmentRepository.GetAll();
}
public Department GetByID(int id)
{
return _departmentRepository.GetByID(id);
}
public int Update(Department department)
{
return _departmentRepository.Update(department);
}
}
The service layer currently delegates the operations directly to the repository. In a larger application, this layer can also contain business rules and validation.
Create DepartmentRepository.cs
The repository is responsible for communicating with SQL Server using Dapper.
using Dapper;
using Microsoft.Data.SqlClient;
using System.Data;
public class DepartmentRepository : IDepartmentRepository
{
private readonly string _connectionString;
public DepartmentRepository(IConfiguration configuration)
{
_connectionString =
configuration.GetConnectionString("DbConnection");
}
public List<Department> GetAll()
{
using var connection =
new SqlConnection(_connectionString);
return connection
.Query<Department>(
"GetDepartments",
commandType: CommandType.StoredProcedure)
.ToList();
}
public Department GetByID(int id)
{
using var connection =
new SqlConnection(_connectionString);
var parameters = new DynamicParameters();
parameters.Add("ID", id);
return connection
.Query<Department>(
"GetDepartments",
parameters,
commandType: CommandType.StoredProcedure)
.FirstOrDefault();
}
public int Create(Department department)
{
using var connection =
new SqlConnection(_connectionString);
var parameters = new DynamicParameters();
parameters.Add("name", department.Name);
parameters.Add("location", department.Location);
return connection
.Query<int>(
"usp_CreateUpdateDepartment",
parameters,
commandType: CommandType.StoredProcedure)
.FirstOrDefault();
}
public int Update(Department department)
{
using var connection =
new SqlConnection(_connectionString);
var parameters = new DynamicParameters();
parameters.Add("id", department.ID);
parameters.Add("name", department.Name);
parameters.Add("location", department.Location);
parameters.Add("isactive", department.IsActive);
return connection
.Query<int>(
"usp_CreateUpdateDepartment",
parameters,
commandType: CommandType.StoredProcedure)
.FirstOrDefault();
}
public int Delete(int id)
{
using var connection =
new SqlConnection(_connectionString);
var parameters = new DynamicParameters();
parameters.Add("id", id);
return connection
.Query<int>(
"usp_deleteDepartment",
parameters,
commandType: CommandType.StoredProcedure)
.FirstOrDefault();
}
}
Join the conversation! Your thoughts help the community grow.