Database

DBMS

ANGULAR

Angular

DIFFERENCE

Difference

VIDEO

Videos

How To Implement Logging in ASP.NET Core.

Logging is the process of recording application events so you can:

  • Debug issues
  • Monitor behavior
  • Trace requests
  • Audit actions in production

Without proper logging:

  • Bugs are hard to reproduce
  • Production failures are invisible
  • Root cause analysis becomes guesswork

ASP.NET Core has ILogger built in via Microsoft.Extensions.Logging. Supported Providers (by default)
  • Console
  • Debug
  • EventSource
  • Application Insights (Azure)
Rule: Never log everything as Information.

How To Use ILogger?

Step 1: Inject ILogger.
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;

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

Step 2: Log Messages
_logger.LogInformation("Order creation started");

_logger.LogWarning("Order {OrderId} has no items", orderId);

_logger.LogError(exception, "Failed to create order {OrderId}", orderId);

Good Option 1: Logging Exception Correctly.
catch (Exception ex)
{
    _logger.LogError(ex, "Error while processing order {OrderId}", orderId);
    throw;
}
👉 Always pass the exception object to preserve stthe ack trace.

Good Option 2: Logging in Middleware.
public async Task InvokeAsync(HttpContext context)
{
    _logger.LogInformation(
        "Request {Method} {Path}",
        context.Request.Method,
        context.Request.Path);

    await _next(context);

    _logger.LogInformation(
        "Response {StatusCode}",
        context.Response.StatusCode);
}

Built-in logging is good, but Serilog is preferred in production because it provides:
  • Rich structured logging
  • Multiple sinks (File, DB, Seq, Elastic)
  • Better performance & flexibility
Step 1: Install Packages
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File

Step 2: Configure Serilog in Program.cs
using Serilog;

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("Logs/app-.log", rollingInterval: RollingInterval.Day)
    .CreateLogger();

builder.Host.UseSerilog();

Step 3: Use ILogger as Usual.
_logger.LogInformation("Invoice {InvoiceId} generated", invoiceId);
You still use ILoggerSerilog works behind the scenes.

Interview Answer: In ASP.NET Core, I use ILogger for application logging and Serilog for structured, production-grade logging. I follow proper log levels, structured messages, correlation IDs, and centralized log storage to ensure observability and easy debugging.

Implement Custom Global Exception Handling in ASP.NET Core.

Exception handling is one of the most important non-functional requirements (NFRs) in any backend application. In ASP.NET Core, poor exception handling leads to:
  • Application crashes
  • Inconsistent error responses
  • Security risks (stack traces exposed)
  • Difficult debugging and monitoring
To solve this, ASP.NET Core promotes Global Exception Handling, where all unhandled exceptions are caught at a single place, logged, and converted into a consistent HTTP response.

This article explains:
  • Why is global exception handling needed
  • How to implement a custom global exception middleware
  • Best practices used in production systems

Why Do We Need Global Exception Handling?

Before understanding Global Exception, we need to know what an exception is and why we need to handle it properly.

An exception is a runtime error that occurs when the application cannot continue normal execution.
Examples:
  • Database connection failure
  • Null reference access
  • Invalid user input
  • External API timeout

If exceptions are not handled properly, they can:
  • Crash the application
  • Leak sensitive information
  • Return inconsistent responses
  • Make debugging extremely difficult
Global Exception Handling is a centralized mechanism that:
  • Catches all unhandled exceptions in one place
  • Logs them consistently
  • Converts them into a standard HTTP response
  • Prevents sensitive details from reaching clients
Instead of handling errors locally in every controller or method, the application handles them globally.

Let's understand the benefits of Global Exception Handling in detail with an example:

Example 1: Error Message Without Global Exception Handler.
{
  "error": "Object reference not set to an instance of an object"
}

Worse Case:
System.NullReferenceException at OrderService.cs line 42
Issues
  • Internal code details exposed
  • Frontend doesn’t know how to handle different formats
  • Logs may be missing or incomplete
Example 2: Error Message With Global Exception Handler.
{
  "statusCode": 400,
  "message": "Invalid request",
  "traceId": "abc123"
}
APIs always return a consistent response.

Imagine a building security desk:
  • Without global handling → every room handles its own security
  • With global handling → one central security desk handles all issues
