Category Archives: .Net 6

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