Thursday, 18 November 2021

C# record types equality comparison : heaven or hell

 it’s 10:25 PM IST and as usual I was doing some learning on C# 10 features and suddenly I got diverted to play around with equality comparison of record types with their member’s value unlike a class. I must tell you here, I was surprised to see the results and reach to conclusion of using this very very carefully or simply ignore until really needed to go for record types.

In case you landed newly here: record type is a new type introduced on C# 9 and enhanced in C#10. For details on C# 9 features please visit C# 9.0 features : a developer handbook

Let’s understand this with below example. To carry on with examples I defined three record types as below:

public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public record Teacher(string FirstName, string LastName, string[] PhoneNumbers, int Grade)
: Person(FirstName, LastName, PhoneNumbers);
public record Student(string FirstName, string LastName, string[] PhoneNumbers, int Grade)
: Person(FirstName, LastName, PhoneNumbers);

Example 1: Results True — Happy path

var phoneNumbers = new string[2];
var teacher1 = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
var teacher2 = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
Console.WriteLine(teacher1 == teacher2);//true

Example 2: Results False — Oops record type spotted two difference string array for Phone numbers.

var phoneNumbers = new string[2];
var teacher3 = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
var teacher4 = new Teacher("Nancy", "Davolio", new string[2], 3);
Console.WriteLine(teacher3 == teacher4);

Example 3: Results False — Aah really, record type don’t entertain the reference types to check even they have equal values in it.

var teacher5 = new Teacher("Nancy", "Davolio", new string[2] { "555-1234", "555-6789" }, 3);
var teacher6 = new Teacher("Nancy", "Davolio", new string[2] { "555-1234", "555-6789" }, 3);
Console.WriteLine(teacher5 == teacher6);

Example 4: Results True— BOOM! though there is a reference type for phone numbers but they points to same memory location or both memory location as well as value, yeah this is the case.

I would have simply memorize it if it would have been part of my academic syllabus.

var phoneNumbers = new string[2] { "555-1234", "555-6789" };
var teacher7 = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
var teacher8 = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
Console.WriteLine(teacher7 == teacher8);

So the conclusion here is, if you want to use record types because of their nature then be extra cautious to save yourself from Hell considering you are going to enjoy Heaven. :)

Now lets see how it behaves in case of inheritance hierarchies. Here we go:

Example 1: Results False. Aah, they can be equal only if their run-time type is equal not because they belongs to same parent.

var phoneNumbers = new string[2];
var teacher = new Teacher("Nancy", "Davolio", phoneNumbers, 3);
var student = new Student("Nancy", "Davolio", phoneNumbers, 3);
Console.WriteLine(teacher == student);//false

Example 2: Results True. Because they are really twins from same father, kidding :)

var phoneNumbers = new string[2];
var student1 = new Student("Nancy", "Davolio", phoneNumbers, 3);
var student2 = new Student("Nancy", "Davolio", phoneNumbers, 3);
Console.WriteLine(student1 == student2);//true

So the conclusion here is, For two record variables to be equal, the run-time type must be equal and store the same values.

Thank You for reading. Hope you enjoyed and don’t forget to fuel me up for such articles with your claps as much you can.

Monday, 15 November 2021

Load Balancer vs Application Gateway vs Traffic Manager vs Front Door service in Azure

 Often confusing when to use what and landed into the same boat while explaining it to one of my friend and that’s encourages me to write this article.

Each and every individual services mentioned here has the specific purpose, let’s understand.

Azure Load Balancer

As name says, it is a service for balancing load. Ok, one boat has too much of load so lets distribute the Load among multiple Similar boats and that’s the concepts. The point here to catch is, this service is to distribute the load to avoid one boat/VM getting loaded heavily. So the concepts of load balancer is about the distributing the load among multiple similar VMs hence it operates at layer 4 of the Open Systems Interconnection (OSI) model.

it helps you to scale your applications and create highly available services by supporting both inbound and outbound scenarios. Load balancer provides low latency and high throughput, and scales up to millions of flows for all TCP and UDP applications.

Azure Application Gateway

As name says it’s a gateway to get the final destination. In boat example, we people travelling across multiple destinations so now the responsibility is to send the right people to right boat according to their Destinations. We have a criteria here ‘Destination’ to choose the right boat and that is the URL in this concept based on URL, service decides which server/VM will process the request. hence it operates operates at layer 7 of the Open Systems Interconnection (OSI) model.

Azure Traffic Manager

Now it is difficult for me to continue the example of boat as here we are reaching to larger scale but I would still go with real time example and consider the example of Airport.

