Enable Swagger and CROS in ASP.NET Core.

Swagger.

Swagger (OpenAPI) is a powerful tool that:

  • Generates interactive API documentation
  • Helps you test APIs right from the browser
  • Makes your API self-descriptive for frontend teams and third-party users

How to configure Swagger?

If not already installed, add the package:

dotnet add package Swashbuckle.AspNetCore

Add Swagger services before building the app:
Example:
var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer(); // For minimal API support
builder.Services.AddSwaggerGen();           // 👈 Adds Swagger generator

var app = builder.Build();

// Use middleware
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();                        // 👈 Enables Swagger JSON
    app.UseSwaggerUI();                      // 👈 Enables Swagger UI
}

app.UseAuthorization();
app.MapControllers();
app.Run();

You can enhance your Swagger UI by providing API details:
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My API",
        Version = "v1",
        Description = "This is a sample Web API using Swagger.",
        Contact = new OpenApiContact
        {
            Name = "Probin Sah",
            Email = "probin@example.com"
        }
    });
});

What is CORS?

CORS (Cross-Origin Resource Sharing) is a security feature implemented by browsers that restricts web apps from making requests to a different domain than the one that served the web page.

Why is it needed?
By default, browsers block cross-origin requests to protect users. For example:
  • If your Angular app runs on http://localhost:4200
  • And your API runs on http://localhost:5000
The browser blocks requests from the Angular app to the API unless CORS is explicitly enabled.

How To Enable CORS in ASP.NET Core?

Step 1: Add CORS Services in Program.cs
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowFrontendApp", policy =>
    {
        policy
            .WithOrigins("http://localhost:4200") // Frontend URL
            .AllowAnyMethod()
            .AllowAnyHeader();
    });
});

Step 2: Use CORS Middleware
Place it before UseAuthorization():
var app = builder.Build();

app.UseCors("AllowFrontendApp"); // 👈 Apply the policy

app.UseAuthorization();
app.MapControllers();

app.Run();
  • CORS allows your frontend (from another origin) to call your backend API.
  • Without it, browsers block cross-origin calls for security.
  • Use .WithOrigins, .AllowAnyMethod, .AllowAnyHeader to control access.

Logging in ASP.NET Core.

Logging is a critical part of any application for monitoring, debugging, and troubleshooting. ASP.NET Core comes with a flexible and powerful built-in logging framework that allows you to plug in various logging providers to send logs to different destinations.

In this article, you'll learn about the built-in logging providers in ASP.NET Core, how they work, and when to use each one.

What Is a Logging Provider?

A logging provider is a component that processes and stores log messages. ASP.NET Core allows you to configure one or more providers that can write logs to:
  • Console
  • Debug window
  • Event log (Windows)
  • Files
  • External systems (like Seq, ELK, or Application Insights)

You can configure logging providers in Program.cs:
var builder = WebApplication.CreateBuilder(args);

// Add built-in providers
builder.Logging.ClearProviders(); // Optional: clears default providers
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.AddEventSourceLogger();

var app = builder.Build();

You can also control log levels in appsettings.json:
"Logging": {
  "LogLevel": {
    "Default": "Information",
    "Microsoft": "Warning",
    "Microsoft.Hosting.Lifetime": "Information"
  }
}

What is ILogger?

ILogger<T> is ASP.NET Core’s built-in logging interface that allows you to log messages inside your classes (controllers, services, etc.).

It supports:
  • Different log levels (Information, Warning, Error, etc.)
  • Structured logging (log message + key-value pairs)
  • Logging to multiple providers (console, file, debug, etc.)

How to Use ILogger in ASP.NET Core

Step 1: Inject ILogger<T> in the Constructor.
public class ProductService
{
    private readonly ILogger<ProductService> _logger;

    public ProductService(ILogger<ProductService> logger)
    {
        _logger = logger;
    }

    public void GetProduct()
    {
        _logger.LogInformation("Fetching product...");
    }
}

