Scott Hanselman

A .NET 6 Minimal API Todo example Playground

September 02, 2021 Comment on this post [3] Posted in DotNetCore | Open Source | Web Services
Sponsored By

Minimalism from UnsplashI really like minimal Web APIs. I've liked the idea for years. With .NET 6, it's starting to happen! Damian Edwards has an interesting minimal API Playground on his GitHub and Maria Naggaga did a great talk on Minimal APIs in .NET 6 that's up on YouTube!

Let's explore! I'm running the latest .NET 6 and you can run it on Windows, Mac, or Linux and I cloned it to a folder locally.

There's two versions of a complete Todo API in this sample, one using Entity Framework Core and one using Dapper for data access. Both are lightweight ORMs (object relational mappers). Let's explore the Dapper example that uses SQLite.

The opening of the code in this example doesn't require a Main() which removes a nice bit of historically unneeded syntactic sodium. The Main is implied.

using System.ComponentModel.DataAnnotations;
using Microsoft.Data.Sqlite;
using Dapper;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("TodoDb") ?? "Data Source=todos.db";
builder.Services.AddScoped(_ => new SqliteConnection(connectionString));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

At this point we've got a SQLite connection string ready to go scoped in the Services Dependency Injection Container (fancy words for "it's in the pile of stuff we'll be using later") and we've told the system we want a nice UI for our Open API (Swagger) web services description. It's WSDL for JSON, kids!

Then a call to EnsureDb which, ahem, ensures there's a database!

await EnsureDb(app.Services, app.Logger);

What's it look like? Just a little make this table if it doesn't exist action:

async Task EnsureDb(IServiceProvider services, ILogger logger)
{
logger.LogInformation("Ensuring database exists at connection string '{connectionString}'", connectionString);

using var db = services.CreateScope().ServiceProvider.GetRequiredService<SqliteConnection>();
var sql = $@"CREATE TABLE IF NOT EXISTS Todos (
{nameof(Todo.Id)} INTEGER PRIMARY KEY AUTOINCREMENT,
{nameof(Todo.Title)} TEXT NOT NULL,
{nameof(Todo.IsComplete)} INTEGER DEFAULT 0 NOT NULL CHECK({nameof(Todo.IsComplete)} IN (0, 1))
);";
await db.ExecuteAsync(sql);
}

Next we'll "map" some paths for /error as well as paths for our API's UI so when I hit /swagger with a web browser it looks nice:

if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error");
}

app.MapGet("/error", () => Results.Problem("An error occurred.", statusCode: 500))
.ExcludeFromDescription();

app.MapSwagger();
app.UseSwaggerUI();

Then sprinkle in a little Hello World just to give folks a taste:

app.MapGet("/", () => "Hello World!")
.WithName("Hello");

app.MapGet("/hello", () => new { Hello = "World" })
.WithName("HelloObject");

You can see how /hello would return a JSON object of Hello: "World"

What's that WithName bit at the end? That names the API and corresponds to 'operationId" in the generated swagger/openAPI json file. It's a shorthand for .WithMetadata(new EndpointNameMetadata("get_product")); which was surely no fun at all.

Now let's get some Todos from this database, shall we? Here's all of them and just the complete ones:

app.MapGet("/todos", async (SqliteConnection db) =>
await db.QueryAsync<Todo>("SELECT * FROM Todos"))
.WithName("GetAllTodos");

app.MapGet("/todos/complete", async (SqliteConnection db) =>
await db.QueryAsync<Todo>("SELECT * FROM Todos WHERE IsComplete = true"))
.WithName("GetCompleteTodos");

Lovely. But what's this Todo object? We haven't seen that. It's just a object that's shaped right. Perhaps one day that could be a record rather than a class but neither Dapper or EFCore support that yet it seems. Still, it's minimal.

public class Todo
{
public int Id { get; set; }
[Required]
public string? Title { get; set; }
public bool IsComplete { get; set; }
}

Let's get a little fancier with an API that gets a Todo but it might not find the result! It may produce an HTTP 200 OK or an HTTP 404 NotFound.

app.MapGet("/todos/{id}", async (int id, SqliteConnection db) =>
await db.QuerySingleOrDefaultAsync<Todo>("SELECT * FROM Todos WHERE Id = @id", new { id })
is Todo todo
? Results.Ok(todo)
: Results.NotFound())
.WithName("GetTodoById")
.Produces<Todo>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);

Don't be sad if you don't like SQL like this, it's just a choice amongst many. You can use whatever ORM you want, worry not.

A thought: The .Produces are used by the OpenAPI/Swagger system. In my mind, it'd be nice to avoid saying it twice as the Results.Ok and Results.NotFound is sitting right there, but you'd need a Source Generator or aspect-oriented post compilation weaver to tack in on after the fact. This is the only part that I don't like.

Go explore the code and check it out for yourself!


Check out our Sponsor! YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service

My Ultimate PowerShell prompt with Oh My Posh and the Windows Terminal

August 31, 2021 Comment on this post [17] Posted in Open Source | Win10 | Win11
Sponsored By

I've long blogged about my love of setting up a nice terminal, getting the prompt just right, setting my colors, fonts, glyphs, and more. Here's some of my posts.

I want to take a moment to update my pretty prompt post with a little more detail and a more complex PowerShell $PROFILE, due to some changes in Oh My Posh, PowerShell, and the Windows Terminal. I doubt that this post is perfect and I'm sure there's stuff here that is a little extra. But I like it, and this post will serve as my "setting up a new machine" post until I get around to writing a script to do all this for me in one line.

I love my prompt.

A pretty prompt in Terminal with Oh My Posh and a lot of Colors

Let's get you set up!

Get PowerShell

I don't mean Windows PowerShell (that's "classic" now) I mean the .NET Core-powered cross-platform PowerShell. There's a LOT of ways to get it but I'm a Store person usually so I can get PowerShell (and it'll auto update) from the Microsoft Store or just "winget install Microsoft.PowerShell" from the command line with winget.

Get Windows Terminal and set a default Shell

Get Windows Terminal if you don't already have it, you can get Windows Terminal free from the Store. If you don't have access to the Microsoft Store, the builds are published on the GitHub releases page. It comes with a lovely font called Cascadia Code...but...

Now that you have Windows Terminal, you'll notice that it knows that you have PowerShell installed and will add it to your Windows Terminal dropdown menu! You can set PowerShell as your default Profile - that's the one you'll get by default when you make a new Tab - in settings:

Set your Default Profile to PowerShell

Upgrade your Terminal/Console Fonts

I like fonts with lots of Glyphs so I also download and Install Caskaydia Cove Nerd Font Complete. This is the same Cascadia Code font but MODIFIED to include hundreds of special characters that you can use to make your prompt cooler.

IMPORTANT NOTE: The string literal name of this font for use in settings or VS Code is "CaskaydiaCove NF". If you're using Cascadia Code, there are different strings for each. The NUMBER ONE question I get is 'why don't my glyphs/fonts show up right in Windows Terminal/VS Code?' and the answer is almost always "you're using the wrong font string." It's usually either an extra space or a missing space, so don't be afraid to double check.
Cascadia Code

Remember that Windows Terminal has a lovely Settings UI but you can always click "open JSON file" to manage the settings.json as text if you prefer. Here's mine. Yours will be different and you should customize it! The Windows Terminal documentation is fantastic. Again, see how PowerShell is in BOLD? That's because it's my default.

Lots of Windows Terminal Profiles

Now, let's add a little...spice...

Add "Oh My Posh" to your Shell

Oh My Posh has amazing docs so check them out. Do note that some stuff has changed, especially from v2 to v3.

EXCITING NOTE: Oh My Posh is portable and works on any shell, so I use it on both my "Pwsh" (PowerShell) in Windows and my Bash shells on WSL running Ubuntu.

You can install Oh My Posh with with PowerShell's "Install-Module" or with the platform-specific install instructions. I used the latter, which is somewhat new, but it's tomato/tomato, so use what works for you.

Again, read the docs but the idea on Windows is basically this (or get it from GitHub):

winget install JanDeDobbeleer.OhMyPosh
# restart shell to reload PATH

Then edit $PROFILE and add the following line, remembering at this point that oh-my-posh is an executable on the PATH.

oh-my-posh --init --shell pwsh --config ~/jandedobbeleer.omp.json | Invoke-Expression

I have changed my Oh My Posh from Jan's default to include my own stuff, and I keep my latest up in a GitHub Gist and also in my DropBox/OneDrive so it's always syncing to all my machines. Mine is this, after I download from my gist.

oh-my-posh --init --shell pwsh --config D:/Dropbox/ohmyposhv3-2.json | Invoke-Expression

Yours will vary. Again, read the docs and experiment! Once added, reload your profile for the changes to take effect, or restart your shell.

. $PROFILE

That .json file is filled with "segments" that are documented on the Oh My Posh site in a lot of detail. Overwhelming detail. You can add your computer's battery, your Azure Subscription, the dotnet or node version of your current folder, really anything. Even your Spotify songs. I'm going to make one that show my Blood Sugar.

Go explore Oh My Posh Themes and then modify them with your own additional Segments.

Again, note that your fonts will need the right glyphs or it will look weird.

Here's a GOOD prompt:

This prompt has nice glyphs

Here's a BAD prompt with an issue!

This prompt has weird squares

Why is it wrong? Either the .json file that is your config has been saved wrong or corrupted the Unicode Glyphs, or you've got a font that doesn't have those glyphs.

Re-assert your Git segment in Oh My Posh

Some folks want full git info, status, added, modified, untracked, etc and others just want the current git branch. Check the Git segment and the Posh Git segment to make sure you are getting the performance AND information you need.

I needed to turn on "display_stash_count" and "display_upstream_icon" in my config json, like this:

{
"type": "git",
"style": "powerline",
"powerline_symbol": "",
"invert_powerline": false,
"foreground": "#193549",
"background": "#fffb38",
"leading_diamond": "",
"trailing_diamond": "",
"properties": {
"display_status": true,
"display_stash_count": true,
"display_upstream_icon": true
}
},

Again, this is all optional and affect performance slightly, but be aware of these properties. I believe I have these set the right way I want them in my public gist. Here is me moving around my source code with "z" in stead of cd, but note the prompt changes.

My prompt shows my Git branch

Turn your PowerShell directories up to 11 with Terminal-Icons

Is your prompt not extra enough? That's because your directory listing needs color AND cool icons!

Install-Module -Name Terminal-Icons -Repository PSGallery

And then add one line to my $profile (edit with "code $profile"):

Import-Module -Name Terminal-Icons

Sweet!

Cool Icons and color in DIR

How far is too far?

At this point you're basically done, but I also LOVE PSReadLine. It's great generally but also nice for bash and Emacs types who are moving to PowerShell or use PowerShell for work.

I've added features like "ctrl shift b" at the command line will run "dotnet build." Why? Because I can and because it's muscle memory so I'm making my prompt work for me.

You can also add Predictive Autocomplete to your prompt if you like but I'll leave that as an exercise to the reader! My PowerShell profile is on a public gist, and while it's not perfect and likely has issues, it works for me!

Enjoy! Thanks to the Windows Terminal Team and the always lovely Jan De Dobbeleer from Oh My Posh, as well as Brandon Olin from Terminal Icons.


Check out our Sponsor! YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service

Hanselminutes is Fresh Air for Developers and has over 800 episodes of fresh tech from fresh faces

August 22, 2021 Comment on this post [7] Posted in
Sponsored By

Hanselminutes PodcastHey friends! I wanted remind you about my podcast! It's http://hanselminutes.com/ and just a few weeks ago I published my 800th episode! My first episode was in January of 2006 so that's over 15 years of shows. And, if I may be a little boastful for a moment, they are pretty darn good. Maybe the first 400 were a little rough but these last 400 have been ROCK SOLID. Just kidding.

Seriously, though, this 30 minute long tech show has diverse topics and new faces you haven't heard on other podcasts. If you check out over 800 episodes here https://www.hanselminutes.com/episodes you can search by Title, Guest, OR search all the Transcripts! There's over 400 hours of shows and you can search for the topics you want.

Subscribe with your favorite podcast app, the raw RSS is here. We're also available on basically every podcast app out there, including, but not limited to:

If you enjoy the show, the best thing you can do to help me is SPREAD THE WORD! Tell a friend, share and episode or favorite code, but above all GET FOLKS TO SUBSCRIBE.

The world is littered with podcasts that gave up after 9 episodes. There's a ton of average talks shows that ramble on. I've worked really hard - at night, as this is not my day job! - to not only bring you the best guests, but to read their papers, books, and thesis, and ask the questions that YOU would have if you were here with me!

Sometimes I even put the Hanselminutes Podcast on YouTube and the results are truly special and heartbreakingly emotional.

Thanks for listening, and thanks for sharing!


Sponsor: YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service

Carter Community for ASP.NET Core means enjoyable Web APIs on the cutting edge

August 19, 2021 Comment on this post [4] Posted in ASP.NET | DotNetCore | Open Source
Sponsored By

I blogged about the open source Carter Community Project in 2019. Let's check in and see what's going on today in 2021!

With .NET 6 on the near horizon, one notes that Carter has a net6 branch. Per their website, this is the goal of the Carter framework:

Carter is framework that is a thin layer of extension methods and functionality over ASP.NET Core allowing code to be more explicit and most importantly more enjoyable.

As of today you can bring Carter into your .NET 6 projects like this:

dotnet add package Carter --version 6.0.0-pre2

And the .NET 6 samples are under active development! Let's bring it down with a clone, switch to the net6 branch and give it a go.

Here's as simple Web API sample with Carter that returns a list of actors at localhost:5001/actors

using Carter;
using CarterSample.Features.Actors;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;


var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IActorProvider, ActorProvider>();
builder.Services.AddCarter();

var app = builder.Build();

app.MapCarter();
app.Run();

Nice! This is using new .NET 6 features so there's no Main(), it's implied. The builder has an ActorProvider added as a Singleton. I bet we'll use that when we ask for /actors in our browser or favorite HTTP API client.

public class ActorsModule : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapGet("/actors", (IActorProvider actorProvider, HttpResponse res) =>
{
var people = actorProvider.Get();
return people;
});
...
}
}

This is nice and clean. Everything is using Dependency Injection so no one is "newing up" an Actor. You'll note also that returning the Actors as JSON is implied when we return the IEmumerable<Actor> that comes from actorProvider.Get().

In fact, the whole Actor Module is just 80 lines so I'll include it here:

public class ActorsModule : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapGet("/actors", (IActorProvider actorProvider, HttpResponse res) =>
{
var people = actorProvider.Get();
return people;
});

app.MapGet("/actors/{id:int}", (int id, IActorProvider actorProvider, HttpResponse res) =>
{
var person = actorProvider.Get(id);
return res.Negotiate(person);
});

app.MapPut("/actors/{id:int}", async (HttpRequest req, Actor actor, HttpResponse res) =>
{
var result = req.Validate<Actor>(actor);

if (!result.IsValid)
{
res.StatusCode = 422;
await res.Negotiate(result.GetFormattedErrors());
return;
}

//Update the user in your database

res.StatusCode = 204;
});

app.MapPost("/actors", async (HttpContext ctx, Actor actor) =>
{
var result = ctx.Request.Validate<Actor>(actor);

if (!result.IsValid)
{
ctx.Response.StatusCode = 422;
await ctx.Response.Negotiate(result.GetFormattedErrors());
return;
}

//Save the user in your database

ctx.Response.StatusCode = 201;
await ctx.Response.Negotiate(actor);
});

app.MapDelete("/actors/{id:int}", (int id, IActorProvider actorProvider, HttpResponse res) =>
{
actorProvider.Delete(id);
return Results.StatusCode(204);
});

app.MapGet("/actors/download", async (HttpResponse response) =>
{
using (var video = new FileStream("earth.mp4", FileMode.Open)) //24406813
{
await response.FromStream(video, "video/mp4");
}
});

app.MapGet("/empty", () => Task.CompletedTask);

app.MapGet("/actors/sample", () => Task.CompletedTask);

app.MapPost("/actors/sample", () => Task.CompletedTask);

app.MapGet("/nullable", () => Task.CompletedTask);
}
}

Note the API example at /actors/download that shows how to return a file like an MP4. Nice and simple. This sample also includes thoughtful validation code with FluentValidation extension methods like ctx.Request.Validate().

Carter is opinionated but surprisingly flexible. You can use two different routing APIs, or clean and performant Endpoint routing:

this.Get("/", (req, res) => res.WriteAsync("There's no place like 127.0.0.1")).RequireAuthorization();

It even supports OpenAPI out of the box! Carter has an active Slack as well as Templates you can add to make your next File | New Project easier!

dotnet new -i CarterTemplate
The following template packages will be installed:
CarterTemplate

Success: CarterTemplate::5.2.0 installed the following templates:
Template Name Short Name Language Tags
--------------- ---------- -------- ------------------------------
Carter Template carter [C#] Carter/Carter Template/NancyFX

There's a lot of great innovation happening in the .NET open source space right now.

Carter Source Code

Carter is just one cool example. Go check out Carter on GitHub, give them a Star, try it out and get involved in open source!


Sponsor: YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service

Hanselminutes is Fresh Air for Developers and has over 800 episodes of fresh tech from fresh faces

August 17, 2021 Comment on this post [0] Posted in
Sponsored By

Hanselminutes PodcastHey friends! I wanted remind you about my podcast! It's http://hanselminutes.com/ and just a few weeks ago I published my 800th episode! My first episode was in January of 2006 so that's over 15 years of shows. And, if I may be a little boastful for a moment, they are pretty darn good. Maybe the first 400 were a little rough but these last 400 have been ROCK SOLID. Just kidding.

Seriously, though, this 30 minute long tech show has diverse topics and new faces you haven't heard on other podcasts. If you check out over 800 episodes here https://www.hanselminutes.com/episodes you can search by Title, Guest, OR search all the Transcripts! There's over 400 hours of shows and you can search for the topics you want.

Subscribe with your favorite podcast app, the raw RSS is here. We're also available on basically every podcast app out there, including, but not limited to:

If you enjoy the show, the best thing you can do to help me is SPREAD THE WORD! Tell a friend, share and episode or favorite code, but above all GET FOLSK TO SUBSCRIBE.

The world is littered with podcasts that gave up after 9 episodes. There's a ton of average talks shows that ramble on. I've worked really hard - at night, as this is not my day job! - to not only bring you the best guests, but to read their papers, books, and thesis, and ask the questions that YOU would have if you were here with me!

Sometimes I even put the Hanselminutes Podcast on YouTube and the results are truly special and heartbreakingly emotional.

Thanks for listening, and thanks for sharing!


Sponsor: YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.