Introduction

Using Dapper for CRUD operations in ASP.NET Core is straightforward and efficient. Dapper is a micro ORM that provides simple methods to execute SQL queries and map results to objects. Here's a basic example of how you can perform CRUD operations with Dapper in ASP.NET Core: Demo Code here.

Prerequisites

Creating database table

Launch SQL Server, select Database, and New Database.

CREATE TABLE [dbo].[Products] (
    [ProductId]          UNIQUEIDENTIFIER NOT NULL,
    [ProductName]        NVARCHAR (100)   NULL,
    [Price]              DECIMAL (18)     NULL,
    [ProdcutDescription] NVARCHAR (MAX)   NULL,
    [CreatedOn]          DATETIME         NULL,
    [UpdateOn]           DATETIME         NULL,
    CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ([ProductId] ASC)
);

Creating a New Project in Visual Studio

Create a Web App

Configuring Your New Project

Additional information

  1. In the Additional information dialog.
    • Select .NET 8.0 (Long Term Support).
    • Verify that Do not use top-level statements is unchecked.

Select Create

Add Connection String

The ASP.NET Core Configuration system reads the DefaultConnection key. For local development, it gets the connection string from the appsettings.json file.

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=farhan1086\\SQLEXPRESS;Initial Catalog=Sample-DB;Integrated Security=True;MultipleActiveResultSets=True;Encrypt=False;TrustServerCertificate=False;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Add a data model class

Right-click the Models folder > Add > Class. Name the file ProductModel.cs.

Replace the Models/ProductModel.cs file using c# following code.

The Company class contains an ID field, which is required by the database for the primary key.

ProductModel.cs

using System.ComponentModel.DataAnnotations;

namespace CurdOperationWithDapperNetCoreMVC_Demo.Models
{
    public class ProductModel
    {
        public Guid ProductId { get; set; }

        [Display(Name = "Product Name")]
        public string ProductName { get; set; }
        public decimal Price { get; set; }

        [Display(Name = "Prodcut Description")]
        public string ProdcutDescription { get; set; }

        [Display(Name = "Create Date")]
        public DateTime? CreatedOn { get; set; }

        [Display(Name = "Update Date")]
        public DateTime? UpdateOn { get; set; }
    }
}

Add DBContext Folder and DapperContext Class

using Microsoft.Data.SqlClient;
using System.Data;

namespace CurdOperationWithDapperNetCoreMVC_Demo.Data
{
    public class DapperDbContext
    {
        private readonly IConfiguration _configuration;
        private readonly string _connection;

        public DapperDbContext(IConfiguration configuration)
        {
            _configuration = configuration;
            _connection = configuration.GetConnectionString("DefaultConnection");
        }
        public IDbConnection CreateConnection() => new SqlConnection(_connection);
    }
}

Adding interfaces

using CurdOperationWithDapperNetCoreMVC_Demo.Models;

namespace CurdOperationWithDapperNetCoreMVC_Demo.Repositories
{
    public interface IProduct
    {
        Task<IEnumerable<ProductModel>> Get();
        Task<ProductModel> Find(Guid uid);
        Task<ProductModel> Add(ProductModel model);
        Task<ProductModel> Update(ProductModel model);
        Task<ProductModel> Remove(ProductModel model);
    }
}

Add Company Repository class to Implement Interface

Right-click on the Repositories folder and AddNew Item C# class name this class toProductRepository.

using CurdOperationWithDapperNetCoreMVC_Demo.Data;
using CurdOperationWithDapperNetCoreMVC_Demo.Models;
using Dapper;

namespace CurdOperationWithDapperNetCoreMVC_Demo.Repositories
{
    public class ProductRepository : IProduct
    {
        private readonly DapperDbContext context;

        public ProductRepository(DapperDbContext context)
        {
            this.context = context;
        }
        public async Task<IEnumerable<ProductModel>> Get()
        {
            var sql = $@"SELECT [ProductId],
                               [ProductName],
                               [Price],
                               [ProdcutDescription],
                               [CreatedOn],
                               [UpdateOn]
                            FROM 
                               [Products]";

            using var connection = context.CreateConnection();
            return await connection.QueryAsync<ProductModel>(sql);
        }
        public async Task<ProductModel> Find(Guid uid)
        {
            var sql = $@"SELECT [ProductId],
                               [ProductName],
                               [Price],
                               [ProdcutDescription],
                               [CreatedOn],
                               [UpdateOn]
                            FROM 
                               [Products]
                            WHERE
                              [ProductId]=@uid";

            using var connection = context.CreateConnection();
            return await connection.QueryFirstOrDefaultAsync<ProductModel>(sql, new { uid });
        }
        public async Task<ProductModel> Add(ProductModel model)
        {
            model.ProductId = Guid.NewGuid();
            model.CreatedOn = DateTime.Now;
            var sql = $@"INSERT INTO [dbo].[Products]
                                ([ProductId],
                                 [ProductName],
                                 [Price],
                                 [ProdcutDescription],
                                 [CreatedOn])
                                VALUES
                                (@ProductId,
                                 @ProductName,
                                 @Price,
                                 @ProdcutDescription,
                                 @CreatedOn)";

            using var connection = context.CreateConnection();
            await connection.ExecuteAsync(sql, model);
            return model;
        }
        public async Task<ProductModel> Update(ProductModel model)
        {
            model.UpdateOn = DateTime.Now;
            var sql = $@"UPDATE[dbo].[Products]
                           SET [ProductId] = @ProductId,
                               [ProductName] = @ProductName,
                               [Price] = @Price,
                               [ProdcutDescription] = @ProdcutDescription,
                               [UpdateOn] = @UpdateOn
                          WHERE
                              ProductId=@ProductId";

            using var connection = context.CreateConnection();
            await connection.ExecuteAsync(sql, model);
            return model;
        }
        public async Task<ProductModel> Remove(ProductModel model)
        {
            var sql = $@"
                        DELETE FROM
                            [dbo].[Products]
                        WHERE
                            [ProductId]=@ProductId";
            using var connection = context.CreateConnection();
            await connection.ExecuteAsync(sql, model);
            return model;
        }
    }
}

