[Automatically] minifying .cshtml files in .NET Core

What gets me more excited than anything? Performance.
Performance dial (credit to https://www.pehub.com)
Performance level 100

Its always exciting to me to perform at max efficiency, because why should you be performing any less? What's even better than efficiency than setting it and forgetting it? Nothing. Fact of the matter is while you may not be caring about efficiency, you should - because your business does. Lack of efficiency costs dollars and time.

You aren't off the hook even if you are doing programming as a hobby or are self-employed.

We are going to explore how to minify your .cshtml files in a .NET Core web application, and why you should care to do so.

Why you should care

Time is a limited resource, and we are tending to be more impatient nowadays, these people think so too. The bottom line is that if you want a chance for your web application to be successful, you can't skimp on quality and hope that you will be a success. Your applications need to deliver quality, and an "easy" way to convey that is to build fast applications.
I quote "easy" because it is simple in practice, but can be sometimes hard to integrate within existing development processes.

The how-to

We'll walk through how to add automatic .cshtml minification in a new .NET Core web application, although you can easily add the necessary steps in your existing application.
First, create an ASP.NET Core Web Application:
Creating a new .NET Core web application
The new project dialog
It is essential that your project is using MVC, as this is not using Razor pages! (Razor pages are used if you select the "Web Application" template).
Choosing the MVC template
MVC mode activated

Activating gulp

We will be using gulpjs (or gulp for short) to do the actual minification. The npm package is written by me, and inspired by the html minification library that Google [once] recommended to use. I never remember how to install gulp in my applications, because I install it and forget about it, but it's quite easy in Visual Studio 2017.

If you have Visual Studio 2015, you should also be okay with these instructions. But in case you are running Visual Studio 2013, you should really upgrade - if you cannot, you'll have to download this for later in the tutorial.

Create a gulpfile.js in your project root directory. (You need to name it this, don't change the name).
Adding gulpfile.js
That's what we need
Stick this code in that file. This code uses new gulp 4.x task execution and wraps a "del" and "minify" task in a parent task named cshtml. What this cshtml task does in totality is it first deletes any existing minified .cshtml files, then looks at all of the .cshtml files in your Views directory using glob syntax, and pipes (SO link) the files to the same directory they were contained in (after renaming and minifying them).
var gulp = require('gulp');
var rename = require('gulp-rename');
var minifyCshtml = require('gulp-cshtml-minify');
var del = require('del'); gulp.task('del', function (done) { // delete pre-existing .min.cshtml files del(["Views/**/*.min.cshtml"]); done();
}); gulp.task('minify', function (done) { // create new .min.cshtml files gulp.src("Views/**/*.cshtml") .pipe(rename({ suffix: ".min" })) .pipe(minifyCshtml()) .pipe(gulp.dest("Views")); done();
}); gulp.task('cshtml', gulp.series('del', 'minify'), function (done) { done();
You are likely to run into an error "ENOENT: no such file or directory" error when running the cshtml task. I'm not quite sure how to fix this yet, as it appears to be an issue with the core npm package del, as opposed to gulp itself. Running the task an extra time is a workaround, unless you try the fix at the end of this github issue.

Now, none of this code will work unless you also have npm (and nodejs) installed. While I am not a fan of installing all these separate applications (I'd rather have them all packaged, it's just easier to maintain mentally), go and install nodejs (npm comes with installing nodejs - "node" for short).

Real-world note - you will need to integrate support in your build/deployment process for installing the proper npm packages in your staging/qa/production environments. This is out of the scope of this article - although we may explore that later.

npm requires us to have a package.json file, so create one of these in the project root directory with the following template code.
Adding the package.json file
Just add the package.json file, the -lock file gets generated on its own

{ "author": "", "description": "", "dependencies": { "del": "3.0.0", "gulp": "4.0.0", "gulp-cshtml-minify": "1.0.4", "gulp-rename": "1.4.0" }, "license": "ISC", "main": "index.js", "name": "minifynetcore", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "version": "1.0.0"
Next, open up your favorite shell (I like Powershell) and navigate to your project directory. You'll want to navigate to the directory that contains the package.json file. Once you are there, you'll want to run npm install which will install all of the npm packages our gulpfile depends on.
Installing npm packages
Installing npm packages

Open the Task Runner Explorer and double click on our cshtml task to run the minification.
Opening the Task Runner Explorer window
The Task Runner Explorer

The minify task in the Task Runner Explorer
Minify that .cshtml
You will see all of the minified files in your Solution Explorer that the task generated. Feel free to browse what they look like.
Viewing minified files in the Solution Explorer
Viewing the minified files

If you see improvements or bugs that you'd like to contribute to the library, don't be afraid to submit a pull request to the library.

Fixing the Solution Explorer

The Solution Explorer looks ugly now, I don't want the minified files to show up on their own, I want them to be nested underneath the unminified file. We are able to fix this by modifying our file nesting settings. Your Solution Explorer will look a little differently since you have not yet made a new file settings option, but click on the little nesting icon and choose Add Custom Settings...
Adding custom file settings
Adding custom settings
Choose the default settings to use the Web template. You can name the settings file anything you want, I named mine MinifiedCshtml. Add the following line in your settings file:
Adding file nesting
Adding file nesting for .cshtml files
Select your solution or project in the Solution Explorer and change your file nesting setting to your custom setting and then all of your minified files will be nested under the unminified files.
Choosing our custom file settings
Choosing our custom setting
Nested minified files under unminified files
The minified files are nested properly now

Fixing the build

If we built our application now, it would fail, and that is because our minification library is doing some things like removing optional tags, so we need to fix that. The easiest way to fix these errors is to remove the minified files from being compiled. To remove any files from being compiled, we have to change the .csproj file. In Visual Studio 2017, you can directly edit the .csproj file by right-clicking the project. In Visual Studio 2015 you must first Unload Project and then you will be able to edit the .csproj file.
You can still unload the project in Visual Studio 2017 too, but it's just more convenient to edit it directly. Note - if you unload the project, you will have to reload the project from the Solution Explorer once you are done editing the .csproj file.
How to edit the .csproj file for VS 2017/2015
Editing the .csproj file
Add the following code in the .csproj file:
<!-- Prevents minified files from being compiled --> <ItemGroup> <Content Remove="**/*.min.cshtml"></Content> </ItemGroup>
Our build is now successful! However - we lost all of our minified files in the Solution Explorer. In order to see them again (without modifying the .csproj file all the time), click the Show All Files button in the Solution Explorer in case you need to view the contents of your minified file.
Showing all hidden [minified] files
Our files are back!

Loading minified views from our controllers

We are halfway there, we have our minified files but have not yet coded our application to use them. In order for our controllers to load the minified files, we are going to create a base controller and have all of our mvc controllers inherit from this base controller. Create an empty mvc controller in the Controllers folder and name this file BaseController. The contents of this controller are below.
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; namespace MinifyNETCore.Controllers
{ public abstract class BaseController : Controller { public override ViewResult View(string viewName, object model) { if (!IsDevelopment()) { string action = ControllerContext.RouteData.Values["action"].ToString(); if (string.IsNullOrEmpty(viewName)) { viewName = $"{action}.min"; } else if (viewName == action) { viewName += ".min"; } } return base.View(viewName, model); } private bool IsDevelopment() { return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == EnvironmentName.Development; } }
This controller overrides the View method that is eventually called whenever you return a View in a controller. The code simply appends a .min to the name of the view if we are in any non-development environment.

Read here if you have needs that require more control over your environments beyond the defaults; development, staging and production.

Test if minification is working

Change your HomeController to inherit from this base class now.

Note - you will want to inherit from BaseController in ALL new controllers your web project needs.

Our HomeController now inherits from the BaseController
Inheriting from the BaseController

In Index.min.cshtml, add the following <p> tag at the beginning of the view. If we see this text on our webpage, we will know we are loading the minified tag.

Testing if we are loading minified views from the HomeController
How to test if our code works

Next, right-click our project and go into Properties. Navigate to the Debug tab and change the ASPNETCORE_ENVIRONMENT to Staging

Changing to test in the Staging environment

Run the application and verify that we see "minified" on the page.

Validating our controller minification logic
Validating our code

If you change the ASPNETCORE_ENVIRONMENT variable back to Development, you will no longer see the text "minified". This would be the recommended way I would test the minification locally if necessary.

Loading minified views from tag helpers

Minifying the view our controller loads isn't all we need to do, as we likely do and should make use of tag helpers (or HTML helpers) to load partial HTML within other views. To ensure our tag helpers are loading the minified partial views, we have to create our own tag helper. Create a new folder in the project root named TagHelpers and create a new class MPartialTagHelper (minified-partial tag helper). Below is the contents of MPartialTagHelper:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Threading.Tasks; namespace MinifyNETCore.TagHelpers
{ public class MPartialTagHelper : PartialTagHelper { public MPartialTagHelper(ICompositeViewEngine viewEngine, IViewBufferScope viewBufferScope) : base(viewEngine, viewBufferScope) { } public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { // Tack on a .min exension to load the minified partial view if (!IsDevelopment()) { Name += ".min"; } return base.ProcessAsync(context, output); } private bool IsDevelopment() { return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == EnvironmentName.Development; } }
The tag helper simply adds a .min extension to the view name we pass into the tag helper. So, if we had the following line of code within a .cshtml file:

<m-partial name="_mypartialview" />

and our ASPNETCORE_ENVIRONMENT was not Development, the tag engine would essentially be executing the line of code above if it looked like this:

<m-partial name="_mypartialview.min" />

Testing the tag helper minification

In order to test this, create a new partial view in the Views/Shared directory. I named mine _TagHelperTest. (Why did I include an underscore?). I then run the cshtml task in the Task Runner Explorer again. In _TagHelperTest.min I add a simple <p> tag in it.

Adding text to the _TagHelperTest.min file
The _TagHelperTest.min file

I include this partial view with a tag helper in my Index view.

Including the new tag helper view in our Index view
Including the tag helper in the Index view

But wait! Something is wrong. Since we added a new tag helper, in order to use the tag helper, we need to add it to the Views/Shared/_ViewImports.cshtml file. Add the following @addTagHelper line to your _ViewImports file, ensuring you use the name of your project in place of MyWebApplication:
@using MinifyNETCore
@using MinifyNETCore.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyWebApplication
Now, simply change the ASPNETCORE_ENVIRONMENT variable to/from Development/Staging and notice the view including/not including the tag you added in your view (similar to what we did when testing the minification logic for the controllers).

Github source

If you'd like to view the source, please go here. Suggestions or comments, please submit them below. I hope this is useful to you.
Incorporating GDPR compliance is something that takes time, effort and thorough understanding of the law. In this post, we are going to show you how to create a stop-gap in your .NET Core web applications until you are able to invest time and resources into understanding your application's structure, usage of personal data, and restructuring it to conform to the GDPR. We will make a sample ASP.NET Core web application to demonstrate how to put a GDPR compliance stop-gap in place.

Why should you care about GDPR? Before we get into the implementation, you should first understand roughly what the GDPR is and why you need to care about it. Put simply, the GDPR are a set of regulations that give consumers rights and protections to their personal information. In order to do business with consumers in the EU, or offer any service that uses consumer data of EU residents, you must be GDPR compliant. Failure to compliant will result in fines.

Blocking EU users To become GDPR "compliant…

I felt a desire to do something I hadn't tried yet, and that was to make a multiplayer game over the network within a .NET Core console application. Because - why not? Programming allows us to create anything we can think of. Let's have a look at making our game, Packet Battle! Overview The format of this article is we will approach different aspects we need to solve in order to make Packet Battle, and at the very end I'll share the entire source you can run and play Packet Battle with your friends.

You likely won't have as much fun as these people, but games are as much as you put into them. Let's begin.

Defining "server" and "client" Before we go further, it is essential to understand the difference between a server and a client. In the terms of a game, each player is typically a client that joins a server that hosts multiple clients. Examples of these types of games are MMOs, team-based shooters, or medieval simulators. The server is the med…

Long before I even knew what SSIS was, I was doing what some might call ad-hoc programming when I was given a spreadsheet and told that they (the business, user) needed this data saved in a database. I looked at the spreadsheet and saw that there were 5,000 rows and I knew I didn't want to do this manually, so I did the next best thing. Regex. Here is how I did it.
Copied the data into a text editor These days I like to use VS Code as my text editor as it has a lot of plugins and built-in support for source control (which is honestly the big reason why I use it). So, I took my data, which looks like this: and pasted it in VS Code. The data in VS Code doesn't look very nice, but trust me that the data is indeed in a usable format for us. Select the data with regex In order to move this data into a SQL table, I need to INSERT the data in a SQL table. The easiest way to do this is to pull out each of the values in the data and insert the values into a string like so: