List all Transitive Dependencies in your .Net Core Project

Photo by cottonbro studio on Pexels.com

Sometimes you need to find out what packages a .Net Core project or solution depends on quickly and whilst you can find this information in the Visual Studio IDE or by opening the project files individually and reading them, there is a quick and easy way using the “dotnet list package” command. What’s more you can also list the packages that your dependencies also have dependencies on, which is really useful for tracking down what part of your solution has a requirement on a specific package.

dotnet list package

This outputs all NuGet package references for a specific project or a solution (which you can specify in the command parameters or just let dotnet find the nearest solution or project file in the current directory structure). Example below:

Project 'WebApplication1' has the following package references
   [net6.0]: 
   Top-level Package                                Requested   Resolved
   > Azure.Storage.Blobs                            12.14.1     12.14.1 
   > Microsoft.AspNet.Identity.EntityFramework      2.2.3       2.2.3  

To see which packages your project relies on and also which packages they in turn rely on then run the command with the –include-transitive flag.

dotnet list package --include-transitive

This outputs the full list of packages and their dependencies, like so:

Project 'WebApplication1' has the following package references
   [net6.0]: 
   Top-level Package                                Requested   Resolved
   > Azure.Storage.Blobs                            12.14.1     12.14.1 
   > Microsoft.AspNet.Identity.EntityFramework      2.2.3       2.2.3   

   Transitive Package                         Resolved
   > Azure.Core                               1.25.0  
   > Azure.Storage.Common                     12.13.0 
   > EntityFramework                          6.1.0   
   > Microsoft.AspNet.Identity.Core           2.2.3   
   > Microsoft.Bcl.AsyncInterfaces            1.1.1   
   > System.Diagnostics.DiagnosticSource      4.6.0   
   > System.IO.Hashing                        6.0.0   
   > System.Memory.Data                       1.0.2   
   > System.Numerics.Vectors                  4.5.0   
   > System.Text.Encodings.Web                4.7.2   
   > System.Text.Json                         4.7.2   
   > System.Threading.Tasks.Extensions        4.5.4   

I found this useful this week when a large solution’s build pipeline was erroring due to a missing dependency but it wasn’t clear which project needed it.

For more info on the dotnet package command see the Microsoft docs here.

Advertisement

Referencing External Controllers in ASP.Net Core 3.x

I recently had a situation where I needed to include a utility controller and set of operations into every .Net Core Web API that used a common in-house framework. The idea being that by baking this utility controller in to the framework then every API built that references it can take advantage of this common set of API operations for free. The types of operations that this might include are logging, registration, security or health check type API operations that every Micro Service might need to implement out of the box (some perhaps only in certain environments). Anyway I knew this was possible in .Net Core through the use of ApplicationPart and ApplicationPartManager so I thought I’d code it up and write a blog post to remind ‘future me’ of the approach, however it soon became clear that since .Net Core 3.x this is now automatic so this is the easiest blog post ever. That said there are times when you’ll want to NOT automatically include external controllers and so we’ll cover that too.

Photo by Luca Bravo on Unsplash

External Controllers Added Automatically in .Net 3.x

If you’re using .Net Core 3.x or upwards then ASP.net Core will traverse any referenced assemblies in the assembly hierarchy looking for those that reference the ASP.Net Core assembly and then look for controllers within those assemblies. Any assemblies found are automatically added to the controllers collection and you’ll be able to hit them from your client.

To test this, create a File > New ASP.Net Core Web Project in Visual Studio, then add class libraries to the solution and in those class libraries add Controller classes with the functionality you need. Add the [ApiController] attribute then follow the auto prompt to add the AspNetCore NuGet package. Code your new controller to return something and then add a reference to that new class library project from the original Web API project and you’re done. Run the Web API and try to hit the operation in your new external controller from the browser/postman. It should find the operation in the controller that lives inside the class library project.

This ability to include controllers from external assemblies into the main Web API project has always been very useful but now its just go easier which is great.

Removing automatically added controllers

