Dapper is a micro ORM, while Entity Framework is a full ORM. Dapper was created by the team behind Stack Overflow. Documentation

Is Dapper an ORM?

  • Dapper is a micro-ORM, offering only a subset of the functionality of full ORMs like Entity Framework Core.
  • Known for speed and simple implementation.

When Should You Use Dapper?

  • Primary reason: Performance.
  • Ideal for frequent, read-only data that changes often.
  • Works well in stateless scenarios (e.g., web apps).
  • Requires SQL queries (does not auto-translate .NET code to SQL).
  • Best for non-normalized databases with minimal schema expectations.
  • Works with any database supporting an ADO.NET provider.
  • Dapper is a lightweight, performant, and easy-to-use choice for efficient data access.
  • Compatible with .NET Framework and .NET Core.


Implementation of dapper in ASP.NET

1. First create two tables:

CREATE TABLE Companies (
    Id INT PRIMARY KEY,
    Name NVARCHAR(100),
    Address NVARCHAR(255),
    Country NVARCHAR(100)
);
 
CREATE TABLE Employees (
    Id INT PRIMARY KEY,
    Name NVARCHAR(100),
    Age INT,
    Position NVARCHAR(100),
    CompanyId INT,
    FOREIGN KEY (CompanyId) REFERENCES Companies(Id)
);

2. Insert some values:

DECLARE @i INT = 1;
DECLARE @companyId INT;
DECLARE @companyCount INT = 10;  -- Number of companies
DECLARE @employeeCount INT = 100;  -- Number of employees
 
-- Insert 10 companies first
WHILE @i <= @companyCount
BEGIN
    INSERT INTO Companies (Id, Name, Address, Country)
    VALUES 
    (@i, 
     'Company ' + CAST(@i AS NVARCHAR(10)), 
     'Address ' + CAST(@i AS NVARCHAR(10)), 
     'Country ' + CAST(@i AS NVARCHAR(10)));
 
    SET @i = @i + 1;
END
 
-- Insert 100 employees with random CompanyId or NULL
SET @i = 1;  -- Reset counter for employees
 
WHILE @i <= @employeeCount
BEGIN
    -- Randomly decide if employee will have a company or not
    IF (RAND() * 2) >= 1  -- 50% chance of assigning a company
    BEGIN
        -- Assign a random CompanyId (choose from the 10 inserted companies)
        SET @companyId = (SELECT TOP 1 Id FROM Companies ORDER BY NEWID());
        
        INSERT INTO Employees (Id, Name, Age, Position, CompanyId)
        VALUES 
        (@i, 
         'Employee ' + CAST(@i AS NVARCHAR(10)), 
         FLOOR(RAND() * (60 - 20 + 1)) + 20,  -- Random age between 20 and 60
         'Position ' + CAST(@i AS NVARCHAR(10)),
         @companyId);
    END
    ELSE
    BEGIN
        -- Employee has no company (CompanyId = NULL)
        INSERT INTO Employees (Id, Name, Age, Position, CompanyId)
        VALUES 
        (@i, 
         'Employee ' + CAST(@i AS NVARCHAR(10)), 
         FLOOR(RAND() * (60 - 20 + 1)) + 20,  -- Random age between 20 and 60
         'Position ' + CAST(@i AS NVARCHAR(10)),
         NULL);  -- No company
    END
 
    SET @i = @i + 1;
END

3. Check values:

SELECT * FROM Employees;
SELECT * FROM Companies;
 
SELECT 
    c.Id AS CompanyId,
    c.Name AS CompanyName,
    c.Address AS CompanyAddress,
    c.Country AS CompanyCountry,
    e.Id AS EmployeeId,
    e.Name AS EmployeeName,
    e.Age AS EmployeeAge,
    e.Position AS EmployeePosition
FROM 
    Companies c
LEFT JOIN 
    Employees e ON c.Id = e.CompanyId
ORDER BY 
    c.Id, e.Id;

4. Now Create an ASP.NET Core API project and install NuGet:

<ItemGroup>
  <PackageReference Include="Dapper" Version="2.1.66" />
  <PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.1" />
  <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1" />
  <PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
</ItemGroup>

5. Add connection string into appsettings.json:

"ConnectionStrings": {
  "DefaultConnection": "Server=prod.asdbd.com;Database=SADB;User ID=asdinc;Password=ASD007;TrustServerCertificate=True;"
},

6. Add lines in Program.cs:

builder.Services.AddSingleton<DbContext>();
builder.Services.AddScoped<ICompanyRepository, CompanyRepository>();
builder.Services.AddScoped<IEmployeeRepository, EmployeeRepository>();
 
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();
 
app.UseSwagger();
app.UseSwaggerUI();

7. Add a DbContext.cs:

