Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt.
Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt.
exclude attribute on the
EnvironmentTagHelper lets you easily tell the
EnvironmentTagHelper to render in all Environments EXCEPT the one(s) you specify. There’s also a new
include attribute that behaves the same as
names did in ASP.NET Core 1.
A comparison between ASP.NET Core 1 and 2:
Environments in ASP.NET Core
In ASP.NET 4 (and previous versions), there was no concept of an Environment. Even though in almost every job, there are the concepts of Environments such as Dev, Staging, UAT, QA, Production, etc. You were essentially on your own to implement your concept of Environments. Sometimes enlisting the help of something horrible like compiler directives.
In ASP.NET Core, they built the Environment concept right into the stack. There are 3 Environments by default – Development, Staging, and Production, but you can add your own custom ones if you want. The Environment is controlled by an Environment variable called
ASPNETCORE_ENVIRONMENT. This allows you to inspect the current Environment using an
IHostingEnvironment and run code like this:
You can find more on Environments on the official documentation here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments.
Environment Tag Helpers in ASP.NET Core 1
IHostingEnvironment, In ASP.NET Core 1 you could conditionally load some markup based on the Environment you were in via an
But that was a little brittle, given you can make your own custom Environments, in addition to the built-in Development, Staging, and Production Environments. Essentially, if you introduce a custom Environment, then you have to CTRL + F for all your
EnvironmentTagHelpers and add in your new custom environment. That’s a real pain.
exclude attributes on the Environment Tag Helper in ASP.NET Core 2
To solve this, the ASP.NET team introduced the
exclude attributes on the
EnvironmentTagHelper. Let’s look at
Exclude does about what you would think. It will only render the contents of the EnvironmentTagHelper if the current environment does NOT match the Environment specified. So
<Environment exclude="Development"> would NOT render the contents in Development, but would render in any other Environment (such as Staging, Production, or a custom Environment you specified).
Include, on the other hand, operates the same as the
names attribute we had in ASP.NET Core 1. It’s just there to provide consistent naming as the opposite of
exclude. It will only run if the current environment DOES match the Environment specified.
So those used together would look a little something like this:
names attribute still exists… but don’t use it
names attribute still exists for backwards compatibility, but I don’t see a reason to use it anymore over
include. I don’t have a crystal ball, but I’d say it’s likely
names will be removed in a later version of ASP.NET Core now that we have
exclude. Although the team seems committed to backwards compatibility at this point in time.
Personally, I think it’d be cool if the ASP.NET team shipped codemods in the form of Roslyn Analyzers to auto upgrade your code to the latest API’s, and then just make a clean break. In this case, anywhere you’d use
names would automatically upgrade to use
include. As a developer, you could still inspect the changes the Roslyn Analyzer made when reviewing your Commit/Pull Request to make sure the Analyzer did the right thing. The React team at Facebook does this, but they also have a financial incentive to ship upgrade-friendly stuff like that when Facebook has over 10M lines of code. So…. yeah…
What if I use include and exclude in the same Tag Helper and use the same Environment in both?
If you screw up like this, ASP.NET Core will do the “safe” thing by default and NOT render in that environment. This is very similar to how NTFS works where a deny will take precedence over an allow.
So if you do something like this:
Then the content will not show in the Development or Production Environments, but it will show in Staging, since it is part of the
include and not in the
In real life, I don’t know why you’d use both together. I would always just pick a whitelist (
include) or a blacklist (
exclude). But it’s probably a good thing to keep in mind.
This will make life so much simpler to deal with the
EnvironmentTagHelper especially when used with custom Environments.
If you’re interested in the ideas they threw around to solve this problem, before ultimately landing on
exclude, you can read the GitHub issue here. You can also see the commit that went into making this feature work.
I was playing around with ASP.NET Core 2.0 before I give a talk on what’s new on it in 4 days, and I came across this and just had to share… now it’s back to rehearsing!
What is Prettier?
One of the things I love about it is it’s not rigid with its rules. It makes some “logical” choices that I would make myself. For instance, Prettier will make a short const array declaration a one liner, but a declaration with a bunch of items in the array, it will split out into multiple lines to avoid horizontal scrolling (see demo GIF at the end). I love that.
VS Code Integration
Overriding Prettier settings in VS Code
By default Prettier uses 2 spaces for tabs. You can increase that to the VS Code default of 4 if you want extremely easily:
- Hit CTRL + , or Command + , on Mac to bring up the Settings.json
- Search for Prettier
- Click to the left of prettier.tabWidth on the left hand side and click Copy To Settings
- On the right side so you can change it to 4 and then hit Save to save your settings.json file.
You can scan the rest of the prettier options that show up in the search to see if there’s ones you want to override.
Formatting is something I never worry about anymore
I find when using Prettier, along with the Format On Save option in VS Code, I don’t think about how to format my code anymore. I don’t ever wonder “oh should I put this on multiple lines or keep it all in one line?” Instead, I focus on solving my problem, hit Save and let Prettier do the rest. It’s shocking how liberating this is. I end up with consistent formatting across my entire project instead of just doing whatever I felt was best at the time.
Prettier in Action
Rather than Formatting your code using Shift + Alt + F on Windows or on Shift + Option + F on Mac, consider just formatting on save instead.
To turn this on go to:
- Or just hit CTRL + Comma on Windows to bypass steps 1-3
- search for editor.formatOnSave and change that to true
- Note: this UI is a little confusing. The left side is the default settings and the right side is your custom settings you’ve overriden. You can either copy the setting over to the right or hover over the setting on the left and click the pencil and select true.
There are tons of times where you want to delete from the right of your cursor, while preserving everything to the left of your cursor.
Today the most common way is to do SHIFT + END to highlight it all and then hit DEL, such as below.
However, there’s a way to delete to the end of the line by binding a keyboard shortcut to Edit.DeleteToEOL. By default this is not bound to any keyboard shortcut, so you’ll have to pick one.
If you haven’t overrode a keyboard shortcut before, you can go to Tools => Options => Keyboard, and then type Edit.DeleteToEOL in the “Show commands containing” text box. You can then type a keyboard
I removed all previous bindings to CTRL + L and bound Edit.DeleteToEOL to that, but you can pick whatever you want.
Now when I hit CTRL + L it removes everything after my cursor, saving me some keyboard strokes.
Hope this helps.
CAVEAT: This is pre-released software. There isn’t even a NuGet package for it yet. The GitHub repo lives here – https://github.com/dotnet-architecture/HealthChecks that I cloned and manually added a project reference to a new demo app. There is a plan for it to get made into a NuGet package, but that hasn’t happened yet.
With that said – I will keep this post updated as I see changes being made, and will eventually remove the Caveat above.
Register your Health Checks in your
Inject in an
IHealthCheckService and call
CheckHealthAsync to run all your Health Checks, or another method like
RunGroupAsync to run a subset of the Health Checks you registered in
What is a Health Check?
Health Checks are pretty much what the name implies. They are a way of checking to see if your application is healthy. Health Checks have become especially critical as more and more applications are moving to a microservice-style architecture. While microservice architectures have many benefits, one of the downsides is there is a higher operations overhead to ensuring all of these services are running. Rather than monitoring the health of one majestic monolith, you need to monitor the status of many different services, which are usually responsible for one thing and one thing only. Heatlh Checks are usually used in combination with a service discovery tool such as Consul that monitor your microservices for when they become healthy and unhealthy. If you use Consul for service discovery as well, Consul will automatically route traffic away from your unhealthy microservices and only serve traffic to your healthy microservices… which is awesome.
How do I implement a Health Check?
There are a few different ways to do Health Checks, but the most common way is exposing an HTTP endpoint to your application dedicated to doing Health Checks. Typically you will return a status code of 200 if everything is good, and any non-2xx code means something went wrong. For example, you might return a 500 if something went wrong along with a JSON payload of what exactly went wrong.
Common scenarios to Health Check
What you Health Check will be based on what your application/microservice does, but some common things:
- Can my service connect to a database?
- Can my service query a 3rd party API?
- Likely making some read-only call
- Can my service access the file system?
- Is the Memory and/or CPU above a certain threshold?
Looking at the Microsoft.AspNetCore.HealthChecks package
Microsoft is on the verge of shipping a set of Health Check packages to help you solve this problem in a consistent way. If you look in the GitHub repo you will notice there is also a package for ASP.NET 4.x as well under the Microsoft.AspNet.HealthChecks namespace. There is a samples folder on that GitHub repo that contains how to wire that up if you’re interested in ASP.NET 4.x. I’m going to focus on the ASP.NET Core package for this blog post.
The Microsoft.AspNetCore.HealthChecks package targets
netcoreapp1.0, but I suspect this will change to be either
netstandard2.0 by the time this RTM’s. The ASP.NET 4 project targets
net461, and all the other libraries target
netstandard1.3which works with both .NET Core and Full Framework.
The basic flow is that you register your health checks in your IoC container of choice (such as the built-in Microsoft one, although I prefer SimpleInjector due to the fantastic feature set, blazing speed, and ridiculously good documentation, but I’ll just use the built-in one for these demos). You register these Health Checks via a fluent
HealthCheckBuilder API in your
ConfigureServices method. This
HealthCheckBuilder will build a
HealthCheckService and register it as an
IHealthCheckService in your IoC container.
That looks something like this:
So to run your Health Checks you inject in an
IHealthCheckService into your Controller and then call
There are a few things to note here:
- You get back a
CompositeHealthCheckResultwhich is a summary of all of your health checks that you registered in your
AddHealthChecksmethod in your
CompositeHealthCheckResultclass has a
CheckStatusproperty which is an enum. That enum has 4 options –
Unknown. You can determine what you want to do with each of those. In my simple example above, I consider anything other than
Healthyto be a problem and return a 500 if it’s not
You can also loop over the results of the
CompositeHealthCheckResult by looking at the
Results property and get even more detail about what exactly happened.
You can optionally run a single Health Check by calling
RunCheckAsync and supplying the name of the Health Check that you registered in your
ConfigureServices method (more on that later).
Out of the box Health Checks
Microsoft ships quite a few Health Checks out of the box that fit into the Common Scenarios section above. They are:
- URL Health Check via
- SQL Server Health Check via
- PrivateMemorySizeCheck via
- VirtualMemorySizeCheck via
- WorkingSetCheck via
- A few Azure Health Checks (such as BLOB Storage, Table Storage, File Storage, and Queue storage).
Let’s take a look at the URL Health Check and the SQL Server Health Check.
URL Health Checks
The URL Health Check lets you specify a URL and then it will execute a
GET to that URL and see if the URL returns a Success Status Code or not (any 2xx Status Code like 200).
You can register the URL Health Check by adding this.
Then you inject your
IHealthCheckService and call
CheckHealthAsync as shown above. If you want to just run this single Health Check, and not others you may have registered, you’ll need to know that the name is not configurable. The name will be UrlCheck(https://github.com). So you would run that single check with
Another thing to note, that second parameter where I’m passing
TimeSpan.FromMilliseconds(1) is the
CacheDuration of the
HealthCheckResult. The default is 5 minutes. So if you have some other service (like Consul) pinging your Health Check endpoint every minute, the
HealthCheckResult will be the same for 5 minutes until the
CacheDuration expires. To me, that doesn’t make a ton of sense, and I don’t want to risk an up to 5 minute delay on being notified when my service becomes unhealthy. So by only adding a 1 millisecond cache, I’m effectively adding no caching at all.
There is also another parameter to the
AddUrlCheck method where you can pass a Func to the URL Checker. This is nice in scenarios such as:
- You want to execute something other than a
- You need to do something special with the HttpRequest in general such as add Auth Headers or something.
- You want to validate the response’s Content contains some specific words or HTML.
So the URL Check should satisfy just about any Web check you could possibly want to do with that flexibility.
Built-in SQL Server Health Checks
The SQL Check lets you specify a name and a connection string to connect to.
The first parameter, “SQL DB Check”, is just the name I chose. You can make it whatever makes sense to you. To run this check, as mentioned above, you would call this from
Making your own custom Health Check
You can of course make your own custom Health Check. For me, most of my use cases are solved by the built-in ones, as I’m usually checking if an API is available (which I could do with the URL Check and overriding the
checkFunc parameter) or I’m checking to see if a SQL Server is available. But you could implement your own if you are missing some functionality that you need such as checking if another DB store is available or how much free space a drive has.
To do that, derive from
IHealthCheck and implement the interface. Below is an example of one that checks to make sure the C drive has at least 1 GB of free space.
Then in your
ConfigureServices method, register the custom Health Check with the lifestyle that makes sense for the Health Check (Singleton, Scoped, Transient, etc.) and then add it to the
AddHealthChecks registration that we’ve done before.
Group your health checks together
You can group your health checks together in a
HealthCheckGroup if you want (such as all performance checks like CPU, Memory, Disk Space, etc. go under a group called “performance”) or you can let them live on their own and mix and match.
This enables you to do things like only call that Group of Health Checks via the
RunGroupAsync method off of
Reminder – this is demo code. Some flaws include that my Health Check endpoint is unsecured and anyone can hit the endpoint. You will likely want to secure your Health Check endpoint, especially if it is on the Internet, so someone doesn’t spam your Health Check endpoint. There are many ways to do this, but are outside the scope of this blog post.
Some Feedback on the Design
Overall I think this abstraction is really useful, and I will use it myself once it RTM’s. The built-in health checks are nice, so that you don’t have to write that logic yourself. I’m all about punting as much logic onto someone else as possible.
There are some little things I wish were a little easier though.
- It seems like the
HealthCheckResult.CheckStatus == CheckStatus.Healthycode is going to be extremely common. It’d be nice if there was a helper prop off of
HealthCheckResult.IsHealthywhich does that computation for you. Much like
IsSuccessStatusCodeproperty which is super useful. Although, I understand that “Healthy” is a relative term that’s tough to globally define. Some people might think that the
CheckStatus.Warningwould qualify as being Healthy and others wouldn’t. Ubiquitous languages are hard.
- I wish there was a way you could override the name for the built-in Health Checks. Like the URL Health Check automatically takes on the Name
UrlCheck(http://google.com). You’ll need this name if you want to pull out the specific results of a Health Check. I had expected to be able to specify the name of each Health Check and store it in something like a HealthCheckConstants class for easily retrieval. Instead, I need to follow this convention when using the constants class, which isn’t the end of the world, but being able to override the name would be nice.
- When calling the
RunGroupAsyncmethod, I wish you could just specify the group name rather than the
HealthCheckGroupinstance and let the
RunGroupAsyncmethod handle getting the HealthCheckGroup instance.
- There should be no Cache Duration on the URL Check. 5 minutes is just too long of a default, and IMO there should be no Cache Duration at all on the URL Check. I control the frequency of how often my Health Check monitoring service hits my health check endpoint. If I want it to check my service every minute, then I’d probably expect the Health Check result to be fresh every time and not be cached.
Overall, I really like this package, and it seems like it’s going to be really useful. I plan on using this when it RTM’s, so I’ll keep this post up to date when I see they make changes to this package.