Thursday, September 23, 2021

Azure Front Door: Intelligent Health Probe Monitoring

Azure Front Door is in simple a Front Door to all your applications. Once a request is entered through Front Door, it travels through Microsoft's global edge network. It has many capabilities, but in this post, we are going to have a look at how easy it is to set up Health Probe Monitoring in Azure Front Door and how Azure Front Door handles unhealthy Backends.

As always it's easy to go by a demo.

I have deployed a very simple web application as an Azure App Service in two regions, one in Australia Southeast and the other in East US 2. (I am using 2 regions because Front Door is resilient to failures to an entire Azure region unlike Azure Application Gateway). This web application exposes /health endpoint, which the Azure Front will use to monitor the health.

And I have an Azure Front Door created and added a single backend pool with my two App Services.

Update Backend Pool

For my backends, their priority and weight are configured to be equal. Since I am in New Zealand, the closest region to me out of my two regions is Australia Southeast. So when I access the Front Door, I will be served from my App Service running in Australia Southeast (Read more: Front Door routing methods)

Served from Australia Southeast
And for some reason, if my App Service in Australia Southeast is down (can be an Application issue or can be a serious issue like Azure App Services in Australia Southeast region is down) and then I will be served from a different backend (determined by Front Door routing methods)

Served from East US 2
All these are handled by Azure Front Door with the very little configuration I have shown above (highlighted in red and green in the first image).

To determine the health and proximity of each backend, the Front Door periodically sends an HTTP/HTTPS request (health probe) to the specified path in each of your configured backends. This request can be either a GET or HEAD (HEAD is preferred due to less overhead) request. We can change the frequency Front Door do the health probe (for the demo purpose, I have set it to 5 seconds)

Then it considers the response status and the latency.  A 200 OK status code indicates the backend is Healthy. Everything else is considered a failure. If it doesn't get any response it's counted as a failure. So for my active backends, Front Door looks at the last n health probe responses. If at least x are healthy, the backend is considered healthy. Here n is the Sample size and x is Successful samples required property in load-balancing settings.

Read More:

Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, September 14, 2021

C# 10.0: Introducing Parallel.ForEachAsync

Finally, with C# 10, we now have the async version of Parallel.ForEach which is Parallel.ForEachAsync.

Consider the below code.

List<stringurls = new()
{
    "https://devblogs.microsoft.com/dotnet/feed",
    
"https://devblogs.microsoft.com/aspnet/feed",
    
"https://devblogs.microsoft.com/visualstudio/feed",
    
"https://devblogs.microsoft.com/devops/feed"
};

var client = new HttpClient();

async 
Task<stringDownload(string urlCancellationToken cancellationToken = default)
{
    HttpResponseMessage response = await client.GetAsync(url, cancellationToken);
    return await response.Content.ReadAsStringAsync(cancellationToken);
}

Prior to C# 10, if we are to do a download in Parallel, most likely what we would do is something like below.

List<Task<string>> tasks = new();
foreach (var url in urls)
{
    tasks.Add(Download(url));
}
List<string>? results = (await Task.WhenAll(tasks)).ToList();
// TODO: Work with results

Now we can easily use Parallel.ForEachAsync.

List<string>? results = new();
await 
Parallel.ForEachAsync(urls, async (urlcancellationToken) =>
{
    results.
Add(await Download(url, cancellationToken));
});
// TODO: Work with results

And this gives us more flexibility like we can control the ParallelOptions.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, September 6, 2021

Visual Studio 2022: Introducing Multi-Repo Support

Let's say we have a solution with 2 Projects and these projects are hosted in two different Git repos (it can be any Git provider like GitHub, Azure DevOps, BitBucket etc). You are working on both the projects at the same time and committing changes. 

In this kind of a scenario, if you are using an earlier version of Visual Studio, for example, Visual Studio 2019, handling/committing changes can be a real pain. If you have the solution opened, Visual Studio will be tracking only one repo at a time, so you need to be extra careful when committing changes to select the appropriate target repo using the Repo Selector in the bottom toolbar of Visual Studio. 
Visual Studio 2019: Repo Selector (one at a time)
Visual Studio 2019 - Git Changes
Or you will have to use different Visual Studio instances, or use another git tooling to commit the changes.

But as the Visual Studio team is trying to make the git experience within Visual Studio more user-friendly, Visual Studio 2022 Preview 3 has introduced this new feature which is Multi-repo support. As of today it's still in its Preview and is disabled by default. If you want try out this feature, you need to explicitly enable it by navigating into Tools -> Options -> Environment -> Preview Features and turning on Enable multi-repo support.
Visual Studio 2022 - Enable multi-repo support
Once you have enabled multi-repo support and opened up a solution with multi repo projects, you will see Visual Studio is automatically identifing different repos in the solution. It will bold out the active repos within the list of all the repositories.
Visual Studio 2022 - Enable multi-repo support
And when you are trying to commit the changes, it will show all the active repos and if you want, you can push to all the repos at one single shot.
Visual Studio 2022 - Git changes and committing to multiple repos
Or alternatively, you can select the target repo, and push only it's changes.
Visual Studio 2022 - Git Changes and committing to a single repo
And when you go to Branches Window, you get the same features as in single repo solution like managing branches, seeing commit details, checking diff and seeing incoming/outgoing commits, etc.
Manage Branches with multi-repo
Try out Visual Studio 2022 today.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Thursday, August 26, 2021

EF Core 6.0: Introducing Migration Bundles

In this post, let's have a look at a new feature that's coming with EF Core 6 in November, 2021. That's EF Core Migration Bundles. So far when we are using EF Core, most of the time we are using dotnet ef migrations script and database update to update the database.

The EF team has come up with this new approach to apply migrations that is by creating an executable with the migrations scripts and once we execute it, it will update the database.

To try out EF Core Migration Bundles, you will need at least the EF Core 6 Preview 7 Version of Tools.

# install
dotnet tool install --global dotnet-ef --version 6.0.0-preview.7.21378.4

# if you have already have it installed, then upgrade
dotnet tool update --global dotnet-ef --version 6.0.0-preview.7.21378.4

Once you have this version installed, you should see a new command.

dotnet ef migrations bundle
dotnet ef migrations bundle
You can see a variety of options that you can use, almost all of them are self-described.

Now let's see things in action. Consider I have the following code.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContextservices) =>
    {
        services
            .AddDbContext<MyDbContext>(options =>
            {
                options.UseSqlServer(hostContext.Configuration.GetConnectionString(nameof(MyDbContext)));
            });
    })
    .Build();

await host.RunAsync();

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }

    public DbSet<Category> Categories { getset; }

    public DbSet<Product> Products { getset; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>(builder =>
        {
            builder
                .HasMany(x => x.Products)
                .WithOne()
                .HasForeignKey(x => x.CategoryId);
        });
    }
}

public class Category
{
    public int Id { getset; }

    public string Name { getset; }

    public ICollection<Product> Products { getset; }
}

public class Product
{
    public int Id { getset; }

    public string Name { getset; }

    public string Description { getset; }

    public int CategoryId { getset; }
}

And I have added some migrations for this DbContext. 

Then I just have to run the bundle command in CLI,

dotnet ef migrations bundle

dotnet ef migrations mundle

Or if you are using VS, run the following command in Package Manager Console. (Note as today with the latest bits, it doesn't work, an issue is already logged: #25555)

Bundle-Migration

Once I do that, it will create an executable named bundle.exe. (EF Core 6 RC 1 will have an option --output that will give us the ability to change the output)

bundle --help

Then I just need to executable the bundle.exe. By default, it will pick up the connection string from appsettings.json. Or you can pass in the connection as an option by doing something like this,

.\bundle.exe --connection "<ConnectionString>"

You can also pass in a target migration if you want to, something like below.

.\bundle.exe 20210825094417_Initial --connection "<ConnectionString>"

So what's the advantage of using migrations bundle.

  • Can be made a self-contained executable with everything needed to run a migration. For example in DevOps pipelines, you don't need to explicitly install dotnet ef.
  • It doesn’t require you to copy source code or install the .NET SDK (only the runtime) and can be integrated as a deployment step in your DevOps pipeline.
Read More:

Sample Code:

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, August 23, 2021

.NET 6 Preview 7: Introducing Implicit Namespaces

If you have created a Console Application Project, an ASP.NET Core Web App/Web API project, or a Worker Service Project using Visual Studio 2022 latest preview (17.0.0 Preview 3.1) targetting the latest .NET 6 Preview (.NET 6 Preview 7), you will see a significant change to the template code/boilerplate code that is generated.

All of them now uses Top-Level statements and most importantly you won't see all the usings that were previously there. 

Console Application

Console Application Project (Microsoft.NET.Sdk)

ASP.NET Core Web API

ASP.NET Core Web API Project (Microsoft.NET.Sdk.Web)

Worker Service

Worker Service Project (Microsoft.NET.Sdk.Worker)
But all the projects are compiling fine, so how is this possible, in this post, we are going to find it out.

This is all made possible via Global Usings that got introduced as part of C# 10. The .NET SDK  now implicitly includes a set of default namespaces for C# projects which targets   .NET 6 or later and use one of the following SDKs:

  • Microsoft.NET.Sdk
  • Microsoft.NET.Sdk.Web
  • Microsoft.NET.Sdk.Worker
Microsoft.NET.Sdk
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
Microsoft.NET.Sdk.Web
// <autogenerated />
// Microsoft.NET.Sdk related global usings plus the following
global using global::System.Net.Http.Json;
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
Microsoft.NET.Sdk.Worker
// <autogenerated />
// Microsoft.NET.Sdk related global usings plus the following
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
You can find these global usings inside in the projects obj directory in {ProjectName}.ImplicitNamespaceImports.cs file.

You can use DisableImplicitNamespaceImports property to disable this feature completely,
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
Or alternatively, you can disable only a set of implicit namespaces, using one of the following SDK-specific properties.
<!--Disable implicit namespaces in Microsoft.NET.Sdk-->
<DisableImplicitNamespaceImports_DotNet>true</DisableImplicitNamespaceImports_DotNet>

<!--Disable implicit namespaces in Microsoft.NET.Sdk.Web-->
<DisableImplicitNamespaceImports_Web>true</DisableImplicitNamespaceImports_Web>

<!--Disable implicit namespaces in Microsoft.NET.Sdk.Worker-->
<DisableImplicitNamespaceImports_Worker>true</DisableImplicitNamespaceImports_Worker>
or you can add or remove individual namespaces using the <Import> item group. For example, let's in a project that targets Microsoft.NET.Sdk, I need to remove System.Net.Http and add System.Text.Json to Implicit Namespaces.
<ItemGroup>
  <Import Remove="System.Net.Http" />
  <Import Include="System.Text.Json" />
</ItemGroup>
Then the implicit usings would be as follows.
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using global::System.Text.Json;
Hope this helps.

More read:

Happy Coding.

Regards,
Jaliya

Wednesday, August 18, 2021

C# 10.0: Introducing File Scoped Namespaces

C# 10.0 is expected to ship with .NET 6 in November, 2021 during .NET Conf 2021 (November 9-11), but some of the features are already available with C# preview LangVersion. In this post let's have a look at File Scoped Namespaces that got shipped with .NET 6 Preview 7 SDK last week.

To try out this feature you will need to use the latest Visual Studio 2022 Preview, which includes the latest .NET 6.0 preview SDK, or alternatively, you can install .NET 6 Preview 7 SDK and use the latest Visual Studio Code.

You can set the LangVersion as preview in your csproj file as follows.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    ...
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

</Project>

Now, this is how we are defining namespaces.

namespace ConsoleApp1.Models
{
    
public class SomeClass
    {
    }
}

So what's the issue with this. Well, there is no technical issue, but this is adding unnecessary indentation to the code and you might have experienced this: when the class becomes longer, you sometimes lose track of indentation. So less indentation is always the best.

Starting with C# 10, you can do something like this.

namespace ConsoleApp1.Models;

public class SomeClass
{
}

Notice the introduction of semicolon after the namespace and the removal of curly braces after the namespace. That's just one less indentation.  

Personally, this is one of my favorite features in C# 10. Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, August 17, 2021

.NET 6 Preview 7: Introducing Static Results Utility Class for Producing Common HTTP Responses as IResult Implementations

Last week .NET 6 Preview 7 was released and one of the new features that got introduced to the world of ASP.NET Core is a new static Results utility class to produce common HTTP responses as IResults. IResult is a new return type that got introduced with Minimal APIs. 

If you are new to Minimal APIs in ASP.NET Core or need to refresh your memories, you can read these posts.

Previously we had to use/maintain our own class to Map IActionResult to IResult.  With this new Results utility class, we no longer have to do that.

If you explore the Results class, it has IResult types for almost all the regularly used HTTP responses.
IResult
You can find the sample code here,
      (PR for Updating packages to .NET Preview 7 and introduce use of static Results utility class)

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, August 2, 2021

Running Puppeteer inside .NET Azure Function

I wanted to check whether Puppeteer can run inside a .NET Azure Function as code and not in a Docker Container (because with Docker it's definitely possible).

Puppeteer is a Node library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

I have created an Azure Function Project with a single HTTP trigger function. So to make sure Puppeteer can run inside a .NET Azure Function App, I am going to call my function with a query parameter of a URL, and I am expecting my function to run Puppeteer, navigate to that URL, download its content and return. So it proves my requirement.

First, we need to install PuppeteerSharp NuGet package, which is the official .NET port of the official Puppeteer. And it's depending on .NETStandard 2.0, so we are all good for .NET Core 2.x, 3.x, and .NET 5 compatibility.

using PuppeteerSharp;

public static class Function1
{
    [FunctionName("Function1")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        string url = req.Query["url"];
        if (string.IsNullOrEmpty(url))
        {
            return new BadRequestObjectResult($"Missing 'url' query parameter.");
        }

        var browserFetcher = new BrowserFetcher(new BrowserFetcherOptions
        {
            Path = Path.GetTempPath()
        });

        await browserFetcher.DownloadAsync(BrowserFetcher.DefaultChromiumRevision);

        Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true,
            ExecutablePath = browserFetcher.RevisionInfo(BrowserFetcher.DefaultChromiumRevision.ToString()).ExecutablePath
        });

        using Page page = await browser.NewPageAsync();
        await page.GoToAsync(url);

        string responseMessage = await page.GetContentAsync();

        return new OkObjectResult(responseMessage);
    }
}

Here the code itself is self-descriptive, I am downloading the chromium browser, launching that through Puppeteer, navigating to the URL, reading the content, and finally returning the content.

I ran the function app locally and the results looked promising.
Working Locally

Then I have deployed this to an Azure Function App with following configuration.
Azure Function App Configuration: Publish, Runtime stack and Version
Azure Function App Configuration: Operating System
Once deployed, tested whether it's working.
Working on Azure
Works like a charm.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, July 19, 2021

Visual Studio 2022 Preview 2: New Project Properties Page

Visual Studio 2022 Preview 2 was released last week, and in this post, let's have a look at another new feature that got introduced in Visual Studio 2022

If you open up a Project Properties window from Visual Studio 2022, that is entirely a new experience.
Visual Studio 2022: New Project Properties Experience

There are a lot of options we can now manage through the new Project Properties page. For example, if it's a web project, we can update Razor Properties and TypeScript properties directly from the Project Properties page. There is also a new Search box, which is very handy when we need to quickly find something.

The Debug Profile management is moved into its own dialog and has a rich UI to manage different settings such as the newest Hot Reload option, etc.
Visual Studio 2022: Launch Profiles Dialog

Haven't you still tried out Visual Studio 2022? As usual, it runs side by side with previous versions, so do try it out today: Download Visual Studio 2022

Happy Coding.

Regards,
Jaliya

Friday, July 16, 2021

.NET 6 Preview 6: Introducing OpenAPI Support in Minimal APIs in ASP.NET Core

We are getting closer to .NET 6 final release and this week .NET 6 Preview 6 was released. .NET 6 Preview 4 has introduced Minimal APIs in ASP.NET Core. With .NET 6 Preview 6, we now have OpenAPI support for Minimal APIs. In this post, let's see how we can set up Swagger for a project that uses the Minimal API approach.

If you are new to Minimal APIs in ASP.NET Core or need to refresh your memories, you can read this post I have written a couple of months back: .NET 6 Preview 4: Introducing Minimal APIs in ASP.NET Core. I am going to upgrade the sample project (minimal-api) used in that post to .NET 6 Preview 6 and add support for OpenAPI.

First, I am upgrading all the relevant packages to their latest previews, and I am installing Swashbuckle.AspNetCore latest package.
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0-preview.6.21352.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-preview.6.21352.12" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
  </ItemGroup>

</Project>
In my case, I had only 2 packages installed.
Once the packages are updated, setting up Swagger is pretty straightforward, it's more or less the same to what we have done over all these years.

First, set up the required dependencies.
builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1"new OpenApiInfo { Title = "Minimal API", Description = "OpenAPI specification for Minimal API", Version = "v1" });
});
Then add Swagger OpenAPI specification and Swagger UI to the middleware.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json""Minimal API V1");
});
That's just it. Now run the application and navigate to https://localhost:5001/swagger and we have Swagger specification at our disposal.
Swagger UI
You can find the sample code here,
   https://github.com/jaliyaudagedara/minimal-api 
      (Commit for Updating packages to .NET Preview 6 and adding OpenAPI support)

Hope this helps.

Happy Coding.

Regards,
Jaliya