Creating a New Project
For the sake of consistency, we’ll go through this exercise starting with a brand-new, freshly-minted ASP.NET Core application.
In Visual Studio 2017, go ahead and create a new ASP.NET Core 2 project. Here are the settings I used:
Go ahead and build and run the app, just to make sure everything works. I’ll wait.
Finished? Good!
Now we’re ready to start adding our code!
Add the Markup
We’re going to use normal Bootstrap markup to display our alert message. We want this alert message to potentially be shown anywhere in our application, so it makes sense to place the message in our primary layout.
I like to keep things as componetized as I can, so we’ll be making a partial view. Let’s pretend that view already exists.
In /Views/Shared/_Layout.cshtml
, let’s go ahead and call our (not-yet-created) partial, right before the existing RenderBody
call:
Now create a new partial view in the /Views/Shared
folder named _StatusMessages.cshtml
, and replace its contents with this:
That’s cool and all, but we don’t want our partial to show on every page with the same static text. Don’t worry though, we’ll come back and fix that shortly!
“If we had an extension method, it would look like this…”
The goal of this exercise is to allow us to easily attach alert messages to action results, and have those alerts show up in our UI. We’ll accomplish that using extension methods.
Let’s pretend that we already have our extension methods in place. What we’d like to be able to do is something like this for our actions that return views…
And we want the same extension method to work for methods that perform redirects, too:
We’re going to create extension methods that work for both of these cases. And we’ll define one extension method for each of the alert types that Bootstrap supports:
NOTE: The names you use don’t really matter. I chose these names because I’m using Bootstrap. You could easily adapt this approach for any other CSS framework, or you could choose to decouple the extension names here from the target CSS framework entirely. It’s totally up to you!
Creating the Extension Methods
We know what we need to build now. Let’s make it real!
We’ll start by creating a new class in our project (in a new folder!), “`/Extensions/Alerts/AlertExtensions.cs““.
Here are the stubs for our extension methods:
Each is basically the same: it calls a helper, Alert
, with the parameters necessary to show the various types of alerts.
That Alert
method is where the magic happens!
Yep, that’s it! All done.
…
Oh wait, no it isn’t! AlertDecoratorResult
doesn’t exist yet! But once it does, it will decorate the real action result, layering on our alert message behavior.
Now, this approach isn’t perfect. One of the things I don’t like about it is that it complicates unit testing. Now your tests will have to unwrap the decorated result if they want to make any assertions about the real, underlying result. But, there are elegant ways to deal with that, which I’ll cover in a future post.
For now, let’s create our actual decorator result!
Creating the AlertDecoratorResult
Let’s go ahead and make a new class, /Extensions/Alerts/AlertDecoratorResult.cs
:
So far, all our result is doing is grabbing the real result, the alert type, title, and body, and storing them in properties so that we can access them from our tests. We need to put those somewhere useful… somewhere that we can access them later from within our view. And for that, we’ll use TempData
!
In .NET Core, we can ask for a ITempDataDictionaryFactory
whenever we want to access TempData
. Let’s do that in our ExecuteResultAsync
method, then let’s store our alert properties there!
If you aren’t familiar with TempData, just think of it as session data that is only accessible once. It will survive one full roundtrip, even across Post-Redirect-Get requests, and that’s it.
Want to know more about
TempData
? Check out the Microsoft docs!
One question that is immediately asked when I show this technique: “why use TempData instead of ViewBag??” ViewBag works fine for ViewResults! It does not work for anything else, including redirects.
Wrapping it Up: Reading the Alert in Razor
We now have our alert stored in TempData
, just waiting for us to use it. So let’s jump back over to our _StatusMessage.cshtml
partial view, and let’s fix it so that it’s reading our alert:
Next Steps
I’ve put together a small sample app to show off this technique. You can clone it from its repo here: HeroicSamples.BootstrapAlerts.
This code works, but there’s a lot we could do to improve it. There are some magic strings lingering in the code, and it won’t actually work with JSON results yet, either. But you get the idea!
In a future post, I’ll show you how you can polish this up a bit further, how you can apply it to a JavaScript application, and how to write specs around decorated results cleanly.
Stay tuned!