This is exactly how Global Exception Handling works.
Interview Answer: Global exception handling is needed to centrally catch all unhandled exceptions, return consistent and secure error responses, improve maintainability, and enable reliable logging without duplicating try–catch blocks across the application.

 How To Handle Exceptions in ASP.NET Core?

ASP.NET Core provides multiple ways to handle exceptions, but not all are equal. A senior .NET developer is expected to choose the right approach based on maintainability, security, and scalability.

1. Try-Catch Block in Controller: Handling exceptions inside each controller action using try–catch.

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    try
    {
        var product = _service.GetProduct(id);
        return Ok(product);
    }
    catch (Exception ex)
    {
        return StatusCode(500, ex.Message);
    }
}
Problem:
  • Code duplication: try–catch in every action
  • Inconsistent responses: Different error formats
  • Security risk: Exception messages exposed
  • Poor separation: Business + error handling mixed
  • Hard to maintain: Changes needed everywhere

2. UseExceptionHandler() Middleware: Built-in middleware provided by ASP.NET Core for basic global exception handling.
app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        context.Response.StatusCode = 500;
        await context.Response.WriteAsync("Something went wrong");
    });
});
Problem:
  • Limited customization
  • No exception type mapping
  • No structured response
  • Not ideal for APIs
3. Custom Exception Middleware: A custom middleware that intercepts all unhandled exceptions, logs them, and returns a standard response.

Step 1: Create Custom Exceptions.
public class NotFoundException : Exception
{
    public NotFoundException(string message) : base(message) { }
}

Step 2: Create Error Response Model.
public class ErrorResponse
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    public string TraceId { get; set; }
}

Step 3: Create Middleware
public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception");

            int statusCode = ex switch
            {
                NotFoundException => 404,
                _ => 500
            };

            var response = new ErrorResponse
            {
                StatusCode = statusCode,
                Message = statusCode == 500 ? "Internal error" : ex.Message,
                TraceId = context.TraceIdentifier
            };

            context.Response.StatusCode = statusCode;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(response);
        }
    }
}

Step 4: Register Middleware
app.UseMiddleware<GlobalExceptionMiddleware>();

4. IExceptionHandler: A new interface-based global exception handling mechanism introduced in .NET 7.

Step 1: Step 1: Implement IExceptionHandler.
public class GlobalExceptionHandler : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(
        HttpContext context,
        Exception exception,
        CancellationToken cancellationToken)
    {
        context.Response.StatusCode = exception switch
        {
            NotFoundException => 404,
            _ => 500
        };

        await context.Response.WriteAsJsonAsync(new
        {
            message = "Error occurred",
            traceId = context.TraceIdentifier
        }, cancellationToken);

        return true;
    }
}

Step 2: Register Service.
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

Step 3: Enable Middleware.
app.UseExceptionHandler();

Benefits:
  • Clean & structured
  • Official Microsoft approach
  • Easy integration with ProblemDetails
  • Less boilerplate
Interview Answer: For production-grade ASP.NET Core APIs, I prefer custom global exception middleware because it gives full control over exception mapping, logging, and response structure. For newer .NET versions, IExceptionHandler is also a clean and modern alternative.

Observer Design Pattern in C#.

In modern software applications, it’s common to have multiple components that need to react whenever something changes, like a weather display updating when the temperature changes, or different modules getting notified when an order is placed. Managing these updates manually quickly becomes messy and tightly coupled. This is where the Observer Design Pattern shines. It provides a clean, event-driven way for one object to notify many others automatically, making applications flexible, maintainable, and scalable.

What is the Observer Design Pattern?

The Observer Design Pattern is a behavioral design pattern used when you need one object (the Subject) to automatically notify other objects (Observers) of changes to its state.

This creates a one-to-many relationship between objects, where the Subject keeps a list of its Observers and notifies them whenever something changes.

Simple Words: Observer Pattern allows an object to publish events and multiple subscribers (observers) to react automatically.

When Do We Need the Observer Design Pattern?

Without the Observer Pattern:

  • You would manually call update functions for all dependent objects.
  • Code becomes tightly coupled.
  • Adding or removing listeners requires modifying the Subject class.
  • Any change breaks the Open/Closed Principle.

With Observer Pattern:

  • The subject doesn’t need to know who is observing.
  • Observers subscribe/unsubscribe on their own.
  • Adding new observers requires zero changes in the Subject.
  • It's event-driven and loosely coupled.
Real-Life Analogy: Think of it like a YouTube channel is a subject, and Subscribers are Observers. Whenever a channel uploads a new video, the YouTube channel notifies all your subscribers automatically. The channel doesn’t care how many people subscribed or who they are. Subscribers get updates based on their interests. 
This is exactly how the Observer Pattern works.

Observer Design Pattern

Observer Design Pattern Example.

Here is a C# example demonstrating the Observer pattern:
namespace PracticeCode.DesignPattern
{
    //Observer Interface
    public interface IObserver
    {
        void Update(float temperature);
    }
    //Subject Interface
    public interface ISubject
    {
        void RegisterObserver(IObserver observer);
        void RemoveObserver(IObserver observer);
        void NotifyObserver();
    }

    //Concrete Subject – Weather Station
    public class WeatherStation : ISubject
    {
        private List<IObserver> observers = new();
        private float temperature;

        public void RegisterObserver(IObserver observer)
        {
            observers.Add(observer);
        }
        public void RemoveObserver(IObserver observer)
        {
            observers.Remove(observer);
        }
        public void NotifyObserver()
        {
            foreach(var observer in observers)
            {
                observer.Update(temperature);
            }
        }

        //When temp change notify everyone
        public void SetTemperature(float newTemp)
        {
            Console.WriteLine($"\nWeatherStation: New Temperature = {newTemp}°C");
            temperature = newTemp;
            NotifyObserver();
        }
    }
    //Concrete Observers – Displays
    public class DigitalDisplay : IObserver
    {
        public void Update(float temperature)
        {
            Console.WriteLine($"Digital Display -> Updated Temperature: {temperature}°C");
        }
    }
    public class MobileDisplay : IObserver
    {
        public void Update(float temperature)
        {
            Console.WriteLine($"Mobile Display -> Updated Temperature: {temperature}");
        }
    }
}

Client Code in Program.cs, where the new observer is getting registered and getting notified whenever there is an update in temperature.
//Observers Subscribe
station.RegisterObserver(digital);
station.RegisterObserver(mobile);

//Observers Notify
station.SetTemperature(29.4f);
station.SetTemperature(30.2f);

//Observer Removed
station.RemoveObserver(digital);

//Observer Notify
station.SetTemperature(32.0f);
Output:
WeatherStation: New Temperature = 28.5°C
Digital DisplayUpdated Temperature: 28.5°C
Mobile AppTemperature Alert: 28.5°C

WeatherStation: New Temperature = 30.2°C
Digital DisplayUpdated Temperature: 30.2°C
Mobile AppTemperature Alert: 30.2°C

WeatherStation: New Temperature = 31.7°C
Digital DisplayUpdated Temperature: 31.7°C

This is one small example of how an Observer Design Pattern helps you write better and maintainable code, and in which all real-life conditions in which you can use this pattern.

These are some key points that you can keep in mind while writing an Observer Design Pattern Code:
  • Type: Behavioral
  • Purpose: Notify multiple objects automatically when one object changes.
  • Relationship: One-to-many
  • Helps With: Loose coupling, event-driven architecture
  • Key Methods: Register, Remove, Notify
  • Real Use Cases: Events, UI updates, stock market tickers, notifications
In Short, the Observer Pattern lets you build a system where one object publishes updates and many other objects automatically react to those updates.

Factory Method Pattern in C#.

The Factory Design Pattern in C# is a creational design pattern that provides an interface for creating objects in a superclass while allowing subclasses to alter the types of objects that are created. This pattern encapsulates object creation logic, decoupling it from the client code that uses the objects.

It is beneficial for situations requiring flexibility, such as adding new product types without modifying existing client code.

When to use the Factory pattern?

  • Uncertainty about object type: When a class cannot predict the type of objects it needs to create, and this decision is better made at runtime.
  • Subclasses decide instantiation: When you want to delegate the responsibility of creating objects to subclasses, allowing them to alter the type of objects created.
  • Encapsulating creation logic: When the process of creating an object is complex, repetitive, or has many dependencies, the factory pattern can encapsulate this logic, making the code cleaner.
  • Extending with new products: When you want to easily add new product types in the future. Instead of modifying the client code, you can simply create a new subclass and implement its creation logic within the factory.
  • Decoupling client from concrete classes: To decouple the client code from the concrete classes it instantiates. The client only interacts with the factory and a common interface, not the specific implementations.
Let's understand a real-life problem and how we can fix that using the Factory Method.

Bad Example: Notification System.

Your application is using a Notification System to send notifications to users. Initially, you were sending only an Email Notification, so it was simple and easy. Later, you added SMS and a Push notification system. You have written if-else conditions to create different kinds of objects based on user requirements.

Example Code:
if (notification == "Email") new EmailNotification();
else if (notification == "SMS") new SMSNotification();
else if (notification == "PUSH") new PUSHNotification();

This is a bad approach to writing code because adding a new payment method means changing existing code, → violates the Open/Closed Principle. This code is also difficult to test and tightly coupled.

You might feel like you are writing too much code for a simple task, but in real-world, large and complex projects, the Factory Method Pattern helps you maintain, extend, and scale your application without modifying existing code.


Good Example: Notification System Using Factory Method.

fff
Step 1: Notification Interface/Abstract Class: Defines the common interface or abstract class for the objects that the factory will create. This ensures that all concrete products share a common contract.
public interface INotification
{
    void Send(string to, string message);
}

Step 2: Concrete Product Classes: Implement the INotification interface or inherit from the abstract product class, providing specific implementations.
public class EmailNotification : INotification
{
    public void Send(string to, string message)
    {
        Console.WriteLine($"Sending Email to {to} : {message}");
    }
}
public class SMSNotification : INotification
{
    public void Send(string to, string message)
    {
        Console.WriteLine($"Sending SMS to {to} : {message}");
    }
}
public class PUSHNotification : INotification
{
    public void Send(string to, string message)
    {
        Console.WriteLine($"Sending PUSH to {to} : {message}");
    }
}

Step 3: Creator/Factory Class (Abstract or Concrete): Declares the factory method, which returns an object of the INotification type. This method can be abstract, forcing subclasses to implement it, or it can provide a default implementation.
public abstract class NotificationFactory
{
    public abstract INotification CreateNotification();
}

Step 4: Concrete Creator/Factory Classes: Override the factory method to return instances of specific concrete product classes.
public class EmailFactory : NotificationFactory
{
    public override INotification CreateNotification()
    {
        return new EmailNotification();
    }
}
public class SMSFactory : NotificationFactory
{
    public override INotification CreateNotification()
    {
        return new SMSNotification();
    }
}
public class PUSHFactory : NotificationFactory
{
    public override INotification CreateNotification()
    {
        return new PUSHNotification();
    }
}
Client using the Factory:
NotificationFactory factory;
factory = new EmailFactory();
var email = factory.CreateNotification();
email.Send("user@example.com", "Welcome Email!");
I hope you now understand how and when to use the Factory Method Design Pattern.

The  Factory Method Pattern lets subclasses decide which object to create, helping you remove direct new calls, simplify object creation, and extend your application without modifying existing code.

How To Add Font Awesome in Angular?

Angular Font Awesome

Icons play a crucial role in building modern and user-friendly web applications. Font Awesome is one of the most popular icon libraries, and Angular provides multiple clean ways to integrate it.

In this article, you’ll learn all the correct ways to add Font Awesome in Angular, when to use each approach, and common mistakes to avoid.

Prerequisites

Before starting, make sure:

  • The Angular project is already created
  • Node.js and npm are installed
  • Basic Angular knowledge (components & templates)

Method 1: Add Font Awesome Using CDN.

Font Awesome CDN method works by loading Font Awesome’s CSS file directly from the internet into your Angular app. This is the fastest method and best for:
  • Demos
  • Small projects
  • Prototypes
Step 1: Add Font Awesome CDN to the index.html page in Angular.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>AngularApp</title>

  <!-- Font Awesome CDN -->
  <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
  />
</head>
<body>
  <app-root></app-root>
</body>
</html>

Step 2: Use Icons in Component HTML.
<i class="fa-solid fa-user"></i>
<i class="fa-solid fa-envelope"></i>
<i class="fa-solid fa-lock"></i>

Screenshot of font awesome icons
Pros
  • No installation required
  • Very easy setup
Cons
  • External dependency
  • Not ideal for production or offline builds

Method 2: Install Font Awesome via npm.

This is the most commonly used and professional approach.