Add a controller

Replace the contents of Controllers/ProductsController.cs with the following code.

using CurdOperationWithDapperNetCoreMVC_Demo.Models;
using CurdOperationWithDapperNetCoreMVC_Demo.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace CurdOperationWithDapperNetCoreMVC_Demo.Controllers
{
    public class ProductsController : Controller
    {
        private readonly IProduct productRepository;
        public ProductsController(IProduct product)
        {
            this.productRepository = product;
        }
        public async Task<IActionResult> Index()
        {
            var products = await productRepository.Get();
            return View(products);
        }
        public async Task<IActionResult> Details(Guid id)
        {
            if (id == Guid.Empty)
            {
                return NotFound();
            }
            var product = await productRepository.Find(id);

            if (product == null)
            {
                return NotFound();
            }

            return View(product);
        }
        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(ProductModel model)
        {
            if (ModelState.IsValid)
            {
                await productRepository.Add(model);
                return RedirectToAction(nameof(Index));
            }
            return View();
        }
        public async Task<IActionResult> Edit(Guid id)
        {
            if (id == Guid.Empty)
            {
                return NotFound();
            }

            var product = await productRepository.Find(id);

            if (product == null)
            {
                return NotFound();
            }
            return View(product);
        }

        [HttpPost]
        public async Task<IActionResult> Edit(Guid id, ProductModel model)
        {
            if (id == Guid.Empty)
            {
                return NotFound();
            }
            var product = await productRepository.Find(id);

            if (product == null)
            {
                return BadRequest();
            }
            await productRepository.Update(model);
            return RedirectToAction(nameof(Index));

        }
        public async Task<IActionResult> Delete(Guid id)
        {
            if (id == Guid.Empty)
            {
                return NotFound();
            }

            var product = await productRepository.Find(id);

            if (product == null)
            {
                return BadRequest();
            }
            return View(product);
        }

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> ConfirmDelete(Guid id)
        {
            var product = await productRepository.Find(id);
            await productRepository.Remove(product);
            return RedirectToAction(nameof(Index));
        }
    }
}

Add a view

Right-click on the action in CompaniesController, and then Add View.

In the Add NewScaffolded Item dialog.

Replace the contents of theViews/Products/Index.CSS HTMLRazor view file with the following.

@model IEnumerable<CurdOperationWithDapperNetCoreMVC_Demo.Models.ProductModel>

@{
    ViewData["Title"] = "Index";
}

<h4 class="text-capitalize">List of products</h4>

<p>
    <a asp-action="Create" class="btn btn-primary rounded-0"><i class="fa-solid fa-plus"></i> Add New</a>
</p>
<table class="table table-bordered table-striped">
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => model.ProductName)</th>
            <th>@Html.DisplayNameFor(model => model.Price)</th>
            <th>@Html.DisplayNameFor(model => model.ProdcutDescription)</th>
            <th>@Html.DisplayNameFor(model => model.CreatedOn)</th>
            <th>@Html.DisplayNameFor(model => model.UpdateOn)</th>
            <th>Action(s)</th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>@Html.DisplayFor(modelItem => item.ProductName)</td>
            <td>@Html.DisplayFor(modelItem => item.Price)</td>
            <td>@Html.DisplayFor(modelItem => item.ProdcutDescription)</td>
            <td>@Html.DisplayFor(modelItem => item.CreatedOn)</td>
            <td>@Html.DisplayFor(modelItem => item.UpdateOn)</td>
            <td>
                    <a href="@Url.Action("Details", "Products", new { id = item.ProductId })" class="btn btn-sm btn-info"><i class="fa-solid fa-eye"></i></a>
                    <a href="@Url.Action("Edit", "Products", new { id = item.ProductId })" class="btn btn-sm btn-primary"><i class="fa-solid fa-pen-to-square"></i></a>
                    <a href="@Url.Action("Delete", "Products", new { id = item.ProductId })" class="btn btn-sm btn-danger"><i class="fa-solid fa-trash"></i></a>
            </td>
        </tr>
}
    </tbody>
</table>

Add repository services to the Program.cs

The object get method handles for example the task of connecting to the database and mapping Speaker objects to database records.

The database context get method is registered with the Dependency Injection container using the C# method, for example, the Program.cs file.

builder.Services.AddTransient<DapperDbContext, DapperDbContext>();
builder.Services.AddTransient<IProduct,ProductRepository>();

Run the Application

Select Ctrl+F5 to run the app without the debugger. Visual Studio runs the ASP.NET app and opens the default browser.

ASP.NET

Conclusion

The above article has taught us. How we can use Dapper with the repository pattern? Dapper is Micro ORM whereas Entity Framework is ORM. Dapper is faster than entity framework. Hope you enjoyed the article. Happy Coding.