using Microsoft.Data.SqlClient;
using System.Data;
 
namespace WebApplicationB1
{
    public class DbContext
    {
        private readonly IConfiguration _configuration;
        private readonly string? _connectionString;
 
        public DbContext(IConfiguration configuration)
        {
            _configuration = configuration;
            _connectionString = _configuration.GetConnectionString("DefaultConnection");
        }
 
        public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
    }
}

8. Add two entities:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Position { get; set; }
    public int CompanyId { get; set; }
}
 
public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Country { get; set; }
    public List<Employee> Employees { get; set; } = new List<Employee>();
}

9. Add repository interface:

public interface IEmployeeRepository
{
    public Task<IEnumerable<Employee>> GetEmployees();
    public Task<Employee> GetById(int id);
    public Task<bool> Add(Employee company);
    public Task<bool> Edit(Employee company);
    public Task<bool> Delete(int id);
}
 
public interface ICompanyRepository
{
    public Task<IEnumerable<Company>> GetCompanies();
    public Task<Company> GetById(int id);
    public Task<bool> Add(Company company);
    public Task<bool> Edit(Company company);
    public Task<bool> Delete(int id);
}

10. Implement repository:

EmployeeRepository:

using Dapper;
using WebApplicationB1.Entities;
 
namespace WebApplicationB1.Repositories
{
    public class EmployeeRepository : IEmployeeRepository
    {
        private readonly DbContext _context;
 
        public EmployeeRepository(DbContext context)
        {
            _context = context;
        }
 
        // Get Employees
        public async Task<IEnumerable<Employee>> GetEmployees()
        {
            var query = "SELECT * FROM Employees";
 
            using (var connection = _context.CreateConnection())
            {
                var employees = await connection.QueryAsync<Employee>(query);
                return employees.ToList();
            }
        }
 
        // Add a new Employee
        public async Task<bool> Add(Employee employee)
        {
            var query = "INSERT INTO Employees (Id, Name, Age, Position, CompanyId) VALUES (@Id, @Name, @Age, @Position, @CompanyId)";
 
            using (var connection = _context.CreateConnection())
            {
                var result = await connection.ExecuteAsync(query, new
                {
                    employee.Id,
                    employee.Name,
                    employee.Age,
                    employee.Position,
                    employee.CompanyId
                });
 
                return result > 0; // Returns true if the insert was successful
            }
        }
 
        // Delete an Employee by Id
        public async Task<bool> Delete(int id)
        {
            var query = "DELETE FROM Employees WHERE Id = @Id";
 
            using (var connection = _context.CreateConnection())
            {
                var result = await connection.ExecuteAsync(query, new { Id = id });
                return result > 0;
            }
        }
 
        // Edit an existing Employee
        public async Task<bool> Edit(Employee employee)
        {
            var query = "UPDATE Employees SET Name = @Name, Age = @Age, Position = @Position, CompanyId = @CompanyId WHERE Id = @Id";
 
            using (var connection = _context.CreateConnection())
            {
                var result = await connection.ExecuteAsync(query, new
                {
                    employee.Name,
                    employee.Age,
                    employee.Position,
                    employee.CompanyId,
                    employee.Id
                });
 
                return result > 0;
            }
        }
 
        // Get an Employee by Id
        public async Task<Employee> GetById(int id)
        {
            var query = "SELECT * FROM Employees WHERE Id = @Id";
 
            using (var connection = _context.CreateConnection())
            {
                var employee = await connection.QuerySingleOrDefaultAsync<Employee>(query, new { Id = id });
                return employee; // Returns null if not found
            }
        }
    }
}

CompanyRepository:

using Dapper;
using WebApplicationB1.Entities;
 
namespace WebApplicationB1.Repositories
{
    public class CompanyRepository : ICompanyRepository
    {
        private readonly DbContext _context;
 
        public CompanyRepository(DbContext context)
        {
            _context = context;
        }
 
        // Get all Companies
        public async Task<IEnumerable<Company>> GetCompanies()
        {
            var query = "SELECT * FROM Companies";
 
            using (var connection = _context.CreateConnection())
            {
                var companies = await connection.QueryAsync<Company>(query);
                return companies.ToList();
            }
        }
 
        // Get Company by Id (including its Employees)
        public async Task<Company> GetById(int id)
        {
            var query = @"SELECT * FROM Companies WHERE Id = @Id;
                          SELECT * FROM Employees WHERE CompanyId = @Id;";
 
            using (var connection = _context.CreateConnection())
            {
                using (var multi = await connection.QueryMultipleAsync(query, new { Id = id }))
                {
                    var company = await multi.ReadSingleOrDefaultAsync<Company>();
                    if (company != null)
                    {
                        company.Employees = (await multi.ReadAsync<Employee>()).ToList();
                    }
                    return company;
                }
            }
        }
 
        // Add a new Company
        public async Task<bool> Add(Company company)
        {
            var query = "INSERT INTO Companies (Id, Name, Address, Country) VALUES (@Id, @Name, @Address, @Country)";
 
            using (var connection = _context.CreateConnection())
            {
                var result = await connection.ExecuteAsync(query, new
                {
                    company.Id,
                    company.Name,
                    company.Address,
                    company.Country
                });
 
                // Assuming employees are added separately after the company is inserted
                if (result > 0 && company.Employees.Any())
                {
                    // Insert employees if any exist
                    foreach (var employee in company.Employees)
                    {
                        var employeeQuery = "INSERT INTO Employees (Id, Name, Age, Position, CompanyId) VALUES (@Id, @Name, @Age, @Position, @CompanyId)";
                        await connection.ExecuteAsync(employeeQuery, new
                        {
                            employee.Name,
                            employee.Age,
                            employee.Position,
                            CompanyId = company.Id
                        });
                    }
                }
 
                return result > 0;
            }
        }
 
        // Delete a Company by Id
        public async Task<bool> Delete(int id)
        {
            // Deleting employees associated with the company
            var deleteEmployeesQuery = "DELETE FROM Employees WHERE CompanyId = @Id";
            var deleteCompanyQuery = "DELETE FROM Companies WHERE Id = @Id";
 
            using (var connection = _context.CreateConnection())
            {
                // Start by deleting employees
                var employeesDeleted = await connection.ExecuteAsync(deleteEmployeesQuery, new { Id = id });
                var companyDeleted = await connection.ExecuteAsync(deleteCompanyQuery, new { Id = id });
 
                return companyDeleted > 0;
            }
        }
 
        // Edit an existing Company
        public async Task<bool> Edit(Company company)
        {
            var query = "UPDATE Companies SET Name = @Name, Address = @Address, Country = @Country WHERE Id = @Id";
 
            using (var connection = _context.CreateConnection())
            {
                var result = await connection.ExecuteAsync(query, new
                {
                    company.Name,
                    company.Address,
                    company.Country,
                    company.Id
                });
 
                return result > 0;
            }
        }
    }
}

11. Add Controller:

EmployeesController:

using Microsoft.AspNetCore.Mvc;
using WebApplicationB1.Entities;
using WebApplicationB1.Repositories;
 
namespace WebApplicationB1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeesController : ControllerBase
    {
        private readonly IEmployeeRepository _employeeRepo;
        public EmployeesController(IEmployeeRepository employeeRepo)
        {
            _employeeRepo = employeeRepo;
        }
        [HttpGet]
        public async Task<IActionResult> GetEmployees()
        {
            try
            {
                var employees = await _employeeRepo.GetEmployees();
                return Ok(employees);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpGet("{id}")]
        public async Task<IActionResult> GetById(int id)
        {
            try
            {
                return Ok(await _employeeRepo.GetById(id));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpPost]
        public async Task<IActionResult> Add([FromBody] Employee employee)
        {
            try
            {
                return Ok(await _employeeRepo.Add(employee));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpPut]
        public async Task<IActionResult> Edit([FromBody] Employee employee)
        {
            try
            {
                return Ok(await _employeeRepo.Edit(employee));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpDelete]
        public async Task<IActionResult> Delete(int id)
        {
            try
            {
                return Ok(await _employeeRepo.Delete(id));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
    }
}

CompaniesController:

using Microsoft.AspNetCore.Mvc;
using WebApplicationB1.Entities;
using WebApplicationB1.Repositories;
 
namespace WebApplicationB1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CompaniesController : ControllerBase
    {
        private readonly ICompanyRepository _companyRepo;
        public CompaniesController(ICompanyRepository companyRepo)
        {
            _companyRepo = companyRepo;
        }
        [HttpGet]
        public async Task<IActionResult> GetCompanies()
        {
            try
            {
                var companies = await _companyRepo.GetCompanies();
                return Ok(companies);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpGet("{id}")]
        public async Task<IActionResult> GetById(int id)
        {
            try
            {
                return Ok(await _companyRepo.GetById(id));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpPost]
        public async Task<IActionResult> Add([FromBody] Company company)
        {
            try
            {
                return Ok(await _companyRepo.Add(company));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpPut]
        public async Task<IActionResult> Edit([FromBody] Company company)
        {
            try
            {
                return Ok(await _companyRepo.Edit(company));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
 
        [HttpDelete]
        public async Task<IActionResult> Delete(int id)
        {
            try
            {
                return Ok(await _companyRepo.Delete(id));
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
    }
}

Finally, when you run the project, it will display like this: