Monthly Archives: November 2021

Dependency Injection in Minimal APIs in .Net 6

Minimal Apis in .Net 6 are really an absolutely amazing feature – you can create an API in about 5 or 6 lines of code. If you have a look online, you’ll see a plethora of examples… but unfortunately, they all show you how to write a “Hello World” API.

I’ve recently been playing with these, and initially found the least obvious part to be the DI part. In a controller, you’d simply inject the dependencies into the constructor of the controller; however, the approach is more nuanced with the minimal APIs. Essentially, you use the parameters of the method itself.

Let’s investigate this by looking at a very simple post method:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", () =>
{
    return Results.Ok();
});

app.Run();

This is a cut down version of the code you’ll get from the weather forecast example in the default template. Now, let’s assume that we want to use the IHttpClientFactory from the controller; we can simply do this:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

As you can see, we’ve injected IHttpClientFactory into our method. This works well, but what if we now want to accept a serialised body in our request; consider the following example:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

We’re passing in MyClass; and, in fact, this will now work – when you run it, you’ll see from swagger that it will expect you to pass the contents of MyClass into the endpoint. “That’s great,” I hear you ask, “But what if I want to register MyClass in the DI and pass that in?” To which I’ll respond: “Well – this actually works out of the box”:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here
builder.Services.AddSingleton<MyClass>(new MyClass() { SomeProperty = "aardvark"});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

The DI is clever enough to determine that you’ve registered a singleton, and so you don’t need that information passing into the endpoint. Again, I hear the reader query this: “All very well, but what if I haven’t registered the class in the DI, but I wanted to and forgot?” Again, I’d respond: “Ah – an excellent question!” You can, indeed force the issue: just as you can use the [FromBody] attribute to insist that the endpoint takes the value from the endpoint body, so you can use the [FromServices] attribute to tell it that you want the class to be resolved from the DI:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here
//builder.Services.AddSingleton<MyClass>(new MyClass() { SomeProperty = "aardvark"});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", ([FromServices]MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

The above code will run, and when you call the endpoint, it will crash, telling you that you haven’t told it what you want MyClass to be.

Summary

All pretty nifty if you ask me – it works by default for the 90% case, and for the last 10% there are some simple attributes you can set to force the issue.

I should probably just add that, generally speaking, adding functionality directly to the controller / endpoint method is a bad idea. Most of the time, what you’ll really want to do in the endpoint is simply resolve a service and call a method there.

References

https://benfoster.io/blog/mvc-to-minimal-apis-aspnet-6/

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis

https://www.hanselman.com/blog/exploring-a-minimal-web-api-with-aspnet-core-6

Printing Extended Ascii Characters in Console Apps

I’ve written a fair few articles on using console apps, especially for the purpose of writing games. This post, for example, is the start of a series on creating a snake game using a C# console app.

Disclaimer: this post is largely just bringing together the information listed at the bottom of this article into a single place. I take no credit for the code herein (with the exception of the rectangle itself).

One thing that used to be common knowledge, back when I wrote games and apps in Turbo Pascal and C, was that you could use the extended character set in order to create a rudimentary set of graphics. In this post, I’m going to cover my re-discovery of those characters. We’ll draw a rectangle in a console app.

Back in the Borland days, the way to add these to your program was to hold Alt-Gr and then type the ASCII code. In .Net Framework, these were, broadly, included; however, in .Net Core+, you need to add a NuGet package:

<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />

Once you’ve done this, you need the following magic line to allow you to actually use the code pages:

System.Text.Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

Before we get into using this, let’s see what’s available. Firstly, this is how you would list the code pages:

var encs = CodePagesEncodingProvider.Instance.GetEncodings();
foreach (var enc in encs.OrderBy(a => a.Name))
{
    Console.WriteLine($"{enc.Name} {enc.CodePage}");
}

The specific code page that we’re interested in is IBM437:

Encoding cp437 = Encoding.GetEncoding("IBM437");
byte[] source = new byte[1];
for (byte i = 0x20; i < 0xFE; i++)
{
    source[0] = i;
    Console.WriteLine($"{i}, {cp437.GetString(source)}");
}

We can display a single character like this:

Console.WriteLine($"{cp437.GetString(new byte[1] { 217 })}");

Okay, so we now have all the tools, it’s just assembling them in the right order:

Console.Write($"{cp437.GetString(new byte[1] { 218 })}");
for (int i = 1; i < 20; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 196 })}");
}
Console.WriteLine($"{cp437.GetString(new byte[1] { 191 })}");

for (int i = 1; i < 5; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 179 })}");
    Console.Write(new String(' ', 19));
    Console.WriteLine($"{cp437.GetString(new byte[1] { 179 })}");
}

Console.Write($"{cp437.GetString(new byte[1] { 192 })}");
for (int i = 1; i < 20; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 196 })}");
}
Console.WriteLine($"{cp437.GetString(new byte[1] { 217 })}");

References

https://stackoverflow.com/questions/28005405/how-can-i-turn-on-special-ascii-characters-in-visual-studio

https://stackoverflow.com/questions/58439415/visual-studio-2019-dont-recognize-ascii-characters

https://stackoverflow.com/questions/17619279/extended-ascii-in-c-sharp

https://stackoverflow.com/questions/49215791/vs-code-c-sharp-system-notsupportedexception-no-data-is-available-for-encodin

https://stackoverflow.com/questions/50858209/system-notsupportedexception-no-data-is-available-for-encoding-1252

Running Hangfire with .Net 6 and Lite Storage

One of the things that I’ve been looking into recently is Hangfire. I’ve used this in the past, but not for a while, and so I wanted to get familiar with it again. I imagine there’ll be a couple more posts on the way, but in this one, I’m going to detail how to get Hangfire running using .Net 6, and a Lite DB Storage. I did initially intend to create this with a SQLite DB; however, when I did, I found that the Hangfire messages were sending multiple times; after some searching, I came across this GitHub issue, which suggested the Hangfire.Lite storage.

In this particular article, we’ll look specifically at running Hangfire with an Asp.Net MVC project. In .Net 6, the default template looks like this:

The first step here is that you’ll need some additional refences; here’s the csproj:

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

	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Hangfire.AspNetCore" Version="1.7.27" />
		<PackageReference Include="Hangfire.Core" Version="1.7.27" />
		<PackageReference Include="Hangfire.LiteDB" Version="0.4.1" />
	</ItemGroup>

</Project>

In addition to the Hangfire Core and AspNetCore projects, there’s the LiteDB package.

The next step is, in program.cs:

using Hangfire;
using Hangfire.LiteDB;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

// HF
builder.Services.AddHangfire(configuration =>
{
    configuration.UseLiteDbStorage("./hf.db");    
});
builder.Services.AddHangfireServer();
// HF - End

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
. . .

That’s actually it – that’s all you need to add Hangfire! However, it won’t, technically speaking, do anything. To test, edit the HomeCrontroller.cs:

        public IActionResult Index()
        {
            var id = BackgroundJob.Enqueue(() => DoSomething());
            return View();
        }

        public void DoSomething()
        {
            Console.WriteLine("Test");
            Console.WriteLine($"test2 {Guid.NewGuid()}");
        }

When you run the project, you should see Hangfire fire up and start printing outputs:

When you run it, don’t be too impatient – but it should print the test output.

References

https://github.com/HangfireIO/Hangfire/issues/1025

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0

https://docs.hangfire.io/en/latest/getting-started/index.html

Implementing a Sidecar Pattern Without Kubernetes

A sidecar pattern essentially allows a running process to communicate with a separate process (or sidecar) in order to offload some non-essential functionality. This is typically logging, or something similarly auxiliary to the main function. You tend to find sidecars in Kubernetes clusters (in fact, you can have more than one container in a pod for exactly this reason); however, the pattern is just that: a pattern; and can therefore apply to pretty much anything.

In this post, I’m going to cover how you might set-up two independent containers, and then implement the sidecar pattern using docker compose.

Console App

The code for this is going to be in .Net Core 6, so let’s create a console app:

It will be easier later on if you follow the following directory structure for the projects:

sidecar-test
	console-app
	web-api

For the purpose of the console app, just put it in a parent directory – so the code lives in /sidecar-test/console-app (we’ll come back to the API).

We’ll need to add docker support for this, which you can do in Visual Studio (right click the project and add docker support):

Once you’ve created the docker build file, you may need to edit it slightly; here’s mine:

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src

COPY ["sidecar-test.csproj", "sidecar-test/"]
WORKDIR "/src/sidecar-test"
RUN dotnet restore "sidecar-test.csproj"
COPY . .

RUN dotnet build "sidecar-test.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "sidecar-test.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "sidecar-test.dll"]

If you find that the dockerfile doesn’t work, then you might find the following posts helpful:

Beginner’s Guide to Docker – Part 2 – Debugging a Docker Build


Beginner’s Guide to Docker – Part 3 – Debugging a Docker Build (Continued)

If you’d rather not wade through that, then the Reader’s Digest version is:

docker build -t sidecar-test .

Once the docker files are building, experiment with running them:

docker run --restart=always -d sidecar-test

Assuming this works, you should see that the output shows Hello World (as per the standard console app).

Web API

For the web API, again, we’re going to go with the default Web API – just create a new project in VS and select ASP.Net Core Web API:

Out of the box, this gives you a Weather Forecaster API, so we’ll stick with that for now, as we’re interested in the pattern itself and not the specifics.

Again, add docker support; here’s mine:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src

COPY ["sidecar-test-api.csproj", "sidecar-test-api/"]
WORKDIR "/src/sidecar-test-api"
RUN dotnet restore "sidecar-test-api.csproj"
COPY . .

RUN dotnet build "sidecar-test-api.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "sidecar-test-api.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "sidecar-test-api.dll"]

You should now be able to build that in a similar fashion as before:

docker build -t sidecar-test-api .

You can now run this, although unlike before, you’ll need to map the ports. The port mapping works by mapping the external port to the internal port – so to connect to the mapping below, you would connect to http://localhost:5001, but that will connect to port 80 inside the container.

docker run --restart=always -d -p 5001:80 sidecar-test

Now that you can connect to the Web API, let’s now implement the sidecar pattern.

Docker Compose

We now have two independent containers; however, they cannot, and do not, communicate. Let’s remind ourselves of the directory structure that we decided on at the start of the post:

sidecar-test
	console-app
	web-api

We now want to open the parent sidecar-test directory. In there, create a file called docker-compose.yml. It should look like this:

version: '3'
services:
  sidecar-api:
    build: .\sidecar-test-sidecar\web-api
    ports: 
      - "5001:80"
  sidecar-program:
    build: .\sidecar-test\console-app
    depends_on: 
      - "sidecar-api"

There’s lots to unpick here. Let’s start with services – this contains all the individual services that we’re going to run in this file; each one has a build section, which tells compose to call docker build on the path specified. You can create the image separately and replace this with an image section. The ports section maps directly to the docker -p switch.

There’s two other important parts of this, this first is the service name (sidecar-api, sidecar-program) – we’ll come back to that shortly. The second is the depends_on – which tells docker compose to ensure that the dependant is built and running first (we could switch the order of these and it would still start the API first).

Running The Sidecar

Finally, we can run our sidecar. You can do this by navigating to the parent sidecar-test directory, and typing:

docker compose up

If you’re changing and running the code, and using PowerShell, you can use the following command:

docker-compose down; docker-compose build --no-cache; docker-compose up

Which will force a rebuild every time.

However, if you run this now, you’ll notice that, whilst it runs, it does nothing.

Linking The Two

Docker Compose has a nifty little feature – all the services in the compose file are networked together, and can be referenced by using their service name.

In the console app, change the main method to:

        public static async Task Main(string[] args)
        {
            Console.WriteLine("Get Weather");
            var httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri("http://sidecar-api");
            var result = await httpClient.GetAsync("WeatherForecast");
            result.EnsureSuccessStatusCode();
            var content = await result.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }

We won’t focus on the fact that I’m directly instantiating HttpClient here; but notice how we are referencing the API, by http://sidecar-api, and because they are both part of the same compose file, this just works.

If we now recompile the docker files:

docker-compose down; docker-compose build --no-cache; docker-compose up

We should have a running sidecar – admittedly it doesn’t behave like a sidecar, but that’s just why it gets called, not the architecture.

References

https://docs.docker.com/compose/