From one Airport, multiple airline service provider companies operates to fly across different location of the country or world. Here if you see, above two solutions is not fulfilling our need to distribute the load/traffic to serve better and the reason is what would you do if one Airline Provider is not capable to serve to a specific destination (all seats are full) so there is another airline service provider to serve the same destination.

In Azure, Azure Traffic Manager is a DNS-based traffic load balancer. This service allows you to distribute traffic to your public facing applications across the global Azure regions. Hence it works to distribute the traffic across regions to facilitated high geographic availability.

Here the DNS (Domain Name System) you can understand the information about the destination so if the destination is unable to be fulfilled by one Airline provider than another airline provider will be ready to serve.

What is Parent Profile here, To fulfil the need of traffic routing we need to a manager to manage the demand and service and that is where we need Traffic Manager Profile.

Traffic Manager profiles use traffic-routing methods to control the distribution of traffic to your cloud services or website endpoints. Within the Traffic Manager Profile we configure endpoints, monitoring, and other settings in the Azure portal. Traffic Manager supports up to 200 endpoints per profile. However, most usage scenarios require only a few of endpoints.

and the broader picture:

Azure Front Door Service

Think about the Airport serving the destination need of Domestic and International with multiple airline providers and to manage the traffic here we would need a better management system to serve the load. Here would need multiple filter check after a certain point which is called main door to separate the traffic for domestic and international.

Azure Front Door is a global, scalable entry-point that uses the Microsoft global edge network to create fast, secure, and widely scalable web applications. With Front Door, you can transform your global consumer and enterprise applications into robust, high-performing personalized modern applications with contents that reach a global audience through Azure.

Front Door works at Layer 7 (HTTP/HTTPS layer) using any cast protocol with split TCP and Microsoft’s global network to improve global connectivity. Based on your routing method you can ensure that Front Door will route your client requests to the fastest and most available application backend.

Look at the example here, there is only one door which serve the first level abstraction to serve three different destinations: /* , /Search/* and /Statics/* and then for each destination multiple possible destination routes is available to fulfill the need of traffic management globally.

Note

Azure provides a suite of fully managed load-balancing solutions for your scenarios.

  • If you are looking to do DNS based global routing and do not have requirements for Transport Layer Security (TLS) protocol termination (“SSL offload”), per-HTTP/HTTPS request or application-layer processing, review Traffic Manager.
  • If you want to load balance between your servers in a region at the application layer, review Application Gateway.
  • If you need to optimize global routing of your web traffic and optimize top-tier end-user performance and reliability through quick global failover, see Front Door.
  • To do network layer load balancing, review Load Balancer.

Hope you enjoyed reading. The purpose about this article is to remove the confusion about above four load balancing azure solutions. Please refer the Microsoft documentations (follow the link from above Note section) for details. Thank You.

Friday, 12 November 2021

Use multiple implementations of an interface with ASP.NET Core DI

 On my previous article Middleware and Filters power in ASP.NET Core, one of my friend requested me to write about multiple implementations of an interface dependency resolution and I promised him that my next article will cove this. So here it is.

In this article, I’ll be using the same example by extending it to use multiple implementation of logger service by resolving it based on a config setting to log my exception.

First we will create ILoggerService and different implementation of it by creating a Services folder in the project.

namespace CoreWebAPIDemo.Services
{
public enum LogType
{
Trace,
Debug,
Information,
Warning,
Error
}
public interface ILoggerService
{
public void Log(LogType logType, object logInfo);
}
public class NullLoggerService : ILoggerService
{
public void Log(LogType logType, object logInfo)
{
//Do Nothing
}
}
public class SerilogFileLoggerService : ILoggerService
{
public void Log(LogType logType, object logInfo)
{
//your logging code
}
}
public class DbLoggerService : ILoggerService
{
public void Log(LogType logType, object logInfo)
{
//your logging code
}
}
}

Next, I’ll add a delegate to hold a reference of a function which accept the one string argument ‘logger type’ and return the ILoggerService instance. For this create a cs file “LoggerServiceResolver.cs” and add below code.

namespace CoreWebAPIDemo.Services
{
public delegate ILoggerService LoggerServiceResolver(string loggerType);
}

Now in Startup.ConfigureServices method, add the different logger services and also add LoggerServiceResolver with an implementation to find the right requested service.

services.AddScoped<SerilogFileLoggerService>();
services.AddScoped<DbLoggerService>();
services.AddScoped<NullLoggerService>();
services.AddTransient<LoggerServiceResolver>(serviceProvider => loggerType =>
{
switch (loggerType)
{
case "SerilogFileLogger":
return serviceProvider.GetService<SerilogFileLoggerService>();
case "DbLogger":
return serviceProvider.GetService<DbLoggerService>();
default:
return serviceProvider.GetService<NullLoggerService>();
}
});

I want to set the logger type option configurable through appsettings.json file so will add a setting there as:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"UserLogger": "SerilogFileLogger",
"AllowedHosts": "*"
}

By this implementation is ready. Now we need to add the code to consume this and for that I’ll enhance my “ExceptionHandlingMiddleware” which I use in my previous article. Please read it here: Middleware and Filters power in ASP.NET Core

using CoreWebAPIDemo.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Net;
using System.Threading.Tasks;
namespace CoreWebAPIDemo.Middleware
{
public class ExceptionHandlingMiddleware
{
private readonly LoggerServiceResolver _serviceResolver;
private readonly IConfiguration _configuration;
private readonly RequestDelegate _requestDelegate;public ExceptionHandlingMiddleware(RequestDelegate requestDelegate, IConfiguration configuraiton, LoggerServiceResolver serviceResolver)
{
_requestDelegate = requestDelegate;
_serviceResolver = serviceResolver;
_configuration = configuraiton;

}
public async Task Invoke(HttpContext context)
{
try
{
await _requestDelegate(context);
}
catch (Exception ex)
{
await HandleException(context, ex);
}
}
private Task HandleException(HttpContext context, Exception ex)
{
var logType = _configuration.GetValue<string>("UserLogger");
var myLogger = _serviceResolver(logType);
myLogger.Log(LogType.Error, ex);

context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(ex.Message);
}
}
}

In above code, Code in Bold is the new code added and needed for this example. Now we all set to use the different implementations of ILoggerService with ASP.NET Core DI with my middleware. Similarly we can implement the same with “ExceptionHandlerFilter” class and changes would be (highlighted in Bold):

using CoreWebAPIDemo.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using System;
using System.Net;
namespace CoreWebAPIDemo.Middleware
{
public class ExceptionHandlerFilter : Attribute, IActionFilter
{
private readonly LoggerServiceResolver _serviceResolver;
private readonly IConfiguration _configuration;
public ExceptionHandlerFilter(IConfiguration configuraiton, LoggerServiceResolver serviceResolver)
{
_serviceResolver = serviceResolver;
_configuration = configuraiton;
}
public void OnActionExecuting(ActionExecutingContext context) { }public void OnActionExecuted(ActionExecutedContext context)
{
var logType = _configuration.GetValue<string>("UserLogger");
var myLogger = _serviceResolver(logType);


if (context.Exception is Exception exception)
{
myLogger.Log(LogType.Error, exception);
context.Result = new ObjectResult(exception.Message)
{
StatusCode = (int)HttpStatusCode.InternalServerError,
};
context.ExceptionHandled = true;
}
}
}
public class HttpResponseException : Exception
{
public int Status { get; set; } = 500;
public object Value { get; set; }
}
}

Thank you for reading. Hope you enjoyed it and learn the Use of multiple implementations of an interface with ASP.NET Core DI. Don’t forget to clap if you like it and follow me to receive such article updates from me.

Thursday, 11 November 2021

Middleware and Filters power in ASP.NET Core

 In ASP.NET core, Middleware and Filters are two concepts which are very useful & powerful but often confuses also on which one to choose.

Middleware and Filters serves similar purpose and both can be used to achieve the common tasks like exception handling, localizations, authentications etc… but both has certain difference in a way it works with the pipeline.

Lets understand the difference and use of Middleware and Filters through an example of Exception Handling.

Middleware allows in the pipeline to handle requests and responses Where you can choose if the component “is to pass the request to the next component in the pipeline” or “can perform work before and after the next component in the pipeline”. i.e. Perform request validation, localization, exception handling or logging each request and response etc.

Filters run within the ASP.NET Core action invocation pipeline and allows to perform certain operation before or after specific stages in the request processing pipeline. i.e. Perform authentication or authorization to validate requested action processing.

Now lets understand it with the example of Exception Handling generically using both the way.

Scenario: Handle all unhandled exception in code to safeguard the crucial information about code to be delivered to the end user.

Note: ASP.NET Core has already predefined middleware to achieve the same but we are doing it manually to get more control on it.

1. Using Middleware.

Here we try to handle all uncaught exception through out the application through a middleware we create. Middleware is a normal class so you don’t have to inherit any interface/specific class but your class must have the method called Invoke “public async Task Invoke(HttpContext context)”.

using Microsoft.AspNetCore.Http;
using System;
using System.Net;
using System.Threading.Tasks;
namespace CoreWebAPIDemo.Middleware
{
public class ExceptionHandlingMiddleware
{
public RequestDelegate requestDelegate;
public ExceptionHandlingMiddleware(RequestDelegate requestDelegate)
{
this.requestDelegate = requestDelegate;
}
public async Task Invoke(HttpContext context)
{
try
{
await requestDelegate(context);
}
catch (Exception ex)
{
await HandleException(context, ex);
}
}

private static Task HandleException(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(ex.Message);
}
}
}

In above code here I have RequestDelegate dependency resolving code to handle the http request because my requirement to catch the unhandled exception in the pipeline . Now lets add an extension method for IApplicationBuilder to simplify the call for this middleware.

using Microsoft.AspNetCore.Builder;namespace CoreWebAPIDemo.Middleware
{
public static class ExceptionHandlerMiddlewareExtension
{
public static IApplicationBuilder UseExceptionHandlerMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExceptionHandlingMiddleware>();
}

}
}

Our Middleware to catch the uncaught exception is ready and now we need to register it to request pipeline which will be from Startup.Configure method. Here we have to be very careful as the pipeline order is driven through the order of middleware services you add. In this we need to catch the exception while processing the request hence I’ll be adding this middleware at the end just before the UseEndpoints middleware call.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseRouting(); app.UseStaticFiles(); app.UseCors("AllowAll"); app.UseExceptionHandlerMiddleware(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

and finally to test the code, lets create a control which will throw the exception.

using Microsoft.AspNetCore.Mvc;
using System;
namespace CoreWebAPIDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
[HttpGet]
public IActionResult Index(int value)
{
if (value == 1)
return Ok("I'm Happy");
else
throw new Exception("I didn't like your number");
}
}
}

output would be:

Now lets do the exception handling through action filter.

2. Using Filter.

Here we will create a action filter which inherits IActionFilter.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Net;
namespace CoreWebAPIDemo.Middleware
{
public class ExceptionHandlerFilter : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is Exception exception)
{
context.Result = new ObjectResult(exception.Message)
{
StatusCode = (int)HttpStatusCode.InternalServerError,
};
context.ExceptionHandled = true;
}
}
}

}

In this case, we are adding code to handle exception OnActionExecute method which will get executed after the action is executed.

Now we need to register it through Startup.ConfigureServices method if we want to apply it forcefully to all the controller’s action execution and here we can add this anywhere in the method without worrying about the order.

services.AddControllers(options => options.Filters.Add(new ExceptionHandlerFilter()));

Or we can also restrict it to a specific controller or action method and this is one big advantage over using Middleware concept to handle exceptions like in this scenario. Also when we reach here our MVC context is fully ready like model binding and all is done and available with the context.

And this is the reason, I have also inherited Attribute class while creating ExceptionHandlerFilter. Now here is the code to use it with the specific controller only.

using CoreWebAPIDemo.Middleware;
using Microsoft.AspNetCore.Mvc;
using System;
namespace CoreWebAPIDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
[ExceptionHandlerFilter]
public class HomeController : ControllerBase
{
[HttpGet]
public IActionResult Index(int value)
{
if (value == 1)
return Ok("I'm Happy");
else
throw new Exception("I didn't like your number");
}
}
}

Note: Don’t forget to comment the earlier middleware code for registering “app.UseExceptionHandlerMiddleware();” in case if you are in same project like me while testing.

Bonus Reading

ASP.NET core also provides predefined middleware for exception handing which you can use to control the information flowing to the use in case of uncaught exception or error and you can achieve that by doing this:

  1. Create an ErrorController and two action method as below to handle development and production scenario.
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
namespace CoreWebAPIDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ErrorController : ControllerBase
{
/// <summary>
/// The preceding Error action sends an RFC 7807-compliant payload to the client.
/// </summary>
/// <returns></returns>
[Route("/error")]
public IActionResult Error() => Problem("Oops there is a problem. Excuse me and smile!");
[Route("/error-local-development")]
public IActionResult ErrorLocalDevelopment(
[FromServices] IWebHostEnvironment webHostEnvironment)
{
if (webHostEnvironment.EnvironmentName != "Development")
{
throw new InvalidOperationException("Ugggh! This is not for Production, go away.");
}
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();return Problem(
detail: context.Error.StackTrace,
title: context.Error.Message);
}
}
}

2. Enable ASP.NET Core predefined ExceptionHandler middleware from Startup.Configure method.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseExceptionHandler("/error-local-development");
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection(); app.UseRouting(); app.UseStaticFiles(); app.UseCors("AllowAll"); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

Thank you for reading. Hope you enjoyed it and learn the use of Middleware and Filter concept of ASP.NET Core. Don’t forget to clap if you like it and follow me to receive such article updates from me.