Step 2: Register and Use the Service in the Controller.
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
    private readonly ProductService _service;

    public ProductController(ProductService service)
    {
        _service = service;
    }

    [HttpGet]
    public IActionResult Get()
    {
        _service.GetProduct();
        return Ok("Product fetched.");
    }
}
Note: ASP.NET Core automatically injects ILogger<T> using its built-in Dependency Injection.

You can log at different severity levels:
_logger.LogTrace("Trace log");
_logger.LogDebug("Debugging info");
_logger.LogInformation("General info");
_logger.LogWarning("Something might be wrong");
_logger.LogError("An error occurred");
_logger.LogCritical("Critical issue!");

Complete Example:
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;

    public OrderController(ILogger<OrderController> logger)
    {
        _logger = logger;
    }

    [HttpGet("{id}")]
    public IActionResult GetOrder(int id)
    {
        _logger.LogInformation("Fetching order with ID {OrderId}", id);

        if (id <= 0)
        {
            _logger.LogWarning("Invalid order ID: {OrderId}", id);
            return BadRequest("Invalid ID");
        }

        return Ok($"Order #{id}");
    }
}

How To Use Serilog in ASP.NET Core?

Serilog is a powerful logging library for .NET that supports structured logging, file output, sinks like Seq, and is easy to plug into ASP.NET Core.
Here’s a step-by-step example of using Serilog in an ASP.NET Core Web API project:

Step 1: Install Serilog NuGet Packages
Run these commands in your terminal:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File

Step 2: Configure Serilog in the Program.cs
Update your Program.cs to use Serilog before building the host:
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// 🔹 Configure Serilog before building the app
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .WriteTo.File("Logs/log-.txt", rollingInterval: RollingInterval.Day)
    .Enrich.FromLogContext()
    .CreateLogger();

// Replace default logger with Serilog
builder.Host.UseSerilog();

builder.Services.AddControllers();
var app = builder.Build();

app.UseAuthorization();
app.MapControllers();
app.Run();

Step 3: Inject and Use ILogger in a Controller
Serilog integrates with ILogger<T> automatically. No need to use Serilog types directly.
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
    private readonly ILogger<TestController> _logger;

    public TestController(ILogger<TestController> logger)
    {
        _logger = logger;
    }

    [HttpGet("{id}")]
    public IActionResult Get(int id)
    {
        _logger.LogInformation("Request received for ID: {Id}", id);

        if (id <= 0)
        {
            _logger.LogWarning("Invalid ID: {Id}", id);
            return BadRequest("Invalid ID");
        }

        return Ok($"Item #{id}");
    }
}

Output Example (in Console or File)
[10:30:12 INF] Request received for ID: 5
[10:30:14 WRN] Invalid ID: -1

Authentication and Authorization in ASP.NET Core

What is Authentication?

Authentication is the process of verifying who the user is.

It ensures the user’s identity is valid, typically through:

  • Username/Password
  • Tokens (e.g., JWT)
  • Cookies
  • External providers (Google, Facebook, Azure AD)

Example: If you log in using your email and password, that’s authentication. If your credentials are correct, the server "knows" who you are.

2. What is Authorization?

Authorization is the process of determining what an authenticated user is allowed to do.

After you are authenticated:

  • Are you allowed to access this endpoint?
  • Do you have the required role?
  • Do you have permission to perform a certain action?

What happens when the user logs in?

  • User submits credentials via login form or API (e.g., /login)
  • Backend validates the credentials (e.g., against a database)
  • If valid, the server issues an authentication token (JWT or sets a cookie).
  • On the next request, the client sends the token or cookie back
  • The middleware validates it and sets the User. Identity
  • Controllers/actions protected by [Authorize] now recognize the user

Example: JWT Authentication (API-Based)

Step 1. Add JWT Authentication Packages.

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Step 2: Configure Authentication in the Program.cs.
builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "your-app",
            ValidateAudience = true,
            ValidAudience = "your-client",
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("your-secret-key")),
            ValidateLifetime = true
        };
    });

builder.Services.AddAuthorization();

Then in middleware:
var app = builder.Build();

app.UseAuthentication();   // Must be before UseAuthorization
app.UseAuthorization();

app.MapControllers();

Step 3: Login Endpoint: Validate Credentials and Generate Token
[HttpPost("login")]
public IActionResult Login(LoginModel model)
{
    if (model.Username == "admin" && model.Password == "password") // DB check in real case
    {
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, model.Username),
            new Claim(ClaimTypes.Role, "Admin")
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: "your-app",
            audience: "your-client",
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: creds);

        return Ok(new
        {
            token = new JwtSecurityTokenHandler().WriteToken(token)
        });
    }

    return Unauthorized("Invalid credentials");
}

Step 4: Client Sends JWT in Authorization Header
On every request to a protected API:
GET /api/secure-data
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Step 5: Use [Authorize] to Protect Routes
[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData()
{
    var username = User.Identity?.Name;
    return Ok($"This data is only visible to {username}");
}

How does it work behind the Scenes?

Step 1: Authentication Middleware checks the incoming request
Step 2: Look for a valid Authorization header (with JWT)
Step 3: If valid:
  • It decodes the token
  • Sets HttpContext.User with claims and identity
Step 4: Then, [Authorize] uses this info to allow or deny access

[Authorize] → Requires the user to be authenticated
[AllowAnonymous] → Bypasses authorization for that method

JWT Token Structure.

A JWT token is made of 3 parts:
  • HEADER
  • PAYLOAD
  • SIGNATURE
1. HEADER
The Header contains metadata about the token:
{
  "alg": "HS256",   // Algorithm used (HMAC SHA-256)
  "typ": "JWT"      // Token type
}

2. PAYLOAD (this is where all your data is)
This is the main body of the JWT. It contains:
  • iss Issuer – who created the token
  • aud Audience – who the token is for
  • exp Expiration time (Unix timestamp)
  • nbf Not before – token valid after this time
  • iat Issued at time
  • sub Subject – usually the user ID
  • jti Token ID – unique identifier
Example:
{
  "iss": "JwtAuthDemo",
  "aud": "JwtAuthClient",
  "exp": 1720542515,
  "sub": "123456"
}

Custom Claims (You define these!)
These are the claims you add for user info, roles, etc.

Example:
{
  "name": "admin",
  "email": "admin@example.com",
  "role": "Admin",
  "department": "HR"
}

ASP.NET Core reads the role claim automatically for [Authorize(Roles = "...")].

3. SIGNATURE
Signature is used to validate the token’s integrity using your secret key.
It's computed like this:
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret-key
)

If someone tries to tamper with the payload, the signature won’t match anymore.

Note: JWT is not encrypted, just base64-encoded. Never store sensitive information like passwords in it.

Middleware in ASP.NET Core.

What is Middleware?

Middleware in ASP.NET Core is a software component that is executed on every HTTP request and response. Middleware components are chained together to form a request-processing pipeline.

Each middleware:
  • Can perform operations before and after the next component in the pipeline
  • Can short-circuit the pipeline (e.g., for authentication, error handling)
  • Is executed in the order registered in the Program.cs

Middleware Examples
ASP.NET Core has built-in middleware such as:
  • UseRouting() – for routing requests
  • UseAuthentication() – for checking identity
  • UseAuthorization() – for permission control
  • UseEndpoints() – for mapping controllers
Middleware executes in the order added — request goes top to bottom, response goes bottom to top.

Request Pipeline Flow
Here’s how a typical request moves through middleware:
Request
  
[Middleware 1]  [Middleware 2]  ...  [Middleware N]  Controller
                                                  
Response                     

Each middleware has a chance to:
  • Do something before passing the request forward
  • Do something after the next middleware has completed

How to Create a Custom Middleware.

Step 1: Create a class with a constructor accepting RequestDelegate
Step 2: Add InvokeAsync(HttpContext) method
Step 3: Call _next(context) inside the method
Step 4: Register it using app.UseMiddleware<YourMiddleware>()

Step 1: Create a Middleware Class
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Incoming request: {context.Request.Method} {context.Request.Path}");
        await _next(context);
        _logger.LogInformation($"Response: {context.Response.StatusCode}");
    }
}

Step 2: To keep Program.cs clean, create extension methods for your middleware.
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder app)
    {
        return app.UseMiddleware<LoggingMiddleware>();
    }
}

Step 3: Program.cs (or Startup.cs in older projects)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseLoggingMiddleware(); // 👈 From your extension method

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

📁 Recommended Middleware File Structure.

/YourProjectRoot

├── Program.cs
├── Startup.cs (if applicable)

├── Middleware/
   ├── LoggingMiddleware.cs
   ├── ExceptionHandlingMiddleware.cs
   └── RequestTimingMiddleware.cs

└── Extensions/
    └── MiddlewareExtensions.cs

When to Use Custom Middleware?

You might create custom middleware to:
  • Log requests and responses
  • Handle exceptions globally
  • Modify headers
  • Implement custom authentication or IP filtering
  • Measure performance/response time

Typical Middleware Order in Program.cs

Here is the recommended and logical order of middleware in most real-world ASP.NET Core applications:
var app = builder.Build();

app.UseExceptionHandler("/error");         // 1. Global exception handling
app.UseHsts();                             // 2. Enforce HTTPS strict transport (in production)
app.UseHttpsRedirection();                 // 3. Redirect HTTP to HTTPS
app.UseStaticFiles();                      // 4. Serve static files (JS, CSS, images)
app.UseRouting();                          // 5. Enable routing system
app.UseCors();                             // 6. Enable CORS if needed
app.UseAuthentication();                   // 7. Identity/authentication check
app.UseAuthorization();                    // 8. Role-based or policy-based authorization
app.UseMiddleware<CustomLoggingMiddleware>(); // 9. Your custom middlewares
app.UseEndpoints(endpoints =>              // 10. Connects routes to controllers
{
    endpoints.MapControllers();
});

app.Run();

Common Middleware Interview Question.

Q1. What is the difference between Use, Run, and Map?
  • Use: Adds middleware to the pipeline, can call next
  • Run: Terminal middleware, doesn't call next
  • Map: Branches pipeline based on request path

Q2. Can a middleware return a response without calling the next middleware?
Answer Tip: Yes. This is called short-circuiting. For example, authentication or CORS middleware might return a response early.

Q3. What happens if you don't call _next(context) in a middleware?
Answer Tip: The pipeline is terminated, and the request doesn't reach subsequent middleware or the controller.

Q4. How do you handle exceptions globally in middleware?
Answer: Use a custom exception-handling middleware or app.UseExceptionHandler().

LINQ In Entity Framework Core.

Entity Framework Core (EF Core) is a powerful Object-Relational Mapper (ORM) for .NET that allows developers to interact with databases using .NET objects. One of EF Core’s most important and powerful features is LINQ (Language Integrated Query) — a feature that allows you to write queries directly in C# using intuitive syntax.

Whether you're working on a Web API, MVC application, or a console app, LINQ in EF Core makes data querying clean, efficient, and maintainable.

What is LINQ?

LINQ (Language Integrated Query) allows you to query collections (like arrays, lists, and database sets) using C# syntax. With EF Core, LINQ becomes the primary way to interact with your database.

Instead of writing raw SQL queries, you use C# expressions to retrieve, filter, and project data from the database.

Benefits of Using LINQ in EF Core

  • Type-safe queries
  • IntelliSense support in IDEs
  • Compile-time checking
  • Improved maintainability
  • No need for raw SQL


Types of LINQ Queries

EF Core supports two styles of LINQ:

1. Method Syntax (most common)
var products = await _context.Products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Name)
    .ToListAsync();

2. Query Syntax
var products = await (from p in _context.Products
                      where p.Price > 100
                      orderby p.Name
                      select p).ToListAsync();

Both styles generate the same SQL under the hood.

Common LINQ Query Examples in EF Core.

1. Get All Records:
var products = await _context.Products.ToListAsync();

2. Filter Records (WHERE)
var expensive = await _context.Products
    .Where(p => p.Price > 500)
    .ToListAsync();

3. Select Specific Fields (SELECT)
var names = await _context.Products
    .Select(p => p.Name)
    .ToListAsync();

4. Sort Records (ORDER BY)
var sorted = await _context.Products
    .OrderByDescending(p => p.Price)
    .ToListAsync();

5. Get Single Record
var product = await _context.Products
    .FirstOrDefaultAsync(p => p.Id == 1);

6. Check Existence (ANY)
bool exists = await _context.Products.AnyAsync(p => p.Price > 1000);

7. Count Records
int count = await _context.Products.CountAsync();

8. Join With Another Table
var orders = await (from o in _context.Orders
                    join c in _context.Customers on o.CustomerId equals c.Id
                    select new { o.Id, c.Name }).ToListAsync();

Asynchronous Queries in LINQ

EF Core supports async versions of most LINQ methods, like:
  • ToListAsync()
  • FirstOrDefaultAsync()
  • AnyAsync()
  • CountAsync()
These async methods are essential in ASP.NET Core to avoid thread blocking and improve scalability.

Performance Tips for LINQ.

  • Use `AsNoTracking()` for read-only: Improves performance, skips tracking overhead
  • Use `Select()` to limit columns: Reduces DB load and memory usage       
  • Avoid multiple `.ToListAsync()`: Combine filters before executing the query
  • Use `AnyAsync()` instead of `Count()`: Faster existence check

Why Use .Include()?

By default, EF Core uses lazy loading, which means related data is not loaded unless explicitly accessed, and that can cause N+1 query problems.

.Include() solves this by retrieving the main entity and its related entities in one go.

Example Scenario
Suppose you have these two related entities:
var products = await _context.Products
    .Include(p => p.Category)
    .ToListAsync();

foreach (var product in products)
{
    Console.WriteLine($"Product: {product.Name}, Category: {product.Category.Name}");
}

This performs a JOIN behind the scenes and loads both the Product and its related Category.

You can use .ThenInclude() to include deeper navigation:
var products = await _context.Products
    .Include(p => p.Category)
        .ThenInclude(c => c.Supplier)
    .ToListAsync();

Common Pitfalls
  • Forget to use await with async methods: Leads to unexecuted queries or runtime bugs.
  • Loading too much data: Avoid pulling unnecessary columns or rows.
  • N+1 queries: Use .Include() to load related data when needed.

Pagination is an essential technique in web development to divide large sets of data into manageable chunks (pages) so you don’t overload the server or the user interface. Let's understand this in detail:

What is Pagination?

Pagination is the process of retrieving a specific subset of records (usually a page of data) from a larger data source. Instead of fetching all records at once, you fetch only what is needed for the current page.

Example: Imagine you have 10,000 products in your database. You don’t want to load them all at once, so you display 10 or 20 products per page:

Key Parameters in Pagination

  • PageNumber: The current page number (e.g., 1, 2, 3…)
  • PageSize: Number of records to show per page (e.g., 10, 20)
  • Skip: Number of records to skip: (PageNumber - 1) * PageSize
  • Take: Number of records to fetch: PageSize

Example in EF Core (LINQ): Let's say you want Page 2 with 5 items per page:
[HttpGet]
public async Task<IActionResult> GetPagedProducts(int pageNumber = 1, int pageSize = 10)
{
    var totalCount = await _context.Products.CountAsync();

    var products = await _context.Products
        .OrderBy(p => p.Id)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    var result = new
    {
        TotalCount = totalCount,
        PageSize = pageSize,
        CurrentPage = pageNumber,
        TotalPages = (int)Math.Ceiling((double)totalCount / pageSize),
        Data = products
    };

    return Ok(result);
}

This generates a SQL query that only returns 5 records, starting from the 6th product (ID 6–10).

Using LINQ in EF Core makes your database logic clean, expressive, and maintainable — a key advantage in modern ASP.NET Core development.

Entity Framework Core (EF Core).

Entity Framework Core (EF Core) is a modern, lightweight, and extensible version of the Entity Framework, which is an Object-Relational Mapper (ORM) for .NET applications. It allows developers to work with databases using .NET objects, eliminating the need for most of the data-access code that developers usually need to write. Below, we will explore the key concepts of EF Core, including the Code-First approach, DbContext and DbSet, migrations, the repository pattern and unit of work, and LINQ queries with async operations.

What is Entity Framework Core?

Entity Framework Core (EF Core) is a modern, lightweight, open-source Object-Relational Mapper (ORM) for .NET. It allows developers to interact with databases using C# objects, eliminating the need to write most raw SQL queries.

🧠 Think of EF Core as a bridge between your C# code and the SQL database.

Key Features of EF Core

  • Cross-platform: Works on Windows, Linux, macOS
  • LINQ support: Query databases using C# syntax
  • Change tracking: Tracks changes in objects to update the DB
  • Migrations: Manage schema changes via code
  • Lazy/Eager/Explicit Loading: Controls how related data is loaded
  • Concurrency Handling: Manages data conflicts in multi-user apps

There are two types of Entity Framework Core approaches that we follow to interact with the database.
  • Database First Approach.
  • Code First Approach.

What is the Code-First Approach?

In the Code-First approach, you define your database schema using C# classes, and EF Core generates the database from this code. You don’t need an existing database instead you start with code, then create and update the database using migrations.

How Code-First Works:

Step 1: Create C# classes to represent tables (called entities).
Step 2: Create a DbContext class to manage the database connection and sets.
Step 3: Configure EF Core in Program.cs.
Step 4: Use Migrations to generate the schema and apply it to the database.

Step-by-Step Example of Code-First Approach.

Step 1. Install EF Core NuGet Packages.

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Step 2. Define Your Entity (Model)
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class Product
{
    [Key]                                // Defines primary key
    public int Id { get; set; }

    [Required]                           // NOT NULL
    [MaxLength(100)]                     // Max length for Name
    public string Name { get; set; }

    [Column(TypeName = "decimal(10,2)")] // Define precision for Price
    [Range(0, 99999.99)]                 // Validation range
    public decimal Price { get; set; }

    [Required]
    [DefaultValue(true)]                 // Default value
    public bool IsAvailable { get; set; } = true;
}

In Entity Framework Core, you can define constraints using Data Annotations (attributes on properties).

Step 3: Create a DbContext.
public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }
}

This is your custom database context class, and it's a key part of using Entity Framework Core (EF Core) with the Code-First approach.
EF Core uses this class to:
  • Configure the connection to the database
  • Map your C# models (like Product) to database tables
  • Track changes and execute queries
  • Save data to the database
DbContext is the core class in EF Core that manages all database operations (querying, saving, updating, etc.).

DbSet<Product> tells EF Core: “I want a table called Products in the database, and each row will be a Product object.”

Step 4: Register the DbContext in Program.cs.
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
And in appSettings.json
"ConnectionStrings": {
  "DefaultConnection": "Server=.;Database=EFCoreDemo;Trusted_Connection=True;"
}
Here we are defining a connection string that will help us connect to our database locally or remotely.

Step 5: Add and Apply Migrations.
dotnet ef migrations add InitialCreate
dotnet ef database update

EF Core will:
  • Create a Migrations folder
  • Generate SQL commands
  • Apply them to your database
Step 6. Use the DbContext to Perform CRUD
public class ProductService
{
    private readonly AppDbContext _context;
    
    public ProductService(AppDbContext context)
    {
        _context = context;
    }

    public async Task AddProduct()
    {
        var product = new Product { Name = "Laptop", Price = 1500 };
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
    }
}

EF Core will keep your code and database in sync using migrations, making your development fast, clean, and flexible.

What is the Database-First Approach?

In the Database-First approach, you start with an existing database, and Entity Framework Core generates the C# classes (models and DbContext) based on that database schema.

When to Use It?

  • You already have a pre-existing database
  • You're working with legacy systems
  • You want your C# models to match a database that someone else designed

Step-by-Step Example of Database-First Approach:

Step 1: Install Required NuGet Packages.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Step 2: Run the Scaffold Command
Use the following CLI command to reverse engineer your DB:
dotnet ef dbcontext scaffold "Your_Connection_String" Microsoft.EntityFrameworkCore.SqlServer -o Models

Explanation:
  • "Your_Connection_String" – The connection string to your existing DB
  • Microsoft.EntityFrameworkCore.SqlServer – The database provider
  • -o Models – Output directory for generated models and DbContext
What It Generates:
  • DbContext file → manages the connection to the DB
  • Model classes for each table → mapped to DB tables