Step 1: Install Font Awesome. 
npm install @fortawesome/fontawesome-free

Step 2: Add CSS to angular.json
"styles": [
  "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
  "src/styles.css"
]

Note: Always add Font Awesome before your custom styles.

Step 3: Restart the Angular Server.
ng serve

Step 4: Use Icons in Template.
<i class="fa-solid fa-user"></i>
<i class="fa-regular fa-bell"></i>
<i class="fa-brands fa-github"></i>

screenshot for font awesome
Pros
  • Works offline
  • Version-controlled
  • Production-ready
Cons
  • Loads entire icon set (slightly larger bundle)
Adding Font Awesome in Angular is simple once you understand which approach fits your use case.
Start with npm-based CSS, and move to Angular Font Awesome for scalable applications.

What is CTE in SQL?

CTE (Common Table Expression) is a temporary named result set that you can use inside a single SQL statement to make complex queries more readable, reusable, and maintainable.

A CTE is defined using the WITH keyword.

We use CTE:

  • To simplify complex queries
  • To avoid repeating subqueries
  • To handle hierarchical or recursive data
  • To improve query readability.
Basic Syntax:
WITH cte_name AS (
    SELECT columns
    FROM table
    WHERE condition
)
SELECT *
FROM cte_name;

Simple Example: Find employees earning more than the average salary
WITH AvgSalaryCTE AS (
    SELECT AVG(Salary) AS AvgSalary
    FROM Employee
)
SELECT *
FROM Employee
WHERE Salary > (SELECT AvgSalary FROM AvgSalaryCTE);
Explanation:
  • AvgSalaryCTE calculates the average salary.
  • The main query uses that result to filter employees.
  • CTE exists only during this query execution.

How is CTE better than a subquery?

A CTE (Common Table Expression) is often better than a subquery because it improves readability, reusability, and maintainability, especially in complex SQL queries.

Example: Using Subquery.
SELECT *
FROM Employee
WHERE Salary > (
    SELECT AVG(Salary)
    FROM Employee
);

Example: Using CTE.
WITH AvgSalaryCTE AS (
    SELECT AVG(Salary) AS AvgSalary
    FROM Employee
)
SELECT *
FROM Employee
WHERE Salary > (SELECT AvgSalary FROM AvgSalaryCTE);

Note: CTE separates logic into named blocks, making queries easier to understand.

Recursive CTE.

A Recursive CTE (Common Table Expression) is a CTE that refers to itself to process hierarchical or tree-structured data (parent → child relationships).

It repeatedly executes until no more rows are returned.

Example: Employee–Manager Hierarchy.
WITH EmpHierarchy AS (
    -- Anchor Query (Top-Level Manager)
    SELECT 
        EmpId,
        Name,
        ManagerId,
        0 AS Level
    FROM Employee
    WHERE ManagerId IS NULL

    UNION ALL

    -- Recursive Query (Subordinates)
    SELECT 
        e.EmpId,
        e.Name,
        e.ManagerId,
        h.Level + 1
    FROM Employee e
    INNER JOIN EmpHierarchy h
        ON e.ManagerId = h.EmpId
)
SELECT * FROM EmpHierarchy;
Output:
EmpId | Name   | ManagerId | Level
1     | CEO    | NULL      | 0
2     | Manager| 1         | 1
3     | Dev    | 2         | 2
4     | Tester | 2         | 2

Use a recursive CTE when the data is hierarchical:
  • Employee–Manager structure
  • Category–Subcategory
  • Organization chart
  • Folder–Subfolder
  • Tree or graph traversal
  • Bill of materials
CTE is better than a subquery because it improves readability, avoids repeated logic, supports recursion, and makes complex SQL queries easier to maintain, while performance remains similar.

Note: A CTE (Common Table Expression) is temporary and exists only for the very next SQL statement that follows the WITH clause. Once that statement finishes execution, the CTE is gone.

Example: Invalid Use of CTE.

WITH EmpCTE AS (
    SELECT * FROM Employee
)
SELECT * FROM EmpCTE;

SELECT COUNT(*) FROM EmpCTE;  -- ❌ ERROR
A CTE can be referenced multiple times within a single SQL statement, but it cannot be used across multiple SELECT statements.

DON'T MISS

Tech News
© all rights reserved
made with by AlgoLesson