Popular Posts

May 16, 2024

Logging in ASP.NET Core using Serilog

 Serilog is a popular logging library for .NET applications. It provides a flexible and efficient logging framework that allows developers to log structured data in a variety of formats and sinks (destinations where log data is stored), such as text files, databases, and cloud-based services like Seq.

Serilog stands out for its structured logging capabilities, which allow developers to log data in a structured format (e.g., JSON) rather than plain text. This structured data can then be easily parsed and analyzed by log management systems, making it particularly useful for troubleshooting and monitoring applications in production environments.

Serilog supports a wide range of logging features, including log levels, message templates, contextual logging with property enrichment, and support for custom sinks and enrichers. It's widely used in the .NET ecosystem and is known for its simplicity, flexibility, and performance.


Create a asp.net core project. You can name a project anything but you can’t named serilog. Because it is a package name and you will get error.

 

Download package from nuget

dotnet add package Serilog.AspNetCore

 

 

Create a folder Services.

Create interface named it IMathService.

Write the following code

public interface IMathService

{

    decimal Divide(decimal a, decimal b);

}

 

Create a class MathService:

public class MathService : IMathService

{

    private readonly ILogger<MathService> _logger;

 

    public MathService(ILogger<MathService> logger)

    {

        _logger = logger;

    }

 

    public decimal Divide(decimal a, decimal b)

    {

        _logger.LogInformation("Parameter 1: " + a);

        _logger.LogInformation("Parameter 2: " + b);

 

        decimal result = 0;

 

        try

        {

            result = a / b;

        }

        catch (DivideByZeroException ex)

        {

            _logger.LogWarning(ex, "You cannot divide by zero.");

            throw ex;

        }

 

        return result;

    }

}

 

In program.cs file add the following code:

builder.Services.AddTransient<IMathService, MathService>();

////Add support to logging with SERILOG

builder.Host.UseSerilog((context, configuration) =>

    configuration.ReadFrom.Configuration(context.Configuration));

 

 

 

app.UseStaticFiles();

 

//Add support to logging request with SERILOG

app.UseSerilogRequestLogging();





Github: https://github.com/itsjubayer/Serilog.git




May 14, 2024

In-Memory Caching in ASP.NET Core

 


 

In ASP.NET Core, in-memory caching is a technique used to store data within the application's memory. This cached data is readily accessible to subsequent requests, which can significantly improve the performance of the application by avoiding expensive operations such as repeated database queries or complex calculations.

 

Create an webapi project named it InMemoryCaching. We will use database first approach.

In appsetting set database configutation:

,

  "ConnectionStrings": {

    "DBConnectionString": "Server=DESKTOP-M80VO7A;Database=EmployeeDB;Trusted_Connection=True;MultipleActiveResultSets=true; TrustServerCertificate=True;Integrated Security=SSPI; TrustServerCertificate=True; MultipleActiveResultSets=true;"

  }

 

 

Create context file: EmployeeDbContext

public partial class EmployeeDbContext : DbContext

{

   

    public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options)

        : base(options)

    {

    }

}

In Program.cs file:

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

 

builder.Services.AddMemoryCache();

 

we need to download some nuget packages like SqlServer, Tools, Design etc.


 

Open package manager console and run the command:

Scaffold-DbContext -Connection Name=DBConnectionString Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models –force

 

Create controller named it EmployeeController

Write the code below in the controller:

 

private readonly EmployeeDbContext _context;

private readonly IMemoryCache _cache;

 

 

public EmployeeController(EmployeeDbContext context, IMemoryCache cache)

{

    _context = context;

    _cache = cache;

}

 

[HttpGet]

public async Task<IActionResult> GetAll()

{

    var products = await _context.Employees.ToListAsync();

 

    return Ok(products);

}

 

[HttpGet]

[Route("GetAllCache")]

public async Task<IActionResult> GetAllCache()

{

    var cacheKey = "GET_ALL_EMPLOYEES";

 

    // If data found in cache, return cached data

    if (_cache.TryGetValue(cacheKey, out List<Employee> Employees))

    {

        return Ok(Employees);

    }

 

    // If not found, then fetch data from database

    Employees = await _context.Employees.ToListAsync();

 

    // Add data in cache

    _cache.Set(cacheKey, Employees);

 

    return Ok(Employees);

}

 

 

Open post man and test the controller, when we run get all we get the result below:


 

 

 

When we run get all cache:


 

To cache data for the exact time, we can use the AbsoluteExpiration setting. In the following code snippet, the AbsoluteExpiration is set to 5 minutes, which means no matter how frequently our cached data is accessed, it will flush after 5 minutes.

            var cacheOptions = new MemoryCacheEntryOptions()

            {

                AbsoluteExpiration = DateTime.Now.AddMinutes(5)

            };

 

            _cache.Set(cacheKey, Employees, cacheOptions);

 

We can also use the SlidingExpiration setting which allows us to remove cached items which are not frequently accessed. In the example below, I set it to 5 minutes which means that data will remove from the cache only if it is not accessed in the last 5 minutes.

            var cacheOptions = new MemoryCacheEntryOptions()

            {

                SlidingExpiration = TimeSpan.FromMinutes(5)

            };

 

            _cache.Set(cacheKey, Employees, cacheOptions);

 

If our data is accessed more frequently than our sliding expiration time, then we will end up in a situation where our data will never expire. We can resolve this problem by following code:

            var cacheOptions = new MemoryCacheEntryOptions()

            {

                SlidingExpiration = TimeSpan.FromMinutes(5),

                AbsoluteExpiration = DateTime.Now.AddMinutes(60)

            };

 

            _cache.Set(cacheKey, Employees, cacheOptions);

 

 

 

We can also set the priority of the cached items to keep high priority items in cache during a memory pressure triggered cleanup. By default, all items in the cache have Normal priority but we are allowed to set Low, Normal, High, and NeverRemove options as well.

 

var cacheOptions = new MemoryCacheEntryOptions()

{

    AbsoluteExpiration = DateTime.Now.AddMinutes(60),

    Priority = CacheItemPriority.High

};

 

_cache.Set(cacheKey, Employees, cacheOptions);




Github: https://github.com/itsjubayer/InMemoryCaching.git

May 12, 2024

ASP.NET Core Data Validations with FluentValidation

FluentValidation is a .NET library used for validation in applications developed using the .NET framework. It provides a fluent interface for defining validation rules for your classes. With FluentValidation, you can define validation rules in a clear and expressive way, making your code more readable and maintainable.

Instead of cluttering your domain classes with validation logic, you can create separate validator classes that define the validation rules for each class. This separation of concerns helps keep your codebase clean and organized.

FluentValidation supports various types of validation rules, including basic checks like required fields, string length, and numeric range, as well as more complex validations such as custom validation logic and cross-property validations.

Overall, FluentValidation simplifies the process of validating input data in .NET applications and promotes clean, maintainable code by separating validation concerns from domain logic.



Create a project named FluentValidationTest.

Run this commands to install fluent validation :

-          Install-Package FluentValidation

-          Install-Package FluentValidation.AspNetCore

-          Install-Package FluentValidation.DependencyInjectionExtensions

 

In Program.cs file add this lines:

builder.Services.AddFluentValidation(v =>

{

    v.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly());

});

 

 

In Models Folder create a class named it “CustomerModel”

public class CustomerModel

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Email { get; set; }

    public decimal Height { get; set; }

    public int Age { get; set; }

    public string Phone { get; set; }

 

 

    public AddressModel PrimaryAddress { get; set; }

    public List<AddressModel> OtherAddresses { get; set; }

 

}

 

Create a directory named Validators

Create a class named CustomerValidator.cs

    public class CustomerValidator : AbstractValidator<CustomerModel>

    {

        public CustomerValidator()

        {

            //RuleFor(x => x.FirstName).NotNull().NotEmpty();

            //RuleFor(x => x.LastName).NotNull().NotEmpty();

 

            

 

            RuleFor(x => x.FirstName).NotEmpty().WithMessage("First Name is required.");

            RuleFor(x => x.LastName).NotEmpty().WithMessage("Last Name is required.");

            RuleFor(x => x.Email).NotNull().NotEmpty().EmailAddress();

            RuleFor(x => x.Height).ScalePrecision(1, 3);

            RuleFor(x => x.Age).InclusiveBetween(18, 50);

 

            RuleFor(x => x.Phone).Must(phone =>

                !string.IsNullOrEmpty(phone) && phone.StartsWith("+")

            ).WithMessage("Phone must starts with + sign.");

 

 

            RuleFor(x => x.PrimaryAddress).InjectValidator();

//  You can also specify any child validators for complex properties

//  by using the SetValidator method.

            RuleFor(x => x.PrimaryAddress).SetValidator(new AddressValidator());

//  To validator the OtherAddresses collection, you can use the RuleForEach 

//  method to associate the same validator to multiple items in a collection.

            RuleForEach(x => x.OtherAddresses).SetValidator(new AddressValidator());

        }

 

 

}

 

 

Create a controller CustomerController

Add this controller:

public IActionResult Create()

{

    return View();

}

 

 

[HttpPost]

public IActionResult Create(CustomerModel model)

{

    if (!ModelState.IsValid)

    {

        return View(model);

    }

 

    //TODO: Save the customer to the database.

 

    return Ok();

}

 

 

Create view:

@model FluentValidationTest.Models.CustomerModel

 

@{

    ViewData["Title"] = "Create";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

 

 

<h3>Add Customer</h3>

<hr />

 

<form asp-action="Create" method="post">

    <div class="row">

        <div class="col-md-6">

            <div class="form-group">

                <label>First Name</label>

                <input asp-for="FirstName" class="form-control" />

                <span asp-validation-for="FirstName" class="text-danger"></span>

            </div>

        </div>

        <div class="col-md-6">

            <div class="form-group">

                <label>Last Name</label>

                <input asp-for="LastName" class="form-control" />

                <span asp-validation-for="LastName" class="text-danger"></span>

            </div>

        </div>

    </div>

    <div class="row">

        <div class="col-md-6">

            <div class="form-group">

                <label>Email</label>

                <input asp-for="Email" class="form-control" />

                <span asp-validation-for="Email" class="text-danger"></span>

            </div>

        </div>

        <div class="col-md-6">

            <div class="form-group">

                <label>Height</label>

                <input asp-for="Height" class="form-control" />

                <span asp-validation-for="Height" class="text-danger"></span>

            </div>

        </div>

    </div>

    <div class="row">

        <div class="col-md-6">

            <div class="form-group">

                <label>Age</label>

                <input asp-for="Age" class="form-control" />

                <span asp-validation-for="Age" class="text-danger"></span>

            </div>

        </div>

        <div class="col-md-6">

            <div class="form-group">

                <label>Phone</label>

                <input asp-for="Phone" class="form-control" />

                <span asp-validation-for="Phone" class="text-danger"></span>

            </div>

        </div>

    </div>

    <div class="row">

        <div class="col-md-6">

            <div class="form-group">

                <input type="submit" value="Create" class="btn btn-primary" />

            </div>

        </div>

    </div>

</form>



For details please visit:  https://docs.fluentvalidation.net/en/latest/inheritance.html

Github: https://github.com/itsjubayer/ASP.NET-FluentValidation-.git