Web Development
Force clients to refresh JS/CSS files

Force clients to refresh JS/CSS files

It's a common problem with an easy solution. You make some changes to a JavaScript of CSS file, but your users still report an issue due to the old version being cached.

You could wait for the browsers cache to expire, but that isn't a great solution. Worse if they have the old version of one file and the new version of another, there could be compatibility issues.

The solution is simple, just add a querystring value so that it looks like a different path and the browser downloads the new version.

Manually updating that path is a bit annoying though so we use modified time from the actual file to add the number of ticks to the querystring.

UrlHelperExtensions.cs

1using Utilities;
2using UrlHelper = System.Web.Mvc.UrlHelper;
3
4namespace Web.Mvc.Utils
5{
6 public static class UrlHelperExtensions
7 {
8 public static string FingerprintedContent(this UrlHelper helper, string contentPath)
9 {
10 return FileUtils.Fingerprint(helper.Content(contentPath));
11 }
12 }
13}

FileUtils.cs

1using System;
2using System.IO;
3using System.Web;
4using System.Web.Caching;
5using System.Web.Hosting;
6
7namespace Utilities
8{
9 public class FileUtils
10 {
11 public static string Fingerprint(string contentPath)
12 {
13 if (HttpRuntime.Cache[contentPath] == null)
14 {
15 string filePath = HostingEnvironment.MapPath(contentPath);
16
17 DateTime date = File.GetLastWriteTime(filePath);
18
19 string result = (contentPath += "?v=" + date.Ticks).TrimEnd('0');
20 HttpRuntime.Cache.Insert(contentPath, result, new CacheDependency(filePath));
21 }
22
23 return HttpRuntime.Cache[contentPath] as string;
24 }
25 }
26}
Using compile options for version compatibility

Using compile options for version compatibility

Here's the scenario; Your building a module and it needs to be compatible with different versions of a platform. e.g. Sitecore, and everything's great up until the day you need to call different methods in different versions of the platform. You'd rather not drop support for the old versions, and nor do you want to start maintaining two code bases. So what do you do?

C# Preprocessor Directives

Preprocessor directives provide a way to give the compiler instructions to follow while its compiling a project. By using this we can give the compiler conditions to compile different versions in different ways. Thereby allowing us to maintain one codebase, but produce compilations for different versions of the platform. e.g. One for Sitecore 8.0 and another for Sitecore 9.0.

#if, #else and #endif

When the compiler encounters an #if followed by an #endif, it will only compile the code between the two if the specified symbol had been defined.

1#if DEBUG
2 Console.WriteLine("Debug version");
3#else
4 Console.WriteLine("Non Debug version");
5#endif
6

Defining a preprocessor symbol

For the if statement to work, your going to need to define your symbol which is being evaluate.

This can be included in code as follows

1#define YOURSYMBOL

A more useful was of defining this however is to include it in your call to MSBuild (this is particularly useful when using a build server).

1-define:name[;name2]

If your compiling from Visual Studio an easier solution is to set up a new build configuration with a conditional compilation symbol.

  • Right click your solution item in Solution Explorer and select Properties
  • Click Configuration Properties on the left and then Configuration Manager on the right
  • In the pop up window click the Active solution configuration drop down and then click New
  • Enter the name of the build config. In my example above I have SC82 for Sitecore 8.2 and SC90 for Sitecore 9.0.
  • Click Ok and close all the windows you just opened
  • Right click the project that your going to build and select Properties
  • Select the Build tab
  • Select your build configuration from the configuration at the top
  • Enter the symbol your using for the #if directives

Reference different versions of an assembly

Adding conditions to our code is good, but for this to fully work we also need to reference different versions of the assemblies that are causing the issue in the first place.

There's no way of doing this through Visual Studio but by editing the .csproj file manually we can update the hint path on a reference to include the configuration name as a variable.

1..\libraries\$(Configuration)\Sitecore.Kernel.dll False

This example shows how different versions of the Sitecore Kernel can be referenced by keeping each version in a subfolder that corresponds with the build configuration name.

As well as different versions of assemblies, it may also be needed to target different versions of the .NET framework. This can be done in the .csproj file by including additional property groups that have a condition on the configuration name.

1bin\SC82\
2TRACE;SC82
3true
4v4.5.2
5
6bin\SC90\
7TRACE;SC90
8true
9v4.6.2

In this example I'm targeting .net 4.5.2 for my Sitecore 8.2 configuration and 4.6.2 for my Sitecore 9 configuration.

Useful Links

C# preprocessor directives
-define (C# Compiler Options)

How to create a new instance of a dependency inject object

How to create a new instance of a dependency inject object

I decided to write this post after seeing it done wrong on a number of occasions and also not finding many great examples of how it should be done. Most articles of DI or IOC tend to focus on objects such as a logger being injected and then calling functions on that object.

Take for instance, this example in the Simple Injector documentation:

1using System;
2using System.Web.Mvc;
3
4public class UserController : Controller {
5 private readonly IUserRepository repository;
6 private readonly ILogger logger;
7
8 public UserController(IUserRepository repository, ILogger logger) {
9 this.repository = repository;
10 this.logger = logger;
11 }
12
13 [HttpGet]
14 public ActionResult Index(Guid id) {
15 this.logger.Log("Index called.");
16 IUser user = this.repository.GetById(id);
17 return this.View(user);
18 }
19}

Here we can see the implementation of IUserRepository and ILogger get passed in the constructor and are then used in the Index method.

However what would the example be if we wanted to create a new user and call the user repository to save it? Creating a new instance of IUser from our controller becomes an issue as we don't know what the implementation of IUser is.

The incorrect implementation I usually see is someone adding a reference to the implementation and just creating a new instance of it. A bit like this:

1public class UserController : Controller {
2 [HttpPost]
3 public ActionResult SaveUser(UserDetails newUserInfo) {
4
5 IUser user = new User;
6 user.FirstName = newUserInfo.FirstName;
7 user.LastName = newUserInfo.LastName;
8 this.repository.SaveUser(user);
9 return this.View(user);
10 }
11}

This will work, but fundamentally defeats the point of using dependency injection as we now have a dependency on the user objects specific implementation.

The solution I use is to add a factory method to our User Repository which takes care of creating a User object.

1public class UserRepository : IUserRepository {
2 public User CreateUser() {
3 return new User();
4 }
5}

Responsibility of creating the object now remains with its implementation and our controller no longer needs to have a reference to anything other than the interface.

1public class UserController : Controller {
2 [HttpPost]
3 public ActionResult SaveUser(UserDetails newUserInfo) {
4
5 IUser user = this.repository.CreateUser();
6 user.FirstName = newUserInfo.FirstName;
7 user.LastName = newUserInfo.LastName;
8 this.repository.SaveUser(user);
9 return this.View(user);
10 }
11}