What is the Repository Pattern?

The Repository Pattern acts as a mediator between the business logic and the data access layer. It hides the details of data access and provides a simple and consistent API for performing database operations.

Benefits:
  • Encapsulates data access logic
  • Promotes loose coupling
  • Makes unit testing easier
  • Clean separation of concerns

Dependancy Injection and Its Lifetime in ASP.NET Core.

Dependency Injection (DI) is a fundamental concept in ASP.NET Core. It allows you to write loosely coupled, testable, and maintainable code by injecting the required dependencies into your classes instead of hard-coding them. ASP.NET Core has a built-in DI container that supports constructor injection and handles object lifetimes efficiently.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern used to achieve Inversion of Control (IoC) between classes and their dependencies. Rather than a class creating its own dependencies, they are provided from outside, typically via the constructor.

📌 In simple terms: A class doesn’t create what it needs — it receives it from someone else.

Use of Dependency Injection:

  • Promotes loose coupling
  • Makes code testable
  • Supports the separation of concerns
  • Encourages interface-based programming

Step-by-Step Implementation of Dependency Injection:

Step 1: Create a New ASP.NET Core Project: You can create a new project using the .NET CLI or Visual Studio.

dotnet new webapi -n MyApi
cd MyApi

Step 2: Define a Service Interface: Create an interface that defines the contract for the service.
public interface IProductService
{
   IEnumerable<string> GetProducts();
}
   

Step 3: Implement the Service: Create a class that implements the service interface.
   public class ProductService : IProductService
   {
       public IEnumerable<string> GetProducts()
       {
           return new List<string> { "Product1", "Product2", "Product3" };
       }
   }
   

Step 4: Registering Services in the Dependency Injection Container.
   var builder = WebApplication.CreateBuilder(args);

   // Register the ProductService with the DI container
   builder.Services.AddScoped<IProductService, ProductService>();

   var app = builder.Build();
   

Step 5: Injecting Dependencies into Controllers
   using Microsoft.AspNetCore.Mvc;

   [ApiController]
   [Route("api/[controller]")]
   public class ProductsController : ControllerBase
   {
       private readonly IProductService _productService;

       // Constructor injection
       public ProductsController(IProductService productService)
       {
           _productService = productService;
       }

       [HttpGet]
       public IActionResult Get()
       {
           var products = _productService.GetProducts();
           return Ok(products);
       }
   }
   

Step 6: Run the Solution.

Service Lifetimes in ASP.NET Core.

In ASP.NET Core, when you register a service in the Dependency Injection (DI) container, you must specify its lifetime. This tells the framework how long to keep the object in memory and how often to create a new one.

ASP.NET Core provides three service lifetimes:
  • Singleton
  • Scoped
  • Transient
Singleton: A single instance is created once and shared throughout the application's entire lifetime.
Use When:
  • The service is stateless and thread-safe
  • You want to cache or reuse resources
  • Examples: Logging, Configuration, In-memory cache

Scoped: A new instance is created per HTTP request. The same instance is reused within that request.
Use When:
  • You want to maintain state only during a single request
  • You’re working with Entity Framework Core DbContext

Transient: A new instance is created every time the service is requested (even multiple times within the same request).
Use When:
  • Service is lightweight and stateless
  • You need fresh data or processing logic every time
Example:
builder.Services.AddSingleton<ISingletonService, MyService>();
builder.Services.AddScoped<IScopedService, MyService>();
builder.Services.AddTransient<ITransientService, MyService>();

Example: Unit Testing with DI.
var mockService = new Mock<IProductService>();
mockService.Setup(x => x.GetAll()).Returns(new[] { "Test Product" });

var controller = new ProductsController(mockService.Object);

var result = controller.GetAll();

Thanks to DI and interface-based design, testing is simple and clean.

Dependency Injection in ASP.NET Core is not just a feature — it’s a core part of the framework. By registering services properly, choosing the right lifetimes, and following interface-based design, you can build robust, maintainable, and testable web applications with ease.

DON'T MISS

Nature, Health, Fitness
© all rights reserved
made with by AlgoLesson