I can see at least two issues with this automatic approach. Firstly it is a potential security risk as you may inadvertently include controllers in your project via package dependencies and these could be nefarious. This is discussed in more detail in this post along with a way clearing out all ApplicationParts from the collection and then adding the default one back in (reproduced below)

 
 public void ConfigureServices(IServiceCollection services)
{
  services.AddControllers().ConfigureApplicationPartManager(o =>
    {
      o.ApplicationParts.Clear();
      o.ApplicationParts.Add(new AssemblyPart(typeof(Startup).Assembly));
    });
} 

Secondly you may just want to control via configuration which ApplicationParts / Controllers are to be used by the API. In my use case I wanted to be able to allow API developers to be able to disable the built-in utility controllers if required. I can use configuration to drive this and remove the auto discovered controller by Assembly name (as below).

 
 public void ConfigureServices(IServiceCollection services)
{
  services.AddControllers().ConfigureApplicationPartManager(o =>
    {
      // custom local config check here
      if (!configuration.UseIntrinsicControllers)
      {
        // assuming the intrinsic controller is in an assembly named IntrinsicControllerLib
        ApplicationPart appPart = o.ApplicationParts.SingleOrDefault(s => s.Name == "IntrinsicControllerLib");
        if (appPart != null)
        {
         // remove it to stop it being used
         o.ApplicationParts.Remove(appPart);
        }
      }
} 

So as you can see its now easier than ever to add utility controllers into your API from externally referenced projects/assemblies but “with great power comes great responsibility” and so we need to ensure that only approved controllers (or other ApplicationParts such as views) are added at runtime.

Hope you found this useful.

References for further reading:

Build warnings & .Net Standard dependencies

Having recently migrated a Windows Azure hosted .Net Framework ASP.net MVC 5 web application over to ASP.net Core (v2.2) I hit a few issues so I’m documenting them here for my future self and anyone else who might find it useful. The application was a multi functional ASP.net MVC site with an Azure Blob Storage Service back-end that I re-wrote in new ASP.Net Core with a mixture of both MVC and Razor Pages. The new site was .Net core 2.2 which referenced supporting projects built to .Net Standard 2.0.

The Problem

The application ran fine locally and as I was re-purposing the same Windows Azure Web Service instance I re-configured it to support .Net Core via the Azure Portal. Its not clear if this step is actually required but it makes sense to set it correctly (see screnshot below).

Next I deployed my Web App via Web Deploy, and then hit the homepage but immediately received a long list of errors such as the one below:

The type 'Attribute' is defined in an assembly that is not referenced. 
You must add a reference to assembly
'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'. 
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCore.Views_Home_Index), @"mvc.1.0.view", @"/Views/Home/Index.cshtml")] ……etc……

It looked like .Net itself was not being found on the target machine, and so after checking the server settings and server logs in the Azure Portal I double checked my project references to confirm I was targeting .Net Core and .Net Standard 2.0 and that’s when I found a build Warning that I had previously missed:

[NU1701] Package 'Microsoft.Azure.KeyVault.Core 1.0.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETStandard,Version=v2.0'. 
This package may not be fully compatible with your project

One of my dependent projects in the solution was referencing the Azure SDK assembly Microsoft.Azure.Storage.Blob (via NuGet package) which in turn was referencing a version of Microsoft.Azure.KeyVault.Core that was not compatible with .NetStandard 2.0. This solution ran fine locally (as the dependent framework is installed on my machine) but when the application was deployed to a server without the .Net Framework Version=v4.6.1 binaries the application failed to load. More on this problem can be found in this GitHub issue.

The Solution

The solution was to reference a later version of the Microsoft.Azure.KeyVault.Core assembly that does support .NetStandard. As my project had no direct reference to Microsoft.Azure.KeyVault.Core (as it was a dependency of Microsoft.Azure.Storage.Blob) I had no dependency reference to update and so instead added a direct dependency to Microsoft.Azure.KeyVault.Core version 3.0.3 in my project file. This didn’t directly resolve my issue though despite reportedly working for other people, but at this point I decided to update all my Azure SDK references which took Microsoft.Azure.Storage.Blob to v11.1.3 which references a .NetStandard 2.0 compliant version of the KeyVault.Core dependency:

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.3" />
  </ItemGroup>

