Understanding Circuit Breaker Pattern in ASP.NET Core Web API
In the world of software development, resilience, and fault tolerance are critical aspects of creating robust applications. One of the patterns that play a crucial role in achieving this resilience is the Circuit Breaker pattern. In the context of ASP.NET Core Web API, implementing the Circuit Breaker pattern can significantly enhance the reliability of your API.
What is a Circuit Breaker?
The Circuit Breaker pattern, analogous to its electrical counterpart, monitors for failures and helps prevent cascading failures in distributed systems. It acts as a barrier between a potentially failing service or resource and the rest of the system.
When a service is working as expected, the Circuit Breaker allows requests to pass through. However, if the service encounters an issue or starts to fail, the Circuit Breaker "opens" and prevents further requests from being sent to the failing service for a defined period. This helps to preserve system resources and prevent overload or degradation.
Implementation in ASP.NET Core Web API
Let's consider a real-world use case to illustrate the implementation of the Circuit Breaker pattern in an ASP.NET Core Web API.
Create Asp.Net Core Web API Project
Crete Asp.net core web API project with Command prompt Or Visual Studio
Add the Data Access Layer
Add the data access layer using the library project
Add the Models in the Data Access Layer
Add the model folder and add the class for Book
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models
{
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
}
}
Add the Application Database Context Class
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models;
using Microsoft.EntityFrameworkCore;
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.AppDbContext
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Book> Books { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Define configurations, relationships, constraints here if needed
modelBuilder.Entity<Book>().HasKey(b => b.Id);
modelBuilder.Entity<Book>().Property(b => b.Title).IsRequired();
}
}
}
Add the IRepository Interface
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models;
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.IRepository
{
public interface IBookRepository
{
Task<IEnumerable<Book>> GetAllBooksAsync();
Task<Book> GetBookByIdAsync(int id);
Task<Book> AddBookAsync(Book book);
Task<Book> UpdateBookAsync(int id, Book book);
Task<bool> DeleteBookAsync(int id);
}
}
Implement the Repository Pattern
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.AppDbContext;
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.IRepository;
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models;
using Microsoft.EntityFrameworkCore;
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Repository
{
public class BookRepository : IBookRepository
{
private readonly ApplicationDbContext _dbContext;
public BookRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IEnumerable<Book>> GetAllBooksAsync()
{
return await _dbContext.Books.ToListAsync();
}
public async Task<Book> GetBookByIdAsync(int id)
{
return await _dbContext.Books.FindAsync(id);
}
public async Task<Book> AddBookAsync(Book book)
{
_dbContext.Books.AddAsync(book);
await _dbContext.SaveChangesAsync();
return book;
}
public async Task<Book> UpdateBookAsync(int id, Book book)
{
var existingBook = await _dbContext.Books.FindAsync(id);
if(existingBook == null)
return null;
// Update existingBook properties with book
existingBook.Title = book.Title;
existingBook.Author = book.Author;
existingBook.ISBN = book.ISBN;
await _dbContext.SaveChangesAsync();
return existingBook;
}
public async Task<bool> DeleteBookAsync(int id)
{
var bookToDelete = await _dbContext.Books.FindAsync(id);
if(bookToDelete == null)
return false;
_dbContext.Books.Remove(bookToDelete);
await _dbContext.SaveChangesAsync();
return true;
}
}
}
Now Add the Business Access Layer
Add the Bussiness Access Layer
Add the service layer using the library projects in the Asp.net core
Create the Interface for IBookService
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models;
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_BAL.IService
{
public interface IBookService
{
Task<IEnumerable<Book>> GetAllBooksAsync();
Task<Book> GetBookByIdAsync(int id);
Task<Book> AddBookAsync(Book book);
Task<Book> UpdateBookAsync(int id, Book book);
Task<bool> DeleteBookAsync(int id);
}
}
Implement the Service with a Circut breaker Design Pattern
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_BAL.IService;
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.IRepository;
using MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_DAL.Models;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.CircuitBreaker;
namespace MicroservicesWithCircutDesignPatternInAspNetCoreWebAPI_BAL.Service
{
public class BookService : IBookService
{
private readonly IBookRepository _bookRepository;
private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy;
private readonly ILogger<BookService> _logger;
public BookService(IBookRepository bookRepository, ILogger<BookService> logger)
{
_bookRepository = bookRepository;
_logger = logger;
_circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(3, TimeSpan.FromSeconds(30), OnBreak, OnReset, OnHalfOpen);
}
public async Task<IEnumerable<Book>> GetAllBooksAsync()
{
return await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
try
{
_logger.LogInformation("Executing GetAllBooksAsync method.");
return await _bookRepository.GetAllBooksAsync();
}
catch(Exception ex)
{
_logger.LogError(ex, "Error occurred in GetAllBooksAsync method.");
throw; // Re-throw the exception to propagate it up the call stack
}
});
}
public async Task<Book> GetBookByIdAsync(int id)
{
return await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
try
{
_logger.LogInformation($"Executing GetBookByIdAsync method for book with ID: {id}.");
return await _bookRepository.GetBookByIdAsync(id);
}
catch(Exception ex)
{
_logger.LogError(ex, $"Error occurred in GetBookByIdAsync method for book with ID: {id}.");
throw; // Re-throw the exception to propagate it up the call stack
}
});
}
public async Task<Book> AddBookAsync(Book book)
{
return await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
try
{
_logger.LogInformation("Executing AddBookAsync method.");
return await _bookRepository.AddBookAsync(book);
}
catch(Exception ex)
{
_logger.LogError(ex, "Error occurred in AddBookAsync method.");
throw; // Re-throw the exception to propagate it up the call stack
}
});
}
public async Task<Book> UpdateBookAsync(int id, Book book)
{
return await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
try
{
_logger.LogInformation($"Executing UpdateBookAsync method for book with ID: {id}.");
return await _bookRepository.UpdateBookAsync(id, book);
}
catch(Exception ex)
{
_logger.LogError(ex, $"Error occurred in UpdateBookAsync method for book with ID: {id}.");
throw; // Re-throw the exception to propagate it up the call stack
}
});
}
public async Task<bool> DeleteBookAsync(int id)
{
return await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
try
{
_logger.LogInformation($"Executing DeleteBookAsync method for book with ID: {id}.");
return await _bookRepository.DeleteBookAsync(id);
}
catch(Exception ex)
{
_logger.LogError(ex, $"Error occurred in DeleteBookAsync method for book with ID: {id}.");
throw; // Re-throw the exception to propagate it up the call stack
}
});
}
private void OnBreak(Exception exception, TimeSpan duration)
{
_logger.LogError(exception, $"Circuit is open for {duration.TotalSeconds} seconds due to {exception.Message}");
}
private void OnReset()
{
_logger.LogInformation("Circuit is reset");
}
private void OnHalfOpen()
{
_logger.LogInformation("Circuit is half-open");
}
}
}

Join the conversation! Your thoughts help the community grow.