Fingerprint bundles in path, not in query parameter with System.Web.Optimization and Visual Studio 2015

In my previous post (https://www.roelvanlisdonk.nl/?p=4550) I explained how to:

– create a new ASP .NET MVC 5 .NET 4.6 project in Visual Studio 2015

– add static caching, add url rewrites

– add fingerprinting to the “folder part” of each URL, to bust the client cache, when a new build is deployed to the website

 

Now if you don’t want to use the URL Rewrite approach, but you still want to fingerprint the “folder url part” of the bundels created with the System.Web.Optimization (instead of using the standard query parameter cache busting technique), you can use the following class:

 

image

 

Use this class, when the bundles are created and when they are used inside the *.cshtml:

 

image

 

Then inside a *.cshtml page:

 

image

 

In this way, only the “name” of the bundle is fingerprinted and bundeling will only take place in “release” build.

The System.Web.Optimization will take care of the “cache busting”, no need for “URL rewriting”.

 

The code uses the following extension method:

 

image

Cache busting / fingerprinting bundles with System.Web.Optimization and Visual Studio 2015

I like to use client build systems like grunt, gulp, webpack etc. to bundle and minify JavaScript and CSS files, but on a resent project I had to use the Microsoft Web optimization framework.

 

Note: All code used in this blog post  can be found at: https://github.com/roelvanlisdonk/POC/tree/master/ASP.NET/CacheBusting

 

In this I will describe how to create an MVC .NET 4.6 web application, that uses bundeling, minification and fingerprinting for cache busting all by using the System.Web.Optimization framework.

 

Start Visual Studio 2015, then click:

File> New > Project…

 

Choose: Installed\Templates\Visual C#\Web\ASP.NET Web Application

 

image

Choose: “ASP.NET 4.6 Templates”: “Empty” and “Add folders and core references for”: “MVC”

 

image

 

Add a “MVC 5 Layout Page (Razor)” called “_Layout” to the “Views” folder

 

image

 

 

 

Add a “Home” controller to the “Controllers” folder:

image

 

Choose: MVC 5 Controller – Empty

 

image

 

Add a “Index.cshtml” view to the /Views/Home folder:

 

image

 

Choose the “_Layout.cshtml” as “layout page”

 

image

 

Change html:

 

image

 

Add some TypeScript files and CSS files to the /Views/Home folder:

 

image

 

image

 

image

image

To allow static file content serving from the “/Views” folder adjust the Web.config inside the “/Views” folder, remove the “BlockViewHandler”:

image

Add the System.Web.Optimization NuGet package to your project:

 

image

 

Search for “Optimization” and choose: Microsoft.AspNet.Web.Optimization

 

image

 

This will install the following dependencies to your project:

 

image

 

After installing the package we will first upgrade some of the components, just change the filer to “Upgrade available”:

 

image

 

Upgrade all packages until all package are upgraded:

image

 

Add a NonOrderingBundleOrderer.cs class to the “/Common” folder.

This class will prevent automatic ordering of the files inside the bundle.

The files will be placed in the html in the order they are added to the bundle.

 

image

 

Add a BundleConfig.cs class to the “App_Start” folder

 

image

 

Change code like:

The “#if !DEBUG”, means:

In “debug” modus all files are rendered separately to the HTML in “release” modus all JavaScript files are combined to one JavaScript file and all CSS files are combined to one CSS file.

image

 

Change the global.asax

Add “BundleConfig.RegisterBundles(BundleTable.Bundles);”

image

 

Add a class “AssemblyExtensions.cs” to the “/Extensions” folder:

image

 

Edit code like:

image

 

 

Add a class “Bundle.cs” to the “/Common” folder:

image

 

Edit code like:

image

 

Change the “_Layout.cshtml” like:

The layout page contains the actual rendering of the CSS and JavaScript bundles.

image

 

When we run the application at this point we get 4 errors in the Browser console, because all JavasScript files and CSS files are fingerprinted with a version number and do not exist on disk:

 

image

 

To solve this error, we can add Url Rewriting to our Web.config file and start caching files:

 

To start caching files, add the following xml to the Web.config:

 

image

 

 

URL Rewrite extension

To start using Url Rewriting, first install the URL from http://www.iis.net/downloads/microsoft/url-rewrite or from the web platform installer inside IIS.

Note that URL Rewriting will work out of the box with IIS Express in Visual Studio 2015.

 

image

 

Then add the following to the Web.config:

 

image

 

All url’s that start with vx.x.x.x/ will be rewritten (the version number will be removed from the URL before passing it to the reset of ASP .NET. Only the part after the first “/” will be presented to the “match” rule.

So only the part “v1.0.0.0/View/Home/Test1.js” of the url http://localhost:4567/v1.0.0.0/View/Home/Test1.js will be passed to the match rule, that’s why the regex can start with a “^”.

 

Now when you run the application in debug modus you will see:

 

image

 

And when you run in “release mode” you will see:

image

 

As you can see the System.Web.Optimization framework automatically adds a “cache busting query parameter”, but in some browsers this is not enough, that’s why we added a version number inside the “folder” part of the url.

 

 

The final project structure should look like:

 

image

 

For more resources see: http://madskristensen.net/post/cache-busting-in-aspnet

 

 

NOTE

If you use “URLs” inside a CSS file, that is added to a bundle, e.g.

background-image: url(https://www.example.com/bck.png);

Then these URLs should also be fingerprinted.

To fingerprint these URLs you can write a custom BundleTransform:

http://blog.mirajavora.com/deep-dive-into-asp.net-bundling-and-minification/

 

Or use the https://bundletransformer.codeplex.com/.

This extension to the System.Web.Optimization framework, supports automatic transformation of relative paths to absolute paths in CSS-code (by using UrlRewritingCssPostProcessor).

Fix: Visual Studio 2013 / 2014–SQL Server 2014 – The term ‘Invoke-Sqlcmd’ is not recognized as the name of a cmdlet

 

Note

When you want to run / edit / debug PowerShell scripts inside Visual Studio 2013 / 2015 you will have to install the “PowerShell Tools for Visual Studio” Visual Studio extension found at: https://visualstudiogallery.msdn.microsoft.com/c9eb3ba8-0c59-4944-9a62-6eee37294597

 

I wanted to run and debug a PowerShell script inside Visual Studio 2013 / 2015, on a machine that only had SQL Server 2014 installed as database server. This threw an exception: The term ‘Invoke-Sqlcmd’ is not recognized as the name of a cmdlet http://blog.sqlauthority.com/2014/11/23/sql-server-fix-the-term-invoke-sqlcmd-is-not-recognized-as-the-name-of-a-cmdlet/

 

Turns out you have to update the PowerShell “PSModulePath”, so it can find the PowerShell modules containing the SQL Server 2014 cmdlets.

 

Temp fix:

http://stackoverflow.com/questions/29562742/invoke-sqlcmd-failing-after-automated-sql-install-with-powershell

Existing Powershell session isn’t aware about Sql’s modules that were just installed. Take a look at environment variable $env:PSModulePath. Compare new shell’s variable to existing and you should see a missing path like …\Microsoft SQL Server\110\Tools\PowerShell\Modules\.
For a work-around, modify the path to include module dir. Like so,

$env:PSModulePath += ";C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules"

 

If you want to persist this extra “Module load path”, you have to change the registry:

http://tomtalks.uk/2013/06/powershell-add-a-persistent-module-path-to-envpsmodulepath/

 

image

 

Now I can run and debug the following PowerShell script inside Visual Studio 2013 / 2015:

# This sccript requires the system to allow running powershell scripts, when not allowed the following code can be used to allow running powershell scripts.
# Set-ExecutionPolicy RemoteSigned
# The following import statement enables the SQL Server PowerShell cmdlets, like "Invoke-Sqlcmd".
Import-Module SqlPs
$serverInstance = "(localdb)\v11.0"
$database = "App"
function ExecuteSqlFile {
[string]$file = $args[0]
Invoke-Sqlcmd -ServerInstance $serverInstance -Database $database -InputFile $file
}
ExecuteSqlFile('C:\Temp\test.sql')

Move C:\ProgramData\Package Cache\ to D:\

Windows with a couple of development tools takes up about 126GB of my 128GB SSD, to free-up some space I followed the guide at: http://www.hanselman.com/blog/GuideToFreeingUpDiskSpaceUnderWindows81.aspx

 

Then I moved the "C:\ProgramData\Package Cache" used by Visual Studio to D:\.

 

I did this by following the instructions at: http://superuser.com/questions/455853/can-i-delete-the-the-folder-c-programdata-package-cache

 

1. Move folder from C:\ProgramData to D:\ProgramData

This must be a move of the complete “Package Cache” folder, so after move you will have a folder C:\ProgramData, that does NOT contain the folder “Package Cache”.

2. Create a filesystem junction: mklink /J "C:\ProgramData\Package Cache" "D:\ProgramData\Package Cache"

This will create a filesystem junction, that will point all software that wants to access  "C:\ProgramData\Package Cache" to "D:\ProgramData\Packa Cache".

 

That alone saved me about 6GB.

How to Fix: No “organize usings” and F12 “Go to definition” disabled or grayed out in Visual Studio 2013

When I right clicked on my using statements in a C# file, the context edit menu, didn’t contain the options for refactoring and “organize usings” and the menu item “F12 Go to definition” was grayed out.

I found the fix at: http://stackoverflow.com/questions/25898616/visual-studio-go-to-definition-disabled-or-gray-out

 

  • Clean your solution
  • Close solution
  • Remove any [solution].ncb, [solution].suo, [solution].vspscc files
  • Reopen solution
  • Rebuild solution

Menu options should appear and be enabled.

Awesome: Visual Studio will minify a css file automatically, when original file is altered.

I Just discovered that Visual Studio will automatically re-minify a css file, when the original not minified version is altered.

So I have two file: in a folder including the map files:

 

test.css

test.css.map

test.min.css

 

When I change the test.css the test.min.css is automatically overwritten by a new minified version.

Awesome!

Microsoft Visual Studio 2013 Update 4 RC

Microsoft Visual Studio 2013 Update 4 RC can be downloaded at: http://www.microsoft.com/en-us/download/confirmation.aspx?id=44545

 

It contains many new features for an update: http://blogs.msdn.com/b/webdev/archive/2014/10/16/announcing-new-web-features-in-visual-studio-2013-update-4-rc.aspx

 

The thing I like the most:

 

Support for custom elements, polymer-elements and attributes

We no longer validate unknown attributers for custom elements as there will be many custom made tags in different frameworks. So there will no longer be squiggles under the unknown elements.

clip_image005[5]

Live reload browser, when you save a JavaScript, CSS or HTML file in Visual Studio 2013, by using BrowserSync or BrowserLink

 

If you want to speed up your SPA development process, it would be nice if you could reload all browser currently showing your SPA, when you press CTRL-S in Visual Studio 2013. The build-in Browserlink provides this feature for ASP .NET WebForms en ASP .NET MVC, but you could also use BrowserSync, which will work with any editor.

 

BrowserSync

 

1. Download node.js http://nodejs.org/download/

2. Install node.js (next – next – finish)

3. Open a command prompt (with administrator privileges) and enter:

4. npm install -g browser-sync

5. Change directory to the location of your Visual Studio solution file:

    – CD “C:\Projects\MyProject”

5. Start your web application in Visual Studio by pressing F5

    – Look at the browser address bar to get the URL to connect BrowserSync to, in my case localhost:50367

6. To start watching all files in all subfolders, at the command prompt enter:

    browser-sync start –proxy "localhost:50367" –files "**.*"

 

This will start a new instance of the default browser on an other port, showing your site connected to BrowserSync

In my case the new url was: http://localhost:3000/

Now when you edit a file in Visual Studio en press CTRL-S the browser at http://localhost:3000 will be refreshed.

 

BrowserLink

BrowserLink is provided in the box with Microsoft Visual Studio 2013, but OOB it will not work with SPA applications created with only html, JavaScript and CSS files.

To get BrowserLink working with those kind of web applications add the following to the web.config:

<system.webServer>
<handlers>
      <add name="Browser Link for HTML" path="*.html" verb="*"
type="System.Web.StaticFileHandler, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
resourceType="File" preCondition="integratedMode" />
</handlers>
</system.webServer>

The good thing about BrowserLink that it is in the box and you don’t have to specify all subfolders you want to watch.

The downside is that you still have to use CTRL-S and CTRL-ALT-ENTER. This can be an upside if you want to edit multiple files en then refresh the connected browsers.

For now I will stick with BrowserLink, because it’s in the box.

 

Note: when using the “empty” asp .net template, I could not get BrowserLink to work, but when I used the web api template all was just fine.

Changing C# on the fly (without stopping the debugger) in an ASP .NET vNext web application with Visual Studio 14 CTP3

The roslyn compiler found in Visual Studio 14 enables us, to adjust C# code during runtime, by compiling the new C# code in-memory. Now we can edit the UI part (HTML, CSS, JavaScript) of a single page app, including the backend C# ASP .NET Web Api service dynamically in runtime.

 

On of the things, that disrupts my workflow, when creating a single page app with AngularJS, backed by a web api C# service in Visual Studio 2013, is the fact that, when I find a bug in my C# code while testing the UI, I have to stop the debugger, adjust the web api C# code, re-compile, re-run and then recreating state in the web app before I can resume testing the UI.

This pain will go away, when using Visual Studio 14

 

Download and install Visual Studio 14 CTP 3 from http://msdn.microsoft.com/en-US/subscriptions/downloads/

Open Visual Studio, choose File > New > Project …

 

image

 

image

 

Start the web application by pressing CTRL + F5 (so don’t start the debugger).

 

image

 

Click on the Contact page in the browser:

You will see the text “Your contact page”.

image

 

Now go to the Controllers\HomeController.cs

image

 

Adjust the the C# code: ViewBag.Message = "Your contact page.";

to

ViewBag.Message = "My contact page.";

 

image

 

Save the file and in the browser press F5, you might see a short delay, because the roslyn compiler is now dynamically compiling the code.

 

The C# code is changed without disrupting my workflow.

 

image