After upgrading (tweaking a few updated namespaces that has changed in the newer package versions) I deployed the project successfully without error. Issue resolved!

The lesson here I think is to firstly don’t ignore build warnings (we all sometimes – right?) and secondly be careful with dependencies and their children not being .NetStandard compliant. This may be less of a problem in the future as .Net Core and .Net Standard gets more widespread adoption but for now its one to watch for.

Razor Pages Fixes to Tag Helpers Issues

Recently when adding Razor Pages to an existing ASP.net Core MVC web application I had issues with the Tag Helpers not working. No markup was being produced. Not only were the tag helpers (i.e. asp-for) not doing their job of but I also noticed that the markup was not being formatted in bold in Visual Studio as it should be.

At this point I checked for a _ViewImports.cshtml file which I found before checking some other things (see the list below), however I failed to notice that as this was an MVC application with Razor Views there is already a _ViewImports.cshtml file but in the wrong place for my Razor Pages. The _ViewImports.cshtml file must be in the root of the Pages folder where your Razor pages reside, and so you will need one in both the Pages folder for your Razor Pages and also in the Views folder for your MVC Razor Views.

The _ViewImports.cshtml file must contain:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Adding a new _ViewImports.cshtml file under the Pages folder resolved my problem, but in the meantime here are some additional things to try if you have other Tag Helper problems:

  • Update all dependency Packages
  • Add/re-add Microsoft.AspNetCore.MVC.TagHelpers package if its missing
  • Check your _ViewImports.cshtml file is in the Pages folder or a parent folder of your Razor Pages. You may need one in the Areas folder if you have one.
  • Check that the _ViewImports.cshtml file includes a reference to Microsoft.AspNetCore.Mvc.TagHelpers. If it does try removing it, rebuilding the solution and then re-adding it, and rebuilding again.
  • Check the namespace in the _ViewImports.cshtml file is correct.
  • Should all these fail, try turning on the developer exception page and see if that helps to narrow down the problem.

Visual Studio 2019 Offline Installer

Microsoft have now released Visual Studio 2019 and like VS2017 there is no offline installer provided by Microsoft, but you can generate one by using the web installer setup program to write all the packages to disk.

To create the offline installer just download the usual web installer exe from the Microsoft download site and then call it from the command line passing in the layout flag and a folder path like this:

vs_community --layout  "C:\Setup\VS2019Offline"

In the example above I’m dowloading the Community verssion, but if its the Enterrpise editions installer then the setup file you downloaded will be called vs_enterprise.

The packages will all be downloaded and a local setup exe installer created.

If you want to limit to English then pass –lang en-US flag too.

vs_community --layout  "C:\Setup\VS2019Offline" --lang en-US

You can also limit what workloads to download if you know their names by listing them after a –add flag.

Enjoy your offline installs.

Create New MSTest Projects for Pre .Net 4.5 in Visual Studio 2017

This post outlines the steps to create a new unit test project in Visual Studio 2017 using MS Test V1 and that targets .Net Frameworks prior to .Net 4.5.

Visual Studio 2017 onwards only has new unit test projects for MS Test V2 onwards and .Net 4.5. This is fine for new applications or ones targeting a recent .Net framework version but what if you have an existing solution targeting an older .Net version. Below shows the Unit Test Project available for .Net 4.5, but as you can see in the second screenshot for .Net 3.5 its not available.

NewTestProj_45

NewTestProj_35

If you want to create a new unit test project targeting .Net 3.5/4 for example then follow the steps below:

Create a new MS Test V2 project targetting .Net Framework 4.5 as in the first screenshot above (i.e. File > New Project > Test > Unit Test Project targeting .Net 4.5).

Once its created, change the project to target your earlier .Net Framework (e.g. .Net 3.5). This is done via the Project Properties page.  Click Yes and Visual Studio will reload the project.

NewTestProj_ChgFmk

Once it reloads the project will complain about some references which is fine as we’re now going to remove the MS Test V2 assemblies.

NewTestProj_Errors

Now remove the two project references to test assemblies.

NewTestProj_RemoveRefs

Then add a reference to the MSTest v1 assembly, Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll.  This should be under Extensions in the Add Reference dialog. Alternatively you can browse to them on your hard drive at the following locations:

For pre .Net 4 projects : C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0

For post .Net 4 projects: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0

If you are not running Visual Studio Enterprise, then swap Enterprise in the path for Community etc.

NewTestProj_AddRefs

Now rebuild the project and you’re all done.

Find assemblies loaded during debugging in Visual Studio

Sometimes you may get the following error when you are debugging a .Net app in Visual Studio:

“The breakpoint will not currently be hit. No symbols have been loaded for this document.”

Or you may have issues whereby the wrong code version appears to be loading at run time or perhaps when debugging you get an error saying a referenced component cannot be located.

All these issues stem from you not being able to view what components are actually being loaded during debug. If only there was a view in Visual Studio that gave you that info…well this is Visual Studio and so they’ve already thought of that, and its called the Modules view.

During debugging of your application from the menu go : Debug > Windows > Modules

Mod1

From this really useful view you can see each component that’s been loaded, the file path, the symbol file location, version information and more. This will show you if a component from the GAC has been loaded instead of your local file version, for example. It also enables you to find and load Symbol files for components where they have not been loaded automatically.

For information on the full functionality of this view check out the documentation here.

Break on Exceptions in Visual Studio 2015

Looking for the option to break on exceptions during debugging in Microsoft Visual Studio 2015? Well Microsoft dumped the old exceptions dialog and replaced it with the new Exception Settings Window. To see it to show that window via the menu: Debug > Windows > Exception Settings.

vsexceptionsettingsmenu

Use the Exception Settings window to choose the types of exceptions on which you wish to break. Right click for the context menu option to turn on/off the option to break or continue when the exception is handled (see below). To break on all exceptions you’ll want to ensure this is set to off (not ticked).

vs2015exceptionssettingsdiag2

For more information check out these MSDN links:

https://blogs.msdn.microsoft.com/visualstudioalm/2015/02/23/the-new-exception-settings-window-in-visual-studio-2015/

https://blogs.msdn.microsoft.com/visualstudioalm/2015/01/07/understanding-exceptions-while-debugging-with-visual-studio/

Preventing Browser Caching using HTTP Headers

Many developers consider the use of HTTPS on a site enough security for a user’s data, however one area often overlooked is the caching of your sites pages by the users browser. By default (for performance) browsers will cache pages visited regardless of whether they are served via HTTP or HTTPS. This behaviour is not ideal for security as it allows an attacker to use the locally stored browser history and browser cache to read possibly sensitive data entered by a user during their web session. The attacker would need access to the users physical machine (either locally in the case of a shared device or remotely via remote access or malware). To avoid this scenario for your site you should consider informing the browser not to cache sensitive pages via the header values in your HTTP response. Unfortunately it’s not quite that easy as different browsers implement different policies and treat the various cache control values in HTTP headers differently.

Taking control of caching via the use of HTTP headers

To control how the browser (and any intermediate server) caches the pages within our web application we need to change the HTTP headers to explicitly prevent caching. The minimum recommended HTTP headers to de-activate caching are:

Cache-control: no-store
Pragma: no-cache

Below are the settings seen on many secure sites as a comparison to above and perhaps as a guide to what we should really be aiming for:

Cache-Control:max-age=0, no-cache, no-store, must-revalidate
Expires:Thu, 01 Jan 1970 00:00:00 GMT
Pragma:no-cache

HTTP Headers & Browser Implementation Differences:

Different web browsers implement caching in differing ways and therefore also implement various subtleties in their support for the cache controlling HTTP headers. This also means that as browsers evolve so too will their implementations related to these header values.

Pragma Header Setting

Use of the ‘Pragma’ setting is often used but it is now outdated (a retained setting from HTTP 1.0) and actually relates to requests and not responses. As developers have been ‘over using’ this on responses many browsers actually started to make use of this setting to control response caching. This is why it is best included even though it has been superseded by specific HTTP 1.1 directives.

Cache-Control ‘No-Store’ & ‘No-Cache’ Header Settings

A “Cache-Control” setting of private instructs any proxies not to cache the page but it does still permit the browser to cache. Changing this to no-store instructs the browser to not cache the page and not store it in a local cache. This is the most secure setting. Again due to variances in implementation a setting of no-cache is also sometimes used to mean no-store (despite this setting actually meaning cache but always re-validate, see here). Due to this the common recommendation is to include both settings, i.e: Cache-control: no-store, no-cache.

Expires Header Setting

This again is an old HTTP 1.0 setting that is maintained for backward compatibility. Setting this date to a date in the past forces the browser to treat the data as stale and therefore it will not be loaded from cache but re-queried from the originating server. The data is still cached locally on disk though and so only provides little security benefits but does prevent an attacker directly using the browser back button to read the data without resorting to accessing the cache on the file system.  For example:  Expires: Thu, 01 Jan 1970 00:00:00 GMT

Max-Age Header Setting

The HTTP 1.1 equivalent of expires header. Setting to 0 will force the browser to re-validate with the originating server before displaying the page from cache. For example: Cache-control: max-age=0

Must-Revalidate Header Setting

This instructs the browser that it must revalidate the page against the originating server before loading from the cache, i.e. Cache-Control: must-revalidate

Implementing the HTTP Header Options

Which pages will be affected?

Technically you only need to turn off caching on those pages where sensitive data is being collected or displayed. This needs to be balanced against the risk of accidently not implementing the change on new pages in the future or making it possible to remove this change accidently on individual pages. A review of your web application might show that the majority of pages display sensitive data and therefore a global setting would be beneficial. A global setting would also ensure that any new future pages added to the application would automatically be covered by this change, reducing the impact of developers forgetting to set the values.

There is a trade off with performance here and this must be considered in your approach. As this change impacts the client caching mechanics of the site there will be performance implications of this change. Pages will no longer be cached on the client, impacting client response times and may also increase load on the servers. A full performance test is required following any change in this area.

Implementing in ASP.net

There are numerous options for implementing the HTTP headers into a web application. These options are outlined below with their strengths/weaknesses. ASP.net and the .Net framework provide methods to set caching controls on the Request and Cache objects. These in turn result in HTTP headers being set for the page/application’s HTTP responses. This provides a level of abstraction from the HTTP headers but that abstraction prevents you setting the headers exactly how you might like them for full browser compatibility. The alternative approach is to explicitly set the HTTP headers. Both options and how they can be implemented are explored below:

Using ASP.net Intrinsic Cache Settings
Declaratively Set Output Cache per ASPX Page

Using the ASPX Page object’s attributes you can declaratively set the output cache properties for the page including the HTTP header values regarding caching. The syntax is show in the example below:

Example ASPX page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CacheTestApp._Default" %> 
<%@ OutputCache Duration="60" VaryByParam="None"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
<title></title> 
</head> 
<body> 
<form id="form1" runat="server"> This is Page 1.</form> 
</body> 
</html>

Parameters can be added to the OutputCache settings via the various supported attributes. Whilst this configuration allows specific targeting of the caching solution by enabling you to define a cache setting for each separate page it has the drawback that it needs changes to be made to all pages and all user controls. In addition developers of any new pages will need to ensure that the page’s cache settings are correctly configured. Lastly this solution is not configurable should the setting need to be changed per environment or disabled for performance reasons.

Declaratively Set Output Cache Using a Global Output Cache Profile

An alternative declarative solution for configuring a page’s cache settings is to use a Cache Profile. This works by again adding an OutputCache directive to each page (and user control) but this time deferring the configuration settings to a CacheProfile in the web.config file.

Example ASPX page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CacheTestApp._Default" %> 
<%@ OutputCache CacheProfile=" RHCacheProfile "%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
<title></title> 
</head> 
<body> 
<form id="form1" runat="server"> 
This is Page 1. 
</form> 
</body> 
</html>

Web.config file:

<system.web> 
<caching> 
<outputCache enableOutputCache="false"/> 
<outputCacheSettings> 
<outputCacheProfiles> 
<OutputCache CacheProfile=" RHCacheProfile"> 
<add name="RHCacheProfile" 
location="None" 
noStore="true"/> 
</outputCacheProfiles> 
</outputCacheSettings> 
</caching> 
</system.web>

