Tuesday, September 22, 2026

Dapper vs Entity Framework Core in .NET – Complete End-to-End Guide


Introduction

When developing modern ASP.NET Core Web API applications, one of the most important decisions is how the application communicates with the database.

Two popular approaches in the .NET ecosystem are:

  • Entity Framework Core (EF Core)

  • Dapper

Both are powerful, but they solve the database-access problem differently.

EF Core is a full-featured Object-Relational Mapper (ORM), while Dapper is a lightweight micro-ORM that provides a thin layer over ADO.NET.

A common interview question is:

"What is the difference between Dapper and Entity Framework Core, and when would you use each?"

This article explains the differences from beginner to production level.


1. What is Entity Framework Core?

Entity Framework Core (EF Core) is Microsoft's modern, cross-platform ORM for .NET.

ORM stands for:

Object Relational Mapping

It allows developers to work with database tables using C# classes and LINQ instead of writing SQL for every operation.

For example, suppose we have a database table:

CREATE TABLE Employees
(
    Id INT PRIMARY KEY IDENTITY,
    Name NVARCHAR(100),
    Department NVARCHAR(100),
    Salary DECIMAL(18,2)
);

In EF Core, we can create:

public class Employee
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Department { get; set; }

    public decimal Salary { get; set; }
}

Then query the database using LINQ:

var employees = await dbContext.Employees
    .Where(x => x.Department == "IT")
    .ToListAsync();

EF Core generates the appropriate SQL behind the scenes.


2. What is Dapper?

Dapper is a lightweight micro-ORM for .NET.

It was originally created by the Stack Overflow team and is widely used for high-performance database access.

Unlike EF Core, Dapper does not attempt to provide a complete ORM framework.

You normally write the SQL yourself:

var sql = """
          SELECT Id, Name, Department, Salary
          FROM Employees
          WHERE Department = @Department
          """;

var employees = await connection.QueryAsync<Employee>(
    sql,
    new { Department = "IT" });

Dapper executes the SQL and maps the returned rows to the Employee C# class.


3. Dapper vs EF Core – High-Level Difference

FeatureDapperEF Core
TypeMicro ORMFull ORM
SQLDeveloper writes SQLEF can generate SQL
LINQLimited/direct SQL approachExtensive LINQ support
Change TrackingNo built-in change trackerYes
MigrationsNoYes
RelationshipsManual SQL/mappingBuilt-in relationship mapping
Lazy LoadingNo built-in ORM-style lazy loadingSupported with configuration
PerformanceGenerally very fastGenerally good, but more abstraction
Control over SQLVery highMedium/High
Learning CurveRelatively lowHigher
CRUDSQL-basedObject-based/LINQ
TransactionsADO.NET transactionsDbContext transactions
Complex queriesExcellent when SQL is knownExcellent with LINQ, but SQL knowledge still useful
Stored ProceduresExcellentSupported
Change TrackingNoYes
Database-firstManualSupported
Code-firstNo EF-style migrationsSupported
Best suited forSQL-centric/high-performance applicationsDomain/application-centric applications

4. Architecture Difference

The biggest conceptual difference is the abstraction level.

Dapper

Application
     |
     v
Repository
     |
     v
Dapper
     |
     v
ADO.NET
     |
     v
SQL Server

You control the SQL.


EF Core

Application
     |
     v
Repository / Service
     |
     v
DbContext
     |
     v
EF Core
     |
     v
LINQ
     |
     v
SQL Generation
     |
     v
SQL Server

EF Core provides more abstraction.


5. Basic EF Core Setup

Suppose we are developing:

ASP.NET Core Web API
        |
        v
EmployeeService
        |
        v
EmployeeRepository
        |
        v
EF Core
        |
        v
Azure SQL / SQL Server

Install the SQL Server provider:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

For migrations:

dotnet add package Microsoft.EntityFrameworkCore.Tools

6. Create DbContext

public class EmployeeDbContext : DbContext
{
    public EmployeeDbContext(
        DbContextOptions<EmployeeDbContext> options)
        : base(options)
    {
    }

    public DbSet<Employee> Employees { get; set; }
}

7. Configure Connection String

{
  "ConnectionStrings": {
    "DefaultConnection":
      "Server=localhost;Database=EmployeeDb;Trusted_Connection=True;TrustServerCertificate=True"
  }
}

Register DbContext:

builder.Services.AddDbContext<EmployeeDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")));

8. EF Core CRUD Operations

Create

var employee = new Employee
{
    Name = "John",
    Department = "IT",
    Salary = 75000
};

dbContext.Employees.Add(employee);

await dbContext.SaveChangesAsync();

EF Core generates an INSERT statement.


9. Read

var employee = await dbContext.Employees
    .FirstOrDefaultAsync(x => x.Id == 10);

10. Read Multiple Records

var employees = await dbContext.Employees
    .Where(x => x.Department == "IT")
    .ToListAsync();

11. Update

var employee = await dbContext.Employees
    .FirstOrDefaultAsync(x => x.Id == 10);

if (employee != null)
{
    employee.Salary = 85000;

    await dbContext.SaveChangesAsync();
}

EF Core detects the modified property using change tracking.


12. Delete

var employee = await dbContext.Employees
    .FindAsync(10);

if (employee != null)
{
    dbContext.Employees.Remove(employee);

    await dbContext.SaveChangesAsync();
}

13. Dapper Setup

Install Dapper:

dotnet add package Dapper

For SQL Server:

dotnet add package Microsoft.Data.SqlClient

14. Dapper Connection

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

public class EmployeeRepository
{
    private readonly string _connectionString;

    public EmployeeRepository(IConfiguration configuration)
    {
        _connectionString =
            configuration.GetConnectionString("DefaultConnection")!;
    }

    private IDbConnection CreateConnection()
    {
        return new SqlConnection(_connectionString);
    }
}

15. Dapper SELECT

public async Task<IEnumerable<Employee>> GetEmployeesAsync()
{
    using var connection = CreateConnection();

    const string sql = """
        SELECT Id, Name, Department, Salary
        FROM Employees
        """;

    return await connection.QueryAsync<Employee>(sql);
}

16. Dapper SELECT By ID

public async Task<Employee?> GetEmployeeAsync(int id)
{
    using var connection = CreateConnection();

    const string sql = """
        SELECT Id, Name, Department, Salary
        FROM Employees
        WHERE Id = @Id
        """;

    return await connection.QuerySingleOrDefaultAsync<Employee>(
        sql,
        new { Id = id });
}

Notice:

WHERE Id = @Id

and:

new { Id = id }

Dapper maps the parameter safely.


17. Dapper INSERT

public async Task<int> CreateEmployeeAsync(Employee employee)
{
    using var connection = CreateConnection();

    const string sql = """
        INSERT INTO Employees
        (
            Name,
            Department,
            Salary
        )
        VALUES
        (
            @Name,
            @Department,
            @Salary
        );

        SELECT CAST(SCOPE_IDENTITY() AS INT);
        """;

    return await connection.ExecuteScalarAsync<int>(
        sql,
        employee);
}

18. Dapper UPDATE

public async Task<int> UpdateEmployeeAsync(Employee employee)
{
    using var connection = CreateConnection();

    const string sql = """
        UPDATE Employees
        SET
            Name = @Name,
            Department = @Department,
            Salary = @Salary
        WHERE Id = @Id
        """;

    return await connection.ExecuteAsync(sql, employee);
}

19. Dapper DELETE

public async Task<int> DeleteEmployeeAsync(int id)
{
    using var connection = CreateConnection();

    const string sql = """
        DELETE FROM Employees
        WHERE Id = @Id
        """;

    return await connection.ExecuteAsync(
        sql,
        new { Id = id });
}

20. Query vs Execute in Dapper

This is an important interview topic.

Query

Use Query when you expect rows back.

var employees =
    await connection.QueryAsync<Employee>(sql);

Execute

Use Execute for INSERT, UPDATE, DELETE.

var affectedRows =
    await connection.ExecuteAsync(sql);

ExecuteScalar

Use when you want one value.

var employeeId =
    await connection.ExecuteScalarAsync<int>(sql);

21. Entity Framework Core Change Tracking

One of EF Core's major features is change tracking.

For example:

var employee =
    await dbContext.Employees
        .FirstAsync(x => x.Id == 10);

employee.Salary = 100000;

await dbContext.SaveChangesAsync();

EF Core knows that Salary changed.

It generates the UPDATE statement.


22. Dapper Does Not Track Changes

Dapper does not maintain an entity state.

For example:

var employee = await connection.QuerySingleAsync<Employee>(
    sql,
    new { Id = 10 });

employee.Salary = 100000;

Nothing happens automatically.

You must execute an UPDATE yourself:

await connection.ExecuteAsync(
    updateSql,
    employee);

23. AsNoTracking in EF Core

When you only need to read data, change tracking may not be required.

Use:

var employees = await dbContext.Employees
    .AsNoTracking()
    .ToListAsync();

This can reduce tracking overhead for read-only queries.

This is especially useful in:

Reporting
Dashboards
Search APIs
Read-only endpoints
Large result sets

24. Relationships in EF Core

Suppose we have:

Department
    |
    +---- Employee
    +---- Employee
    +---- Employee

Entities:

public class Department
{
    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Employee> Employees { get; set; }
        = new List<Employee>();
}

Employee:

public class Employee
{
    public int Id { get; set; }

    public string Name { get; set; }

    public int DepartmentId { get; set; }

    public Department Department { get; set; }
}

EF Core can configure the relationship.

modelBuilder.Entity<Employee>()
    .HasOne(e => e.Department)
    .WithMany(d => d.Employees)
    .HasForeignKey(e => e.DepartmentId);

25. Dapper Relationships

Dapper doesn't automatically manage relationships like EF Core.

You normally write SQL joins.

SELECT
    e.Id,
    e.Name,
    d.Id,
    d.Name
FROM Employees e
INNER JOIN Departments d
    ON e.DepartmentId = d.Id

Then map the result manually using Dapper's multi-mapping capabilities or project into DTOs.


26. EF Core Include

EF Core provides Include.

var employees = await dbContext.Employees
    .Include(x => x.Department)
    .ToListAsync();

This tells EF Core to load the related department data.


27. Dapper Stored Procedures

Dapper works very well with stored procedures.

Example:

CREATE PROCEDURE GetEmployeesByDepartment
    @Department NVARCHAR(100)
AS
BEGIN
    SELECT
        Id,
        Name,
        Department,
        Salary
    FROM Employees
    WHERE Department = @Department;
END

Dapper:

var employees =
    await connection.QueryAsync<Employee>(
        "GetEmployeesByDepartment",
        new
        {
            Department = "IT"
        },
        commandType: CommandType.StoredProcedure);

28. EF Core Stored Procedures

EF Core can also execute stored procedures.

For example, raw SQL can be used:

var employees =
    await dbContext.Employees
        .FromSqlInterpolated(
            $"EXEC GetEmployeesByDepartment {"IT"}")
        .ToListAsync();

EF Core also provides APIs for executing SQL commands where appropriate.

The important point is:

Choosing EF Core does not mean you can never use SQL or stored procedures.


29. SQL Injection

Both Dapper and EF Core can be used safely.

The important thing is parameterization.

Dapper

const string sql = """
    SELECT *
    FROM Employees
    WHERE Department = @Department
    """;

var employees = await connection.QueryAsync<Employee>(
    sql,
    new { Department = department });

Avoid building SQL using string concatenation:

var sql =
    "SELECT * FROM Employees WHERE Department = '" +
    department +
    "'";

30. EF Core Parameterization

LINQ queries are parameterized by EF Core.

var employees = await dbContext.Employees
    .Where(x => x.Department == department)
    .ToListAsync();

EF Core generates parameterized SQL.


31. Transactions with Dapper

Dapper uses ADO.NET transactions.

using var connection = CreateConnection();

connection.Open();

using var transaction = connection.BeginTransaction();

try
{
    await connection.ExecuteAsync(
        insertEmployeeSql,
        employee,
        transaction);

    await connection.ExecuteAsync(
        updateDepartmentSql,
        department,
        transaction);

    transaction.Commit();
}
catch
{
    transaction.Rollback();
    throw;
}

32. Transactions with EF Core

EF Core provides transaction support through Database.

await using var transaction =
    await dbContext.Database.BeginTransactionAsync();

try
{
    dbContext.Employees.Add(employee);

    await dbContext.SaveChangesAsync();

    department.Budget += 1000;

    await dbContext.SaveChangesAsync();

    await transaction.CommitAsync();
}
catch
{
    await transaction.RollbackAsync();
    throw;
}

33. EF Core Migrations

One of the major advantages of EF Core is migrations.

For example:

dotnet ef migrations add InitialCreate

Then:

dotnet ef database update

If you add a property:

public string Email { get; set; }

you can create another migration:

dotnet ef migrations add AddEmployeeEmail

Then:

dotnet ef database update

EF Core manages schema changes through migrations.


34. Does Dapper Support Migrations?

No.

Dapper itself is not a database schema migration framework.

You can use tools such as:

  • SQL scripts

  • DbUp

  • FluentMigrator

  • Flyway

  • Liquibase

  • CI/CD database migration processes

For example:

Application
    |
    +---- Dapper
    |
    +---- SQL Scripts
    |
    +---- Migration Tool
    |
    v
SQL Server

35. Performance Comparison

A common misconception is:

"Dapper is always faster than EF Core."

That is too simplistic.

Dapper has a smaller abstraction layer and often performs very well for straightforward SQL queries.

EF Core has additional capabilities such as:

  • Change tracking

  • Identity resolution

  • LINQ translation

  • Relationship handling

  • Model metadata

  • State management

These features have overhead.

However, properly written EF Core queries can perform very well.

For example:

var employees = await dbContext.Employees
    .AsNoTracking()
    .Where(x => x.Department == "IT")
    .Select(x => new EmployeeDto
    {
        Id = x.Id,
        Name = x.Name
    })
    .ToListAsync();

This avoids retrieving unnecessary columns and avoids tracking.


36. Performance Depends on SQL and Database Design

Database performance is not determined only by Dapper or EF Core.

Other factors include:

Indexes
Query design
Execution plan
Database schema
Network latency
Connection pooling
Number of records
Pagination
Locking
Blocking
CPU
Memory
I/O
Database configuration

For example, a poorly written Dapper query can be slower than a well-written EF Core query.


37. Pagination with Dapper

const string sql = """
    SELECT Id, Name, Department, Salary
    FROM Employees
    ORDER BY Id
    OFFSET @Offset ROWS
    FETCH NEXT @PageSize ROWS ONLY
    """;

var employees = await connection.QueryAsync<Employee>(
    sql,
    new
    {
        Offset = (pageNumber - 1) * pageSize,
        PageSize = pageSize
    });

38. Pagination with EF Core

var employees = await dbContext.Employees
    .AsNoTracking()
    .OrderBy(x => x.Id)
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

39. Repository Pattern with EF Core

public interface IEmployeeRepository
{
    Task<Employee?> GetByIdAsync(int id);

    Task<IEnumerable<Employee>> GetAllAsync();

    Task AddAsync(Employee employee);

    Task UpdateAsync(Employee employee);

    Task DeleteAsync(int id);
}

Implementation:

public class EmployeeRepository : IEmployeeRepository
{
    private readonly EmployeeDbContext _context;

    public EmployeeRepository(EmployeeDbContext context)
    {
        _context = context;
    }

    public async Task<Employee?> GetByIdAsync(int id)
    {
        return await _context.Employees
            .FirstOrDefaultAsync(x => x.Id == id);
    }
}

40. Repository Pattern with Dapper

public class EmployeeRepository : IEmployeeRepository
{
    private readonly string _connectionString;

    public EmployeeRepository(IConfiguration configuration)
    {
        _connectionString =
            configuration.GetConnectionString("DefaultConnection")!;
    }

    public async Task<Employee?> GetByIdAsync(int id)
    {
        using var connection =
            new SqlConnection(_connectionString);

        const string sql = """
            SELECT Id, Name, Department, Salary
            FROM Employees
            WHERE Id = @Id
            """;

        return await connection.QuerySingleOrDefaultAsync<Employee>(
            sql,
            new { Id = id });
    }
}

41. CQRS with Dapper and EF Core

A very common enterprise architecture is:

                 API
                  |
             Application
                  |
             +----+----+
             |         |
         Commands    Queries
             |         |
             v         v
          EF Core    Dapper
             |         |
             v         v
          Database   Database

For example:

Command

Create employee:

POST /employees
       |
       v
Command Handler
       |
       v
EF Core
       |
       v
SQL Server

Query

Get employees:

GET /employees
       |
       v
Query Handler
       |
       v
Dapper
       |
       v
SQL Server

This approach can combine the strengths of both technologies.


42. Real-Time Enterprise Example

Consider a BPO workforce-management application.

The system contains:

Forecast Service
Workforce Service
Capacity Service
Hiring Service
Employee Service
Reporting Service

Suppose the Reporting Service needs a complicated dashboard query:

Employee
    |
Department
    |
Skill
    |
Shift
    |
Forecast
    |
Actual Volume
    |
Utilization

The query may involve:

  • Multiple JOINs

  • Aggregations

  • Window functions

  • CTEs

  • Stored procedures

  • Complex filtering

Dapper can be a strong choice when the team wants precise control over the SQL.

For standard application CRUD, EF Core may provide faster development.

Therefore:

CRUD / Domain Logic
       |
     EF Core

Complex Reporting
       |
     Dapper

This is a hybrid approach.


43. Can We Use Dapper and EF Core in the Same Application?

Yes.

There is no requirement to choose only one.

For example:

ASP.NET Core API
       |
       +----------------+
       |                |
   EF Core           Dapper
       |                |
       +-------+--------+
               |
          SQL Server

Use EF Core for:

Create
Update
Delete
Domain operations
Change tracking
Relationships

Use Dapper for:

Reports
Complex queries
Read-heavy APIs
Stored procedures
Performance-sensitive queries

44. Hybrid Repository Example

public class EmployeeService
{
    private readonly EmployeeDbContext _context;
    private readonly IDbConnection _connection;

    public EmployeeService(
        EmployeeDbContext context,
        IDbConnection connection)
    {
        _context = context;
        _connection = connection;
    }
}

You can then use:

// EF Core
await _context.Employees.AddAsync(employee);
await _context.SaveChangesAsync();

and:

// Dapper
var result = await _connection.QueryAsync<EmployeeReport>(
    reportSql);

45. Advantages of Dapper

1. Lightweight

Dapper has a small abstraction layer.

2. High SQL Control

Developers write the SQL directly.

3. Excellent for Complex Queries

SQL features such as:

CTE
JOIN
Window Functions
Stored Procedures
Temporary Tables
Aggregation
Database-specific features

can be used naturally.

4. Good Performance

Dapper avoids many ORM features that introduce additional processing.

5. Easy to Integrate

It works directly with ADO.NET connections and transactions.


46. Disadvantages of Dapper

1. More SQL Code

Developers must write SQL manually.

2. No Change Tracking

You must explicitly update the database.

3. No Built-in Migrations

A separate migration solution is required.

4. Relationship Handling

Complex relationships require additional SQL and mapping.

5. Maintenance

Large applications can end up with many SQL strings.

For example:

Repository
   |
   +-- SQL Query 1
   +-- SQL Query 2
   +-- SQL Query 3
   +-- SQL Query 4
   +-- SQL Query 5
   ...

Maintaining these queries requires discipline.


47. Advantages of EF Core

1. Productivity

CRUD operations require relatively little code.

2. LINQ

You can query using strongly typed C# expressions.

var employees = await context.Employees
    .Where(x => x.Salary > 50000)
    .OrderBy(x => x.Name)
    .ToListAsync();

3. Change Tracking

EF Core automatically tracks entity changes.

4. Relationships

Relationships are modeled naturally.

5. Migrations

Database schema changes can be managed through migrations.

6. Strong Typing

Many errors can be caught at compile time.

7. Integration with .NET

EF Core integrates naturally with:

ASP.NET Core
Dependency Injection
LINQ
Async/Await
Configuration
Logging
Testing

48. Disadvantages of EF Core

1. Abstraction Overhead

EF Core performs more work than a lightweight data-access library.

2. Generated SQL

Developers may not have direct control over every aspect of generated SQL.

3. Learning Curve

You need to understand:

DbContext
Change Tracking
LINQ
Relationships
Loading strategies
Migrations
Transactions
Query translation

4. N+1 Query Problems

Poorly designed loading can result in many database queries.

5. Complex SQL

Some highly database-specific SQL can be more straightforward to express directly using SQL/Dapper.


49. What is N+1 Query Problem?

Suppose we load departments:

var departments =
    await context.Departments.ToListAsync();

Then separately query employees for each department.

If there are 100 departments:

1 query
+
100 queries
=
101 queries

This is the N+1 problem.

It can cause serious performance issues.

Possible solutions include:

Include()

or projection:

.Select(...)

or using an optimized SQL query/Dapper.


50. EF Core Projection

Instead of loading the entire entity:

var employees = await context.Employees
    .Select(x => new EmployeeDto
    {
        Id = x.Id,
        Name = x.Name
    })
    .ToListAsync();

This can generate SQL that selects only the required columns.


51. Security Comparison

Both Dapper and EF Core can be secure.

The developer must follow secure coding practices.

Important practices:

Parameterized queries
Least-privilege database accounts
Secrets management
Azure Key Vault
Managed Identity
Input validation
Authorization
HTTPS
Auditing

Never put passwords directly into source code.

Bad:

Server=myserver;
User Id=admin;
Password=MyPassword123;

In Azure environments, consider:

Managed Identity
        |
        v
Azure SQL

and/or secure secret management where credentials are required.


52. Azure SQL with EF Core

A typical Azure architecture:

Internet
   |
Azure Front Door / Application Gateway
   |
API Management
   |
ASP.NET Core API
   |
EF Core
   |
Azure SQL Database

53. Azure SQL with Dapper

Dapper fits the same architecture:

Internet
   |
API Management
   |
ASP.NET Core API
   |
Dapper
   |
Azure SQL Database

The difference is primarily how the application accesses the database.


54. Testing EF Core

EF Core repositories/services can be tested using approaches such as:

Real SQL Server
SQL Server test container
SQLite where appropriate
Mocked abstractions in specific cases

For realistic database behavior, integration tests against a real SQL Server-compatible database are often more representative than relying exclusively on mocks.


55. Testing Dapper

Dapper-based repositories are also commonly tested using:

SQL Server test database
Testcontainers
Docker
Integration tests

Example architecture:

Integration Test
       |
       v
Dapper Repository
       |
       v
SQL Server Container

This provides realistic SQL execution.


56. Dependency Injection

EF Core:

builder.Services.AddDbContext<EmployeeDbContext>(
    options =>
        options.UseSqlServer(connectionString));

Dapper:

builder.Services.AddScoped<IDbConnection>(
    _ => new SqlConnection(connectionString));

In modern applications, it is also common to create a connection factory abstraction rather than injecting a raw connection everywhere.


57. Which One Should You Choose?

There is no universal answer.

Consider the application's requirements.

Choose EF Core when:

CRUD-heavy application
Domain-driven design
Complex entity relationships
Change tracking required
Rapid application development
Code-first migrations
LINQ-heavy application
Strong domain model

Choose Dapper when:

SQL-heavy application
Complex reporting
Existing stored procedures
Fine SQL control required
Read-heavy APIs
Database-specific SQL
Very lightweight data-access layer desired

Choose both when:

Business/domain operations -> EF Core

Complex queries/reports -> Dapper

58. Interview Question: Is Dapper faster than EF Core?

A good interview answer:

Dapper generally has less abstraction and less ORM overhead, so it can perform very well for SQL-centric operations. However, it is not correct to say that Dapper is always faster. Actual performance depends on query design, indexing, database workload, network latency, result size, tracking, and application architecture. Properly optimized EF Core queries can also provide excellent performance.


59. Interview Question: Does Dapper track changes?

No.

Dapper does not provide EF Core-style change tracking.

The developer explicitly executes INSERT, UPDATE, or DELETE statements.


60. Interview Question: Does EF Core always generate efficient SQL?

No.

EF Core generates SQL based on the LINQ expression and model configuration, but developers still need to understand the SQL being generated.

For performance-critical queries, inspect:

Generated SQL
Execution Plan
Indexes
Query duration
Database waits
Logical reads

61. Interview Question: Can Dapper call stored procedures?

Yes.

Example:

await connection.QueryAsync<Employee>(
    "GetEmployees",
    parameters,
    commandType: CommandType.StoredProcedure);

62. Interview Question: Can EF Core use stored procedures?

Yes.

EF Core supports raw SQL and stored procedure-related database operations.

However, the exact API depends on whether you are querying entities, executing commands, or using EF Core's newer modification stored-procedure capabilities.


63. Interview Question: Can Dapper perform transactions?

Yes.

Dapper uses ADO.NET transaction support.

using var transaction =
    connection.BeginTransaction();

64. Interview Question: Can EF Core and Dapper use the Same Database?

Yes.

For example:

                     SQL Server
                         |
             +-----------+-----------+
             |                       |
          EF Core                 Dapper
             |                       |
       Write operations         Read operations

They can coexist in the same application.


65. Interview Question: Which is better for Microservices?

Neither is universally better.

The decision should depend on the microservice's responsibilities.

For example:

Order Service
    |
    +-- EF Core

because it contains domain relationships and transactional business operations.

A:

Reporting Service
    |
    +-- Dapper

may be appropriate when it is primarily SQL/reporting focused.

Different microservices can use different data-access technologies.


66. Recommended Enterprise Architecture

For a large ASP.NET Core application, a practical architecture can be:

                    Client
                      |
                      v
                API Management
                      |
                      v
                ASP.NET Core
                      |
              +-------+-------+
              |               |
          Commands          Queries
              |               |
              v               v
           EF Core          Dapper
              |               |
              +-------+-------+
                      |
                  SQL Server

The important principle is:

Select the data-access technology based on the use case rather than choosing a technology simply because it is popular.


67. Dapper vs EF Core – Final Comparison

AreaDapperEF Core
AbstractionLowHigh
SQL ControlExcellentGood
CRUD ProductivityGoodExcellent
Change TrackingNoYes
LINQLimitedExcellent
RelationshipsManualExcellent
MigrationsExternalBuilt-in
Stored ProceduresExcellentSupported
Complex SQLExcellentGood
PerformanceVery goodVery good
LearningEasier initiallyMore concepts
Domain ModelingManualStrong
Read ModelsExcellentExcellent
Write ModelsGoodExcellent
ReportingExcellentGood/Excellent depending on query
Large Enterprise ApplicationVery goodVery good
Hybrid UsageYesYes

68. Real-World Recommendation

For a typical enterprise .NET application, don't think:

Dapper OR EF Core

Think:

Where does each technology provide the most value?

For example:

                   ASP.NET Core
                         |
                 Application Layer
                         |
             +-----------+-----------+
             |                       |
        Business Commands          Queries
             |                       |
             v                       v
          EF Core                 Dapper
             |                       |
             +-----------+-----------+
                         |
                     SQL Server

EF Core can handle:

Aggregate updates
CRUD
Transactions
Relationships
Change tracking
Domain operations

Dapper can handle:

Reports
Dashboards
Complex joins
Read models
Stored procedures
SQL-specific operations
Performance-sensitive queries

69. Final Conclusion

Both Dapper and Entity Framework Core are excellent database-access technologies for .NET, but they have different philosophies.

Dapper

Think:

"I want control over my SQL and a lightweight mapping layer."

EF Core

Think:

"I want an ORM that manages entities, relationships, tracking, LINQ and database schema evolution."

For many enterprise applications, the best architecture is not necessarily choosing one exclusively.

A hybrid approach can be very effective:

             ASP.NET Core Application
                       |
          +------------+------------+
          |                         |
       EF Core                    Dapper
          |                         |
    Business/Data               Reporting/
    modifications               Read models
          |                         |
          +------------+------------+
                       |
                    SQL Server

The most important skill for a senior .NET developer is not simply knowing Dapper vs EF Core, but understanding:

  • How SQL is executed

  • How connections are managed

  • How transactions work

  • How indexes affect queries

  • How to avoid N+1 queries

  • How to optimize EF Core queries

  • How to write efficient Dapper SQL

  • How to analyze execution plans

  • How to handle concurrency

  • How to design repositories and services

  • How to secure database access

  • How to monitor production database performance

Ultimately, database design, SQL quality, indexing, query patterns, and architecture usually have a much larger impact on application performance than simply selecting Dapper or EF Core.


Quick Interview Summary

If an interviewer asks:

"Dapper or EF Core — which one would you choose?"

A strong answer is:

"I would choose based on the application's requirements. EF Core is useful when we need an ORM with LINQ, change tracking, relationships, migrations and strong domain modeling. Dapper is useful when we need fine-grained SQL control, lightweight data access, complex reporting queries or existing stored procedures. In an enterprise application, I can also use both — EF Core for transactional/domain operations and Dapper for optimized read models and reporting. I would validate the decision using actual query performance, execution plans and application requirements rather than assuming one technology is always faster."

This is structured to work as a technical blog article and also as an interview-preparation reference. If you want, I can next turn this into a Dapper vs EF Core Part-2 with a complete ASP.NET Core Web API project, SQL Server database, Repository + Unit of Work + CQRS, and production-ready code.

Thursday, September 17, 2026

MongoDB with Microsoft Azure and ASP.NET Core – Complete End-to-End Guide with Real-Time E-Commerce Example

Introduction

Modern applications often need a database that can handle:

  • Large volumes of data

  • Rapidly changing data structures

  • High read/write workloads

  • Horizontal scalability

  • JSON-based application data

  • Microservices architectures

  • Event-driven applications

MongoDB is a popular NoSQL document database designed for these types of workloads.

When MongoDB is deployed using MongoDB Atlas on Microsoft Azure, we can build a cloud-native architecture where:

Angular / React / Mobile
          |
          v
     Azure Front Door
          |
          v
 Azure API Management
          |
          v
 ASP.NET Core Web API
          |
          v
     MongoDB Atlas
       on Azure

MongoDB Atlas is MongoDB's fully managed cloud database service, and Atlas supports Microsoft Azure as a cloud provider. (MongoDB)


1. What is MongoDB?

MongoDB is a NoSQL document-oriented database.

Unlike SQL Server, where data is normally organized as:

Database
   |
   +-- Tables
          |
          +-- Rows

MongoDB organizes data as:

Database
   |
   +-- Collections
          |
          +-- Documents

A MongoDB document looks similar to JSON:

{
  "customerId": "CUST1001",
  "name": "Ravi Kumar",
  "email": "ravi@example.com",
  "city": "Hyderabad"
}

Internally MongoDB stores documents using BSON, which extends JSON with additional data types.


2. SQL Server vs MongoDB

SQL ServerMongoDB
DatabaseDatabase
TableCollection
RowDocument
ColumnField
Primary Key_id
JOIN$lookup / application-level modeling
Stored ProcedureApplication/service logic
RelationalDocument-oriented
Fixed schema commonly usedFlexible document schema
SQLMongoDB Query API

For example, SQL Server might contain:

Customers
-------------------------
CustomerId
Name
Email
City

MongoDB could contain:

{
  "_id": "CUST1001",
  "name": "Ravi Kumar",
  "email": "ravi@example.com",
  "city": "Hyderabad"
}

3. What is MongoDB Atlas?

MongoDB Atlas is the managed cloud service for MongoDB.

Instead of installing and maintaining MongoDB servers yourself, Atlas manages much of the database infrastructure.

Conceptually:

Your Application
       |
       | MongoDB Driver
       |
       v
+----------------------+
|   MongoDB Atlas      |
|                      |
|  MongoDB Cluster     |
|                      |
|  Database            |
|    |                 |
|  Collection          |
|    |                 |
|  Documents           |
+----------------------+

Atlas can deploy MongoDB clusters on Microsoft Azure. (MongoDB)


4. Why MongoDB on Azure?

A common enterprise architecture is:

Azure
│
├── Azure Front Door
│
├── API Management
│
├── AKS / App Service
│       │
│       └── ASP.NET Core APIs
│
├── Key Vault
│
├── Application Insights
│
└── MongoDB Atlas
        │
        └── MongoDB Cluster

Advantages include:

Scalability

MongoDB can scale horizontally using sharding.

High availability

Atlas supports replica-set based deployments, and Azure availability zones can be used for supported regions. (MongoDB)

Managed infrastructure

Atlas manages much of the operational database infrastructure.

Flexible document model

Different documents can contain different fields when your application requires a flexible schema.

Cloud integration

Applications running on Azure can connect to MongoDB Atlas using standard or private networking approaches.


5. Real-Time Example

Let's take an E-Commerce Order Management System.

Suppose customers place orders through an Angular application.

Architecture:

                Customer
                   |
                   v
            Angular Application
                   |
                   v
           Azure API Management
                   |
                   v
          ASP.NET Core Web API
                   |
       +-----------+-----------+
       |                       |
       v                       v
 MongoDB Atlas             Azure Service Bus
       |                       |
       v                       v
 Orders Collection       Other Microservices

The Order Service needs to store:

  • Customer information

  • Order information

  • Products

  • Quantity

  • Price

  • Shipping address

  • Payment status

  • Order status

  • Created date

MongoDB is well suited to representing this as a document.


6. MongoDB Data Model

Our database can be:

ECommerceDB
   |
   +-- Orders
   |
   +-- Customers
   |
   +-- Products

For this example:

Database:
ECommerceDB

Collection:
Orders

7. Sample MongoDB Document

An order could look like this:

{
  "_id": "ORD10001",
  "customer": {
    "customerId": "CUST1001",
    "name": "Ravi Kumar",
    "email": "ravi@example.com"
  },
  "items": [
    {
      "productId": "P1001",
      "productName": "Laptop",
      "quantity": 1,
      "unitPrice": 75000
    },
    {
      "productId": "P1002",
      "productName": "Mouse",
      "quantity": 2,
      "unitPrice": 1500
    }
  ],
  "shippingAddress": {
    "street": "Hitech City",
    "city": "Hyderabad",
    "state": "Telangana",
    "country": "India",
    "postalCode": "500081"
  },
  "paymentStatus": "Paid",
  "orderStatus": "Confirmed",
  "totalAmount": 78000,
  "createdAt": "2026-09-17T08:30:00Z"
}

Notice that customer, items, and shipping information can be represented naturally inside the document.


8. MongoDB Terminology

Understanding the terminology is important.

MongoDB
   |
   +-- Database
          |
          +-- Collection
                  |
                  +-- Document
                          |
                          +-- Field

For our application:

MongoDB
   |
   +-- ECommerceDB
          |
          +-- Orders
          |
          +-- Customers
          |
          +-- Products

9. Create MongoDB Atlas on Azure

Go to MongoDB Atlas and create an Atlas organization/project.

When creating your cluster, select:

Cloud Provider:
Microsoft Azure

Then select an appropriate Azure region.

MongoDB currently lists Azure regions including Central India, South India and West India, among many other supported Azure regions. Availability and cluster-tier support vary by region. (MongoDB)

For an application primarily running in India, an appropriate India region can reduce network latency, subject to your application's requirements and the cluster tier available there.


10. Create Database User

Create a MongoDB database user such as:

Username:
ecommerce-api-user

Use a strong password.

Do not hard-code the password in source code.

Bad:

var connectionString =
    "mongodb+srv://user:password@cluster.mongodb.net";

Instead, use:

Azure Key Vault
       |
       v
ASP.NET Core
       |
       v
MongoDB Atlas

11. Configure Network Access

Atlas provides network controls that determine which clients can connect.

For development, you may configure an appropriate IP access rule.

For production, consider private networking where appropriate.

A production architecture could be:

Azure VNet
   |
   +-----------------------+
   |                       |
   v                       v
AKS                    Private Endpoint
   |                       |
   |                       v
   +--------------> MongoDB Atlas

MongoDB documents different connection approaches, including standard connections, peering and private endpoints depending on how the application network is configured. (MongoDB)


12. Get MongoDB Connection String

From Atlas, obtain the application connection string.

It generally resembles:

mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/

The MongoDB .NET driver uses the connection URI together with MongoClient to establish the connection. (MongoDB)

Never publish your real connection string in GitHub or your blog.


13. Create ASP.NET Core Web API

Create the project:

dotnet new webapi -n Ecommerce.Api

Move into the project:

cd Ecommerce.Api

Install the MongoDB .NET driver:

dotnet add package MongoDB.Driver

MongoDB provides an official .NET/C# driver for connecting .NET applications to MongoDB deployments including Atlas. (MongoDB)


14. Configure appsettings.json

Create:

{
  "MongoDB": {
    "ConnectionString": "mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/",
    "DatabaseName": "ECommerceDB",
    "OrdersCollectionName": "Orders"
  }
}

For production, don't store the actual secret directly in appsettings.json.

Instead:

Azure Key Vault
      |
      v
ASP.NET Core Configuration
      |
      v
MongoDB Driver

MongoDB's own REST API tutorial demonstrates storing the MongoDB URI, database name, and collection name in application configuration. (MongoDB)


15. Create MongoDB Settings Class

Create:

Configuration/
    MongoDbSettings.cs
public class MongoDbSettings
{
    public string ConnectionString { get; set; } = string.Empty;

    public string DatabaseName { get; set; } = string.Empty;

    public string OrdersCollectionName { get; set; } = string.Empty;
}

16. Create Order Model

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class Order
{
    [BsonId]
    public string Id { get; set; } = string.Empty;

    public Customer Customer { get; set; } = new();

    public List<OrderItem> Items { get; set; } = new();

    public ShippingAddress ShippingAddress { get; set; } = new();

    public decimal TotalAmount { get; set; }

    public string PaymentStatus { get; set; } = string.Empty;

    public string OrderStatus { get; set; } = string.Empty;

    public DateTime CreatedAt { get; set; }
}

17. Customer Model

public class Customer
{
    public string CustomerId { get; set; } = string.Empty;

    public string Name { get; set; } = string.Empty;

    public string Email { get; set; } = string.Empty;
}

18. OrderItem Model

public class OrderItem
{
    public string ProductId { get; set; } = string.Empty;

    public string ProductName { get; set; } = string.Empty;

    public int Quantity { get; set; }

    public decimal UnitPrice { get; set; }
}

19. ShippingAddress Model

public class ShippingAddress
{
    public string Street { get; set; } = string.Empty;

    public string City { get; set; } = string.Empty;

    public string State { get; set; } = string.Empty;

    public string Country { get; set; } = string.Empty;

    public string PostalCode { get; set; } = string.Empty;
}

20. Create MongoDB Service

Create:

Services/
    OrderService.cs
using MongoDB.Driver;

public class OrderService
{
    private readonly IMongoCollection<Order> _orders;

    public OrderService(IConfiguration configuration)
    {
        var connectionString =
            configuration["MongoDB:ConnectionString"];

        var databaseName =
            configuration["MongoDB:DatabaseName"];

        var collectionName =
            configuration["MongoDB:OrdersCollectionName"];

        var client = new MongoClient(connectionString);

        var database = client.GetDatabase(databaseName);

        _orders = database.GetCollection<Order>(collectionName);
    }
}

The basic MongoDB .NET architecture is:

MongoDB Connection String
          |
          v
     MongoClient
          |
          v
      Database
          |
          v
     Collection
          |
          v
      Documents

21. Insert Order

Add:

public async Task CreateAsync(Order order)
{
    await _orders.InsertOneAsync(order);
}

Complete example:

public async Task CreateAsync(Order order)
{
    order.Id = $"ORD{DateTime.UtcNow.Ticks}";

    order.CreatedAt = DateTime.UtcNow;

    await _orders.InsertOneAsync(order);
}

MongoDB creates the document in the Orders collection.


22. Insert Sample Data

You can insert this document:

{
  "_id": "ORD10001",
  "customer": {
    "customerId": "CUST1001",
    "name": "Ravi Kumar",
    "email": "ravi@example.com"
  },
  "items": [
    {
      "productId": "P1001",
      "productName": "Laptop",
      "quantity": 1,
      "unitPrice": 75000
    },
    {
      "productId": "P1002",
      "productName": "Mouse",
      "quantity": 2,
      "unitPrice": 1500
    }
  ],
  "shippingAddress": {
    "street": "Hitech City",
    "city": "Hyderabad",
    "state": "Telangana",
    "country": "India",
    "postalCode": "500081"
  },
  "totalAmount": 78000,
  "paymentStatus": "Paid",
  "orderStatus": "Confirmed",
  "createdAt": "2026-09-17T08:30:00Z"
}

23. Retrieve All Orders

public async Task<List<Order>> GetAllAsync()
{
    return await _orders
        .Find(_ => true)
        .ToListAsync();
}

Here:

.Find(_ => true)

means:

Return all documents.

24. Retrieve Order by ID

public async Task<Order?> GetByIdAsync(string id)
{
    return await _orders
        .Find(x => x.Id == id)
        .FirstOrDefaultAsync();
}

25. Search Orders by Customer

public async Task<List<Order>> GetByCustomerAsync(
    string customerId)
{
    return await _orders
        .Find(x => x.Customer.CustomerId == customerId)
        .ToListAsync();
}

This demonstrates querying a nested document property.


26. Search Orders by City

public async Task<List<Order>> GetByCityAsync(
    string city)
{
    return await _orders
        .Find(x => x.ShippingAddress.City == city)
        .ToListAsync();
}

For example:

GET /api/orders/city/Hyderabad

27. Update an Order

Suppose an order changes from:

Confirmed

to:

Shipped

We can write:

public async Task UpdateStatusAsync(
    string id,
    string status)
{
    var filter =
        Builders<Order>.Filter.Eq(x => x.Id, id);

    var update =
        Builders<Order>.Update
            .Set(x => x.OrderStatus, status);

    await _orders.UpdateOneAsync(
        filter,
        update);
}

28. Delete an Order

public async Task DeleteAsync(string id)
{
    await _orders.DeleteOneAsync(
        x => x.Id == id);
}

29. Create Controller

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly OrderService _service;

    public OrdersController(OrderService service)
    {
        _service = service;
    }

    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var orders = await _service.GetAllAsync();

        return Ok(orders);
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(string id)
    {
        var order = await _service.GetByIdAsync(id);

        if (order == null)
            return NotFound();

        return Ok(order);
    }

    [HttpPost]
    public async Task<IActionResult> Create(Order order)
    {
        await _service.CreateAsync(order);

        return Ok(order);
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(string id)
    {
        await _service.DeleteAsync(id);

        return NoContent();
    }
}

30. Register MongoDB Service

In Program.cs:

builder.Services.AddSingleton<OrderService>();

Then:

var app = builder.Build();

app.MapControllers();

app.Run();

A simplified architecture becomes:

HTTP Request
     |
     v
OrdersController
     |
     v
OrderService
     |
     v
MongoClient
     |
     v
MongoDB Atlas
     |
     v
Orders Collection

31. POST Request Example

Send:

POST /api/orders
Content-Type: application/json

Request body:

{
  "customer": {
    "customerId": "CUST1002",
    "name": "Suresh",
    "email": "suresh@example.com"
  },
  "items": [
    {
      "productId": "P2001",
      "productName": "Mobile Phone",
      "quantity": 1,
      "unitPrice": 45000
    }
  ],
  "shippingAddress": {
    "street": "Madhapur",
    "city": "Hyderabad",
    "state": "Telangana",
    "country": "India",
    "postalCode": "500081"
  },
  "totalAmount": 45000,
  "paymentStatus": "Paid",
  "orderStatus": "Confirmed"
}

32. GET Request

GET /api/orders

Response:

[
  {
    "_id": "ORD10001",
    "customer": {
      "customerId": "CUST1001",
      "name": "Ravi Kumar"
    },
    "totalAmount": 78000,
    "orderStatus": "Confirmed"
  }
]

33. MongoDB Query Example

MongoDB shell query:

db.Orders.find({
    "customer.customerId": "CUST1001"
})

Another example:

db.Orders.find({
    "shippingAddress.city": "Hyderabad"
})

Find paid orders:

db.Orders.find({
    "paymentStatus": "Paid"
})

34. MongoDB Operators

MongoDB provides many query operators.

Greater Than

db.Orders.find({
    "totalAmount": {
        $gt: 50000
    }
})

Less Than

db.Orders.find({
    "totalAmount": {
        $lt: 50000
    }
})

Greater Than or Equal

db.Orders.find({
    "totalAmount": {
        $gte: 50000
    }
})

IN

db.Orders.find({
    "orderStatus": {
        $in: ["Confirmed", "Shipped"]
    }
})

35. MongoDB Indexes

Indexes are extremely important for production applications.

Suppose we frequently search:

CustomerId

Create an index:

db.Orders.createIndex({
    "customer.customerId": 1
})

For city:

db.Orders.createIndex({
    "shippingAddress.city": 1
})

For order status:

db.Orders.createIndex({
    "orderStatus": 1
})

The application should create indexes based on actual query patterns rather than creating indexes on every field.

MongoDB's .NET driver documentation includes dedicated guidance for creating and managing indexes. (MongoDB)


36. Compound Index

Suppose the application frequently queries:

Customer + Order Status

We can create:

db.Orders.createIndex({
    "customer.customerId": 1,
    "orderStatus": 1
})

This can support queries such as:

db.Orders.find({
    "customer.customerId": "CUST1001",
    "orderStatus": "Confirmed"
})

37. MongoDB Aggregation

Aggregation is one of MongoDB's powerful features.

Suppose we want:

Total sales by city

We can use:

db.Orders.aggregate([
    {
        $group: {
            _id: "$shippingAddress.city",
            totalSales: {
                $sum: "$totalAmount"
            }
        }
    }
])

Example result:

[
  {
    "_id": "Hyderabad",
    "totalSales": 2500000
  },
  {
    "_id": "Bangalore",
    "totalSales": 1800000
  }
]

38. Aggregation Pipeline

MongoDB aggregation works as a pipeline:

Documents
    |
    v
   $match
    |
    v
  $group
    |
    v
  $sort
    |
    v
 Result

Example:

db.Orders.aggregate([
    {
        $match: {
            "paymentStatus": "Paid"
        }
    },
    {
        $group: {
            _id: "$shippingAddress.city",
            totalSales: {
                $sum: "$totalAmount"
            }
        }
    },
    {
        $sort: {
            totalSales: -1
        }
    }
])

39. Consuming MongoDB Data from Angular

The frontend does not normally connect directly to MongoDB.

Instead:

Angular
   |
   | HTTP
   v
ASP.NET Core Web API
   |
   | MongoDB Driver
   v
MongoDB Atlas

Angular service:

@Injectable({
  providedIn: 'root'
})
export class OrderService {

  private apiUrl = 'https://api.example.com/api/orders';

  constructor(private http: HttpClient) {}

  getOrders() {
    return this.http.get<any[]>(this.apiUrl);
  }

  getOrder(id: string) {
    return this.http.get<any>(
      `${this.apiUrl}/${id}`
    );
  }
}

Component:

export class OrdersComponent {

  orders: any[] = [];

  constructor(
    private orderService: OrderService
  ) {}

  ngOnInit() {

    this.orderService
      .getOrders()
      .subscribe(data => {
        this.orders = data;
      });

  }
}

40. Complete Data Flow

When the Angular application requests orders:

1. User opens Orders screen
             |
             v
2. Angular sends HTTP GET
             |
             v
3. Azure API Management
             |
             v
4. ASP.NET Core API
             |
             v
5. OrderService
             |
             v
6. MongoDB Driver
             |
             v
7. MongoDB Atlas
             |
             v
8. Orders Collection
             |
             v
9. MongoDB returns documents
             |
             v
10. ASP.NET Core returns JSON
             |
             v
11. Angular displays data

41. Production Azure Architecture

For a larger enterprise system, the architecture could look like this:

                         Internet
                            |
                            v
                  +-------------------+
                  | Azure Front Door  |
                  +-------------------+
                            |
                            v
                  +-------------------+
                  | Azure API         |
                  | Management        |
                  +-------------------+
                            |
                            v
                  +-------------------+
                  | Azure Application |
                  | / AKS             |
                  +-------------------+
                            |
               +------------+------------+
               |                         |
               v                         v
        Order Microservice       Customer Service
               |                         |
               v                         v
        MongoDB Atlas              MongoDB Atlas
               |
               v
       Orders Collection

Supporting services:

Azure Key Vault
Azure Monitor
Application Insights
Azure Service Bus
Azure Container Registry
Azure DevOps

42. MongoDB with Microservices

MongoDB works particularly well with microservice architectures when each service owns its data.

For example:

Order Service
     |
     v
OrderDB
     |
     +-- Orders

Customer Service
     |
     v
CustomerDB
     |
     +-- Customers

Product Service
     |
     v
ProductDB
     |
     +-- Products

Instead of:

All Microservices
       |
       v
One Shared Database

we can use:

Order Service --------> Order Database

Customer Service -----> Customer Database

Product Service ------> Product Database

This helps maintain service ownership and reduces tight database coupling.


43. MongoDB and Azure Service Bus

Consider an order workflow:

Customer
   |
   v
Order API
   |
   v
MongoDB
   |
   v
Order Created
   |
   v
Azure Service Bus
   |
   +-----------> Inventory Service
   |
   +-----------> Payment Service
   |
   +-----------> Notification Service

For example:

{
  "eventType": "OrderCreated",
  "orderId": "ORD10001",
  "customerId": "CUST1001",
  "totalAmount": 78000
}

MongoDB stores the order state while Azure Service Bus can be used for asynchronous communication between services.


44. Security Architecture

A production architecture should avoid exposing database credentials.

Recommended:

Azure Key Vault
       |
       | Secret
       v
ASP.NET Core
       |
       | TLS
       v
MongoDB Atlas

Instead of:

appsettings.json
       |
       +-- username
       +-- password

Use:

Key Vault
   |
   v
Managed Identity
   |
   v
ASP.NET Core

Also consider:

  • TLS encryption

  • Network restrictions

  • Private connectivity where appropriate

  • Least-privilege database users

  • Secret rotation

  • Application-level authentication and authorization

  • Audit and monitoring


45. Azure Deployment Flow

A typical CI/CD pipeline could be:

Developer
    |
    v
Git Repository
    |
    v
Azure DevOps Pipeline
    |
    +---- Build
    |
    +---- Unit Tests
    |
    +---- Security Scan
    |
    +---- Docker Build
    |
    +---- Push to ACR
    |
    v
Azure AKS
    |
    v
ASP.NET Core Container
    |
    v
MongoDB Atlas

46. Docker Example

A simplified Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base

WORKDIR /app

EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build

WORKDIR /src

COPY . .

RUN dotnet restore

RUN dotnet publish \
    -c Release \
    -o /app/publish

FROM base AS final

WORKDIR /app

COPY --from=build /app/publish .

ENTRYPOINT ["dotnet", "Ecommerce.Api.dll"]

The container can then be deployed to Azure services such as AKS or another appropriate Azure hosting platform.


47. Performance Considerations

When using MongoDB in production, pay attention to:

1. Indexes

Create indexes for frequently executed queries.

2. Document Size

Avoid unnecessarily huge documents.

3. Query Projection

Retrieve only the fields you need when appropriate.

4. Connection Management

Reuse MongoClient rather than creating a new client for every request.

5. Pagination

Don't return millions of records in a single API request.

Example:

GET /api/orders?page=1&pageSize=50

6. Monitoring

Monitor:

CPU
Memory
Disk
Connections
Query performance
Latency
Operations per second

48. Pagination Example

A simple MongoDB pagination query:

public async Task<List<Order>> GetPagedAsync(
    int page,
    int pageSize)
{
    return await _orders
        .Find(_ => true)
        .Skip((page - 1) * pageSize)
        .Limit(pageSize)
        .ToListAsync();
}

For example:

page = 1
pageSize = 20

MongoDB returns the first 20 documents.


49. MongoDB Repository Pattern

For larger applications, you can introduce a repository layer:

Controller
    |
    v
Application Service
    |
    v
Repository
    |
    v
MongoDB

Example:

public interface IOrderRepository
{
    Task<List<Order>> GetAllAsync();

    Task<Order?> GetByIdAsync(string id);

    Task CreateAsync(Order order);

    Task UpdateAsync(Order order);

    Task DeleteAsync(string id);
}

Implementation:

public class OrderRepository : IOrderRepository
{
    private readonly IMongoCollection<Order> _orders;

    public OrderRepository(IMongoDatabase database)
    {
        _orders = database.GetCollection<Order>("Orders");
    }

    public async Task<List<Order>> GetAllAsync()
    {
        return await _orders
            .Find(_ => true)
            .ToListAsync();
    }

    public async Task<Order?> GetByIdAsync(string id)
    {
        return await _orders
            .Find(x => x.Id == id)
            .FirstOrDefaultAsync();
    }

    public async Task CreateAsync(Order order)
    {
        await _orders.InsertOneAsync(order);
    }

    public async Task UpdateAsync(Order order)
    {
        await _orders.ReplaceOneAsync(
            x => x.Id == order.Id,
            order);
    }

    public async Task DeleteAsync(string id)
    {
        await _orders.DeleteOneAsync(
            x => x.Id == id);
    }
}

50. Recommended Enterprise Project Structure

A clean architecture could look like:

Ecommerce.Api
│
├── Controllers
│     └── OrdersController.cs
│
├── Models
│     ├── Order.cs
│     ├── Customer.cs
│     └── OrderItem.cs
│
├── DTOs
│     ├── CreateOrderRequest.cs
│     └── OrderResponse.cs
│
├── Services
│     └── OrderService.cs
│
├── Repositories
│     ├── IOrderRepository.cs
│     └── OrderRepository.cs
│
├── Configuration
│     └── MongoDbSettings.cs
│
├── Program.cs
│
└── appsettings.json

51. MongoDB vs SQL Server – When to Use Which?

MongoDB can be useful when:

  • Data is naturally document-oriented

  • Schema changes frequently

  • High-scale workloads require horizontal scaling

  • Application data is naturally represented as JSON-like documents

  • Microservices need independently owned data

  • Flexible document structures are valuable

SQL Server can be preferable when:

  • Strong relational modeling is central

  • Complex joins dominate the workload

  • Existing enterprise applications rely heavily on relational features

  • Strong transactional relational constraints are fundamental

The choice should be based on workload and data requirements rather than simply choosing NoSQL because it is newer.


52. Real-Time End-to-End Example

Let's put everything together.

Suppose a customer purchases a laptop.

Step 1 – Customer

Customer submits:

Laptop
Quantity: 1
Price: ₹75,000

Step 2 – Angular

Angular sends:

POST /api/orders

Step 3 – Azure API Management

APIM handles API gateway responsibilities.

Step 4 – ASP.NET Core

The Order Controller receives the request.

Step 5 – Order Service

The service validates the order.

Step 6 – MongoDB

The Order Service saves:

{
  "_id": "ORD10001",
  "customer": {
    "customerId": "CUST1001"
  },
  "items": [
    {
      "productId": "P1001",
      "quantity": 1,
      "unitPrice": 75000
    }
  ],
  "totalAmount": 75000,
  "orderStatus": "Confirmed"
}

Step 7 – Event

The service publishes:

OrderCreated

to Azure Service Bus.

Step 8 – Inventory

Inventory Service receives the event.

Laptop Stock
100 → 99

Step 9 – Payment

Payment Service processes payment.

Step 10 – Notification

Notification Service sends the order confirmation.

Complete flow:

Angular
   |
   v
Azure API Management
   |
   v
Order API
   |
   v
Order Service
   |
   +--------------------+
   |                    |
   v                    v
MongoDB Atlas       Azure Service Bus
   |                    |
   |             +------+------+
   |             |             |
   |             v             v
   |        Inventory      Payment
   |                         |
   |                         v
   |                    Notification
   |
   v
Order Status

53. Important Production Best Practices

Database

  • Use appropriate indexes.

  • Design documents around access patterns.

  • Avoid unnecessarily large documents.

  • Monitor slow queries.

  • Use appropriate replication/high availability configuration.

Application

  • Reuse MongoClient.

  • Use asynchronous APIs.

  • Implement pagination.

  • Use DTOs rather than exposing internal models directly.

  • Validate incoming requests.

Security

  • Never commit passwords.

  • Use Azure Key Vault.

  • Use least-privilege database accounts.

  • Restrict network access.

  • Use encrypted connections.

Azure

  • Use Application Insights/Azure Monitor.

  • Use managed identities where applicable.

  • Use private networking where appropriate.

  • Use CI/CD.

  • Use autoscaling based on workload requirements.

Architecture

  • Keep database ownership with the appropriate service.

  • Use Service Bus for asynchronous communication where appropriate.

  • Don't make every microservice directly access every other service's database.


54. Complete Architecture Summary

The complete enterprise architecture can be represented as:

                         USERS
                           |
                           v
                    Angular / Mobile
                           |
                           v
                  +-------------------+
                  | Azure Front Door  |
                  +-------------------+
                           |
                           v
                  +-------------------+
                  | Azure API         |
                  | Management        |
                  +-------------------+
                           |
                           v
                +-----------------------+
                | Azure AKS / App       |
                | Service               |
                +-----------------------+
                           |
                           v
                 ASP.NET Core Web API
                           |
              +------------+-------------+
              |                          |
              v                          v
       MongoDB Atlas              Azure Service Bus
              |                          |
              v                    +-----+------+
       ECommerceDB                 |            |
              |                    v            v
          Collections          Inventory     Payment
              |
       +------+------+
       |             |
    Orders        Customers
       |
       v
   Products
       
Supporting Services:
       
Azure Key Vault
Azure Monitor
Application Insights
Azure Container Registry
Azure DevOps

55. Conclusion

MongoDB provides a document-oriented approach to data storage that can be particularly useful for modern cloud-native and microservices applications.

When combined with MongoDB Atlas on Microsoft Azure, an ASP.NET Core application can follow a cloud architecture such as:

Frontend
   ↓
Azure API Management
   ↓
ASP.NET Core
   ↓
MongoDB Atlas on Azure

For an enterprise application, this can be extended with:

Azure Front Door
       ↓
API Management
       ↓
AKS
       ↓
Microservices
       ↓
MongoDB Atlas
       +
Azure Service Bus
       +
Azure Key Vault
       +
Application Insights

The official MongoDB .NET driver provides the APIs needed to connect to Atlas and perform CRUD, aggregation, indexing, and other database operations. (MongoDB)

Useful official references

MongoDB Atlas on Microsoft Azure
MongoDB .NET/C# Driver documentation
.NET Driver – Get Started
MongoDB Atlas Driver Connection Guide


Don't Copy

Protected by Copyscape Online Plagiarism Checker