Skip to main content

[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": "mywebapplication",
  "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 MyWebApplication.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


Modifying the ASPNETCORE_ENVIRONMENT
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 MyWebApplication.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 MyWebApplication
@using MyWebApplication.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.

Comments



  1. Thanks for sharing such a great information.It is really one of the finest article and more informative too. I want to share some informative data about dot net training and c# tutorial . Expecting more articles from you.

    ReplyDelete
  2. Absolutely wonderful information. I am delighted. Many thanks!

    ReplyDelete
  3. Are there any solutions out there that use Webpack or something more modern (and not abandoned) than Gulp.

    ReplyDelete

  4. Thanks for publishing this amazing article. I really big fan of this post thanks a lot. Recently i have learned about Civil ID Renew which gives a lot of information to us.

    Visit them and thanks again and also keep it up...

    ReplyDelete

  5. Firstly talking about the Blog it is providing the great information providing by you . Thanks for that .Hope More articles from you . Next i want to share some Information about Salesforce training in Hyderabad .

    ReplyDelete
  6. I was diagnosed as HEPATITIS B carrier in 2013 with fibrosis of the
    liver already present. I started on antiviral medications which
    reduced the viral load initially. After a couple of years the virus
    became resistant. I started on HEPATITIS B Herbal treatment from
    ULTIMATE LIFE CLINIC (www.ultimatelifeclinic.com) in March, 2020. Their
    treatment totally reversed the virus. I did another blood test after
    the 6 months long treatment and tested negative to the virus. Amazing
    treatment! This treatment is a breakthrough for all HBV carriers.

    ReplyDelete
  7. Hi. I am trying to use your tutorial on my ASP.Net Razor Page Project but I am stuck when I convert BaseController.
    Please give me some advice about it.

    Thanks in Advance.

    ReplyDelete
  8. I in looking for of reality appreciated perusing your weblog. It transformed into every one in all adroitly wrote and simple to undertand. rather than auxiliary sites I have legitimate to utilize that are in mean of reality now not tht top notch. I after that decided your posts really alluring. As a matter of fact in the wake of examining, I expected to move ham it occurring it to my buddy and he ejoyed it as quickly! Windows 7 Crack

    ReplyDelete
  9. Kaspersky License Key also defend your browsers from sticked substance by remove them enduringly. Kaspersky Total Security 2019 Keygen

    ReplyDelete
  10. Deep Dark Quotes concerning Life may be practiced at varied times in one’s life, like when a breakup, a business loss, A natural catastrophe. Deep Dark Quotes About Life

    ReplyDelete
  11. Check out what I've got online. The software for cracking PCs can be found here. Here's the link:
    Soul House Vocals VST

    ReplyDelete

Post a Comment

Popular posts from this blog

UI redesigns are mostly a waste of time

To preface the article, I primarily work on, and prefer, back-end code. I've been involved in both web and software development for over 4 years now and worked with many front-end and back-end frameworks. New Twitter UI Before all of the UI designers that read this go out and riot and champion against me for saying UI redesigns are a waste of time, let me say that I do value design . I think at the bare minimum, a product or website needs to be usable , and if you possess a good eye and steady hand , you should feel compelled to create something that looks pleasing. David Just stop redesigning the UI all the time . UI redesigns, in my opinion, are a waste of time 95% of the time. Let me explain further. No one cares Come see our fresh new look ! What about our new  material design , come see! I'm sorry, but besides fixing the UI where it impacts the usability of your application, no one is raving about how a redesign makes the application any better.

[Fix] - ASUS PCE-AC68 adapter (no internet)

There seem to be a lot of problems with this adapter, even with such strong performance . Why so many issues? I'm not quite sure, but I needed to find a fix because I kept on losing wifi. The ASUS PCE-AC68 The fix Keeping it short - this is how I fixed the issue: Downloaded the driver for my OS from ASUS's support page -  https://www.asus.com/us/Networking/PCEAC68/HelpDesk_Download/ (in my case it was Windows 10 64-bit). Open Device Manager by holding the Windows key and pressing R, then typing "devmgmt.msc" and hitting Enter. (Don't worry, this isn't a scam . We are simply opening Window's Device Manager through the Microsoft Management Console snap-in .) Navigate to the yellow warning sign sitting under Network adapters and right click it. Select Update driver . Select Browse my computer for driver software  and choose the following path of the OS that you have installed on your computer. (The path for the driver on my computer was C

Logging into a website with Powershell

Powershell is great, and it's lately been my go-to shell while I'm working on Windows. Sorry command prompt I really don't do a lot of work in the shell, but I do like to play with low-level interfaces from time to time. The article is about Linux shells, but goes into good explanation about what a shell is if you don't know. Log into a website Today, I wanted to do something that I have never really tried before and that is logging into a website using Powershell . The concepts behind this are quite simple really, as Powershell has support to send HTTP requests  and that's usually all we need, unless the server has CSRF protections in place (which it should). We are going to attempt  to log in to my favorite website for buying socks, Absolute Socks : Is that a turkey?! In order to do that, we need to have a login. So if you don't already have an account on www.absolutesocks.com , go make one now. Viewing the login request On websites,