This option provides the specific targeting per page and the related drawbacks of having to make changes to every page and user control. This solution does provide the ability to centralise the cache settings in one place (minimising the impact of future changes) and enables caching to be set during installation depending on target environment via the deployment process.

Programmatically Set HTTP Headers in ASPX Pages

Output caching can also be set in code in the code behind page (or indeed anywhere where the response object can be manipulated). The code snippet below shows setting the HTTP headers indirectly via the Response.Cache object:

Response.Cache.SetCacheability(HttpCacheability.NoCache); 
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1)); 
Response.Cache.SetNoStore();
Response.Cache.SetMaxAge(new TimeSpan(0,0,30));

This code would need to be added to each page and so results in duplicate code to maintain and again introduces the requirement for this to be remembered to be added to all new pages as they are developed. It results in the below headers being produced:

Cache-Control:no-cache, no-store

Expires:-1

Pragma:no-cache

 

Programmatically Set HTTP Headers in Global ASAX File

Instead of adding the above code in each page an alternative approach is to add it to the Global ASAX file so as to apply to all requests made through the application.

void Application_BeginRequest(object sender, EventArgs e)
{
	Response.Cache.SetCacheability(HttpCacheability.NoCache);
	Response.Cache.SetExpires(DateTime.Now);
	Response.Cache.SetNoStore();
	Response.Cache.SetMaxAge(new TimeSpan(0,0,30));
}

This would apply to all pages being requested through the application. It results in the below headers being produced:

Cache-Control:no-cache, no-store

Expires:-1

Pragma:no-cache

 

Explicitly define HTTP Headers outside of ASP.net Cache settings.

Explicitly Define HTTP Headers in ASPX Pages

The response object can have its HTTP Headers set explicitly instead of using the ASP.net Cache objects abstraction layer. This involves setting the header on every page:

void Page_Load(object sender, EventArgs e)
{
	Response.AddHeader("Cache-Control", "max-age=0,no-cache,no-store,must-revalidate");
	Response.AddHeader("Pragma", "no-cache");
	Response.AddHeader("Expires", "Tue, 01 Jan 1970 00:00:00 GMT");
}

Again as a page specific approach it requires a change to be made on each page. It results in the below headers being produced:

Cache-Control:max-age=0,no-cache,no-store,must-revalidate

Expires:Tue, 01 Jan 1970 00:00:00 GMT

Pragma:no-cache

Explicitly Define HTTP Headers in Global ASAX File

To avoid having to set the header explicitly on each page the above code can be inserted into the Application_BeginRequest event within the application’s Global ASAX file:

void Application_BeginRequest(object sender, EventArgs e)
{
	Response.AddHeader("Cache-Control", "max-age=0,no-cache,no-store,must-revalidate");
	Response.AddHeader("Pragma", "no-cache");
	Response.AddHeader("Expires", "Tue, 01 Jan 1970 00:00:00 GMT");
}

Again this results in the below headers being produced:

Cache-Control:max-age=0,no-cache,no-store,must-revalidate

Expires:Tue, 01 Jan 1970 00:00:00 GMT

Pragma:no-cache

Environment Specific Settings

It’s useful to be able to set the header values via configuration settings, not least to be able to test this change in a performance test environment via before/after tests.

All of the above changes should be made configurable and be able to be triggered/tweaked via the web.config file (and therefore can be modified via deployment settings).

Useful Links For More Information

Upgrading MVC 3 to MVC 4 via NuGet

I had to upgrade an old ASP.NET MVC 3 project to MVC 4 yesterday and whilst searching for the official instructions I found that there is a NuGet package that does all the hard work for you.

The official instructions for upgrading are in the MVC 4 release notes here: http://www.asp.net/whitepapers/mvc4-release-notes#_Toc303253806

But Nandip Makwana has created a NuGet package that automates this process. Check it out here: https://www.nuget.org/packages/UpgradeMvc3ToMvc4

It worked great for me.