ASP.NET Core MVC Input Tag Helper Deep Dive

In my last post I touched briefly on how tag helpers can be used as an alternative to HTML helpers in ASP.NET Core MVC.

In this series, we will take a deep dive into the InputTagHelper.

Overview

The input tag helper is used to bind an HTML input element to a model property in your razor view. It is an alternative to @Html.EditorFor(m => m.MyProperty).

Given a view that is bound to the following model class:

public class SimpleViewModel
{
public string Email { get; set; }
}

We can specify an input element for the Email property using the asp-for attribute:

<input asp-for="Email" />

This will cause the Input Tag Helper to process the input tag on the server side and generate the following HTML to be sent to the browser:

<input type="text" id="Email" name="Email" value="thisaddress@isfrommymodel.com" />

The tag helper adds an id and a name based on the property name specified in the asp-for attribute. The type of the input was set to text because the Email property is a string. If the property specified is a bool, then the tag helper will generate a input of type checkbox.

.NET Type Input Type
String type=”text”
DateTime type=”datetime”
Byte type=”number”
Int16, Int32, Int64 type=”number”
Single, Double type=”number”
Boolean type=”checkbox”

Validation and Other Types

In addition to being aware of property types, the input tag helper is also aware of common data annotations for special types and validation. For example, if we add the [Required] attribute to the Email property…

public class SimpleViewModel
{
[Required]
public string Email { get; set; }
}

…then the tag helper would output HTML with the data-val-required attribute needed for jQuery Validation on the client side.

<input type="text" data-val="true" 
data-val-required="The Email field is required."
id="Email"
name="Email"
value="" />

Adding the [EmailAddress] attribute will change the type to email and add data-val-email so jQuery Validation can validate that the value is a valid email address.

<input name="Email" 
id="Email"
type="text"
value=""
data-val-required="The Email field is required."
data-val="true">

Here are a some attributes that the input tag helper will map to specific input types:

Attribute Input Type
[EmailAddress] type=”email”
[Url] type=”url”
[HiddenInput] type=”hidden”
[Phone] type=”tel”
[DataType(DataType.Password)] type=”password”
[DataType(DataType.Date)] type=”date”
[DataType(DataType.Time)] type=”time”

You can also navigate to child properties of your view model. Consider a more complex model class that contains a child Address property.

public class AddressViewModel
{
public string AddressLine1 { get; set; }
}

public class RegisterViewModel
{
public string UserName { get; set;}
public AddressViewModel Address { get; set; }
}

In the view, we can bind our input to Address.AddressLine1…

<input asp-for="Address.AddressLine1" />

…which will generate the following HTML:

<input name="Address.AddressLine1" id="Address_AddressLine1" type="text" value="">

Merging HTML Attributes

One of the really nice things about tag helpers is that it allows you to specify additional HTML attributes on an element. If you specify attributes on an input element, the tag helper will merge those attribute with any attributes it also adds to the element.

A simple example is adding a class to an input.

<input asp-for="Email" class="form-control" />

…will generate the following HTML:

<input class="form-control" type="text" id="Email" name="Email" value="thiscomes@frommymodel.com" />

You can add any additional attribute you want to the input element and it will be included in the generated HTML. This includes any data-* or ng-* attributes that you might be used for a client side framework.

Formatting Values

The input tag helper also provides an asp-format attribute. This attribute allows you to specify a standard .NET string format that will be applied to the value of the input.

For example, if you wanted to display a numeric value to 4 decimal places, you could use the asp-format attributes as follows:

<input asp-for="SomeNumber" asp-format="{0:N4}"/>

This would have not add any additional attributes to the input element but would format the value of SomeNumber of 4 decimal places.

Intellisense and Strong-Typing

Visual Studio 2015 provides Intellisense for the asp-for attribute. As you can see here, it will tell you what properties are available on the current model.

Visual Studio will also highlight errors if we specify a property name that does not exist:

An error is thrown at view compile time. By default, razor views are not pre-compiled when you run your application in Visual Studio so you won’t get the error until you try to dynamically access the page.

Note: At the time of writing this, rename refactorings of model properties do not apply to the asp-for attribute values but I do expect that this will change in the future.

Conclusion

That should cover everything you need to know to start using the input tag helper. In the next blog post, I cover the Validation tag helpers that are used to display client and server side validation messages.

Cleaner Forms using Tag Helpers in ASP.NET Core MVC

ASP.NET Core MVC introduces a new feature called Tag Helpers. In this post, we will explore how tag helpers can be used to improve the readability of your Razor views that generate HTML forms.

How do Tag Helpers work?

Tag Helpers are an alternative to HTML helpers for generating HTML. The easiest way to show this is with an example.

Let’s start by looking at an extremely simple example of a login view that is bound to a LoginViewModel that contains a UserName and a Password:

public class LoginViewModel
{
public string UserName { get; set; }

public string Password { get; set; }
}

Here is how we would generate an HTML input for the UserName property using an HTML Helper and a Tag Helper.

<!--Create an input for UserName using Html Helper-->
@Html.EditorFor(l => l.UserName)
<!--Create an input for UserName using Tag Helper-->
<input asp-for="UserName" />

With the HTML helper, we call C# code that return some HTML. With Tag Helpers we augment some HTML with special tag helper attributes. These special attributes are processed by MVC which will generate some HTML. Both of these approaches above will generate an input that looks like this:

<input name="UserName" class="text-box single-line" id="UserName" type="text" value="">

Why is this better?

At first glance, it might look like Tag Helpers are just a syntax change with no obvious benefits. The difference however, can make your Razor forms much more readable. Let’s say we wanted to do something simple like add a class to our UserName input:

<!--Create an input with additional class for UserName using Html Helper-->
@Html.EditorFor(l => l.UserName, new { htmlAttributes = new { @class = "form-control" } })
<!--Create an input with additional class for UserName using Tag Helper-->
<input asp-for="UserName" class="form-control" />

As you can see, the HTML helper approach becomes very hard to understand while the tag helper approach is very clear and concise.

Here is a full blown example of a login form using HTML helpers:

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })

<div class="form-group">
<div class="row">
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="row">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
</div>
</div>

<div class="form-group">
<div class="row">
<div class="col-md-offset-2 col-md-2">
<input type="submit" value="Log in" class="btn btn-primary" />
</div>
</div>
</div>
}

And now the same form using tag helpers:

<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" class="form-horizontal" role="form">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="UserName" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
</form>

Overall, the tag helper version is much more readable. I especially like that we no longer need to use a using statement to generate a form element. That had always felt like a bit of a hack to me.

Another nice thing with the form tag helpers is that we don’t need to remember to explicitly add the AntiForgeryToken. The form tag helper does this automatically unless we explicitly turn it off using asp-anti-forgery=”false”

Of course, Visual Studio does a good job of highlighting the tag helper attributes so it is easy to distinguish them from regular HTML attributes

We also get full Intellisense inside the asp-for attributes.

How to enable Tag Helpers

The MVC Tag Helpers are located in the Microsoft.AspNet.Mvc.TagHelpers package so you will need to add a reference to that in your project.json file. Once you have added the reference, you can enable tag helpers in all your views by adding the following code to GlobalImports.cshtml.

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

Conclusion

HTML helpers are still supported in ASP.NET Core MVC and you can keep using them in the same way as you always did. I think Tag Helpers provide a cleaner syntax that more closely matches the HTML that will be generated. Sound off on the comments…what are your thoughts on tag helpers?

Stay tuned, in the next couple posts, we will explore each ASP.NET Core MVC tag helper in more detail.

Link and Script Tag Helpers in ASP.NET Core MVC

In my last post we explored how to use the environment tag helper to generate different HTML for Development versus Production environments in ASP.NET MVC6. I very briefly touched on the link and script tag helpers. In this post, we will dig into the details of these very useful tag helpers.

Overview

The link and script tag helpers are intended to make it easier for us to add link and script tags to our HTML. In particular, these tag helpers simplify the process of referencing a large number of files in a development environment, referencing CDNs with local fallbacks and cache busting.

Globbing

No, I’m not making up words. Globbing is a way to specify sets of filename using wildcard patterns. In a very simple example, we could specify all the JavaScript files in a particular folder by specifying the pattern /scripts/*.js. This pattern, however, would not include any of the files in sub-directories. This is where globbing gets more interesting. We can include all JS files in a folder and all subfolders by specifying the pattern /scripts/**/*.js.

That’s nice, but how does this relate to the link and script tag helpers? Well, imagine you are building an web application that consists of a very large number of JavaScript files. When running in production, you will probably want to combine all those files into a single file, but in a development environment it can be convenient to reference each individual file. Remembering to add a new script tag every time you add a new JavaScript file can be tedious and error prone. The script tag helper lets you specify glob patterns for all the files that you want to include.

We could include all the individual script files above using a single script tag helper as follows:

<script asp-src-include="~/app/**/*.js"></script>

Which will generate the following HTML:

<script src="/app/app.js"></script>
<script src="/app/controllers/controller1.js"></script>
<script src="/app/controllers/controller2.js"></script>
<script src="/app/controllers/controller3.js"></script>
<script src="/app/controllers/controller4.js"></script>
<script src="/app/services/service1.js"></script>
<script src="/app/services/service2.js"></script>

There is also src-exclude attribute if there were a set of files you wanted to exclude from the list. For example, to exclude the services scripts in the example above, we would use the following:

<script asp-src-include="~/app/**/*.js" 
asp-src-exclude="~/app/services/**/*.js">
</script>

The nice thing about using a glob pattern is that we don’t need to remember to update our HTML every time we add or remove a JavaScript file to our application.

The link tag helper works in exactly the same way with the asp-href-include and asp-href-exclude attributes.

CDNs and Fallbacks

It is a common approach to reference popular frameworks from hosted CDN in order to reduce network load on your server and potentially improve performance for the end user. For popular frameworks like jQuery and Bootstrap, there is a good chance that the browser will already have a cached version of these files.

Referencing files from a CDN can be a bit of a pain because you also need to provide a fallback to a version of the file hosted on your servers. A fallback is necessary because you do not want your application to go down just because the CDN is not currently reachable.

The script and link tag helpers make it easier to specify a fallback test and file location. Here is a basic example for the popular Bootstrap framework:

<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css"
asp-fallback-test-class="hidden"
asp-fallback-test-property="visibility"
asp-fallback-test-value="hidden" />


<script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js"
asp-fallback-test="window.jQuery">
</script>

Which is infinitely cleaner than manually writing the following HTML that gets generated by the tag helpers:

<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" />
<meta name="x-stylesheet-fallback-test" class="hidden" />
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName("SCRIPT"),g=f[f.length-1].previousElementSibling,h=e.defaultView&amp;&amp;e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&amp;&amp;h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}("visibility","hidden",["\/lib\/bootstrap\/css\/bootstrap.min.css"]);</script>

<script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js">
</script>

<script>(typeof($.fn.modal) === 'undefined'||document.write("<script src=\"\/lib\/bootstrap\/js\/bootstrap.min.js\"><\/script>"));</script>

Cache Busting

Cache busting is the process of appending some form of file version hash to the filename of resources like JavaScript and CSS files. The performance advantage of doing this is that we can tell the browser to cache these files indefinitely without worrying about the client not getting the latest version when the file changes. Since the name of the resource changes when the file contents change, the updated files are always downloaded. Mads Kristensen has a really good overview of cache busting in previous versions of ASP.NET. Luckily, the new link and script tag helpers make this MUCH easier.

To enabling cache busting, just set the asp-append-version attribute to true.

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Which will generate a link tag in HTML with a file version query parameter appended:

<link rel="stylesheet" href="/css/site.min.css?v=UdxKHVNJA5vb1EsG9O9uURFDfEE3j1E3DgwL6NiDGMc" />

The process is identical for the script tag helper.

Conclusion

The new link and script tag helpers make the process of including multiple files, referencing CDN hosted resources and cache busting extremely simple.

August 29, 2015 – Updated to align with new renamed attributes in Beta 6 and newer

Web Optimization – Development and Production in ASP.NET Core MVC

Updated Jan. 5 2015: Updated for ASP.NET 5 RC1
Updated May 22 2016: Updated for ASP.NET 5 RC2

Bundling and minification is an important optimization technique for managing scripts and stylesheets in any web application. ASP.NET Core MVC takes a different approach than previous versions to solving this problem. In this post I will illustrate the new approach using a simple example.

How it worked before

In previous versions of MVC, bundling and minification were handled by configuring bundles in the web optimization framework. You would define named bundles of JavaScript or CSS code in a BundleConfig class. Let’s take a simple example where the styles for our application are split into 2 separate files. The style bundle would be defined as follows:

bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/site1.css",
"~/Content/site2.css"));

In your razor view, you could reference a bundle using the @Scripts and @Styles helpers. If you were running the application in a Debug mode, the @Scripts helper would add individual script references for each file in a bundle. Likewise, the @Styles helper would render individual links to each CSS file in the bundle.

For example, the following code:

@Styles.Render("~/Content/css")

would render the following HTML when executed in a debug mode:

<link href="/Content/site1.css" rel="stylesheet"/>
<link href="/Content/site2.css" rel="stylesheet"/>

In a Release mode the web optimization framework would optimize the bundles by generating a single combined and minified file for each bundle that is referenced. The same example above in a release configuration would render the following HTML:

<link href="/Content/css?v=NNbQGTaB9DsVxla4W-u6EqSVahvQb1lT0FjAB1yOkvo1" rel="stylesheet"/>

Testing the optimized and non-optimized versions in Visual Studio was relatively easy. You could turn debug mode on/off from the web.config or manually override turn optimization on/off by setting the BundleTable.EnableOptimizations value at application startup.

How it works in ASP.NET Core MVC

The first big change is that the bundling and minification steps happen at build time instead of run time.

Optimizing assets

In previous versions, the optimized file was created on the fly by the web optimization framework. In ASP.NET Core MVC, you are expected to manage bundling and minification using a task runner like Grunt or Gulp. One of the big advantages to using a task runner for bundling and minification is that we have access to a vast number of existing tasks from the grunt and gulp ecosystems.

The default ASP.NET Core MVC project template uses Gulp so I will use that to show how accomplish this.

First, we will add 2 node packages for the gulp package to bundle (gulp-concat) and minify CSS (gulp-minify-css) files. Add these to the package.json file:

{
"name": "ASP.NET",
"version": "0.0.0",
"devDependencies": {
"gulp": "3.8.11",
"gulp-concat": "2.5.2",
"gulp-minify-css": "1.1.1",
"rimraf": "2.2.8"
}
}

Next, include these 2 packages in gulpfile.js. Node packages are included in a gulp file using requirejs:

var gulp = require("gulp"),
minifycss = require("gulp-minify-css"),
concat = require("gulp-concat");

After that, add a css property to the paths property:

var paths = {
bower: "./bower_components/",
lib: "./" + project.webroot + "/lib/",
css: "./" + project.webroot + "/css/",
};

Finally, add a gulp task to combine and minify any CSS files from the css folder.

gulp.task("minifycss", function () {
return gulp.src([ paths.css + "/*.css",
"!" + paths.css + "/*.min.css"])
.pipe(minifycss())
.pipe(concat("site.min.css"))
.pipe(gulp.dest(paths.css));
});

You can run this task manually from the command line by calling gulp minifycss:

Another way to run it is using the Task Runner Explorer. Launch the Task Runner Explorer by right clicking gulpefile.js in the Solution Explorer and selecting Task Runner Explorer. From the task runner explorer window, you can run any task by right clicking it and selecting Run.

Important: You can (and should) also configure these tasks to run automatically whenever you change the source css files. See my previous post on using Gulp in Visual Studio for more details on this.

The process for optimizing JavasScript files is very similar to the process for CSS, but we use gulp-uglify instead of gulp-minify-css. We will explore the JavaScript scenario further in the next post.

Now our wwwroot folder contains both optimized and non-optimized versions of our CSS:

Of course, this is just a very simple example of how you might choose to optimize your files. The process is entirely up to you and very flexible. I encourage you to explore the vast set of plugins available.

Managing Development vs Production environments

In previous version of MVC, the @Scripts and @Styles helpers would decide when to reference all the un-optimized files and when to reference the auto-generated optimized versions. The Debug vs Release configuration was used to determine when to use the optimized versions. While this was ultimately convenient from a developer perspective, it was never very clear what was happening. When looking at the razor view, it was hard to tell what has happening.

ASP.NET Core you use the ASPNETCORE_ENVIRONMENT environment variable instead of debug/release configurations to decide which files to reference. Typical values for ASPNETCORE_ENVIRONMENT are Development, Staging and Production. The default when running ASP.NET MVC Core from Visual Studio is Development. You can see this by viewing the Properties of your MVC project file in Visual Studio.

ASPNETCORE_ENVIRONMENT Variable

In your Razor view, you can use the environment tag helper to decide how to reference your scripts and stylesheets based on the current Hosting:Environment value. The link tag helper is used to append a version to the minified file name in Staging and Production. This technique is often called cache busting. A similar script tag helper exists for referencing script files.

<environment names="Development">            
<link rel="stylesheet" href="~/css/site1.css" />
<link rel="stylesheet" href="~/css/site2.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="~/css/site.min.css" asp-file-version="true"/>
</environment>

Learn more about tag helpers here

Looking at this razor code, it is much more obvious how a file will be reference in Development vs Staging/Production. In Development, the HTML is as follows:

<link rel="stylesheet" href="/css/site1.css" />
<link rel="stylesheet" href="/css/site2.css" />

In Staging and Production, the HTML is as follows:

<link rel="stylesheet" href="/css/site.min.css?v=UdxKHVNJA5vb1EsG9O9uURFDfEE3j1E3DgwL6NiDGMc" />

Testing Production from Visual Studio

To test Staging or Production builds from Visual Studio, you need to create a new debug profile and set the Hosting:Environment environment variable to the desired value.

Here are some simple steps to create a Production profile:

  1. Right click on your project in Solution Explorer and select Properties.
  2. Select the Debug tab
  3. Click New… to create a new profile and enter ‘IIS Express - Prod” for the name.
  4. For Launch, select IIS Express
  5. Add a new Environment Variable with name ASPNETCORE_ENVIRONMENT and value Production Save your project.

Setting ASPNETCORE_ENVIRONMENT to Production

Now, from the Run menu in Visual Studio, you can select the IIS Express – Prod profile to run and test your application using the optimized versions of scripts and styles.

Conclusion

While this new approach for web optimization in ASP.NET Core MVC may seem like a little more work than in previous versions, it does provide much more flexibility and in my opinion aligns better with the majority of the web development world.

We very briefly touched on the link and script tag helpers. There is a lot of functionality available (CDNs, fallbacks, globbing, etc.) in these tag helpers and we will explore these in more detail in the next post –> Overview of Link and Script Tag Helpers in MVC6.

Displaying Custom ASP.NET MVC Views per Deployment

Have you ever been asked to deploy a second (or third or fourth) instance of an ASP.NET MVC app but with minor customizations to the views? Maybe it’s something as simple as a logo or color change, but more likely it is much more elaborate customizations that require completely different layouts and view structure.

One option would be to add application settings for each individual view variation then add conditional logic to your razor views so they can render differently based on those settings. This might work for very simple cases but it will quickly becomes unwieldy.

I have found an option using a custom view engine that is surprisingly simple. Here’s how it works.

View Folder Structure

We will implement a custom view engine that will extend the existing View folder layout to support custom views per deployment. By default, everything works just like the base MVC rendering engine. If you need to customize a view for a particular deployment, just add views in a sub-folder with a name that is unique to that deployment.

In the example below, there is a custom Contact.cshtml view for deployments using Client1 and _Client2. _There is also a default Contact.cshtml that is used for any other deployments. The custom views are optional. The flexibility here I great because it allows us to put anything we want in the custom Client1 and Client2 views.

Note that the custom views are completely optional. In the example above, there are no customizations for the About.cshtml and Index.cshtml pages. All deployments will use the default views.

Custom View Engine

To make this all work, we need to implement a custom view engine that knows to look for custom views before falling back to the default views.

This is actually much easier than I had anticipated. The constructor for our custom view engine takes in a string indicating the name of the folders that would contain the custom views. Using that folder name, all it needs to do is specify new view location formats for the RazorViewEngine base class.

public class CustomViewEngine : RazorViewEngine
{
    public CustomViewEngine(string customViewName)
    {
        base.AreaViewLocationFormats = new string[] {
            "~/Areas/{2}/Views/{1}/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"

        };

        base.AreaMasterLocationFormats = new string[] {
            "~/Areas/{2}/Views/{1}/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"

        };

        base.AreaPartialViewLocationFormats = new string[] {
            "~/Areas/{2}/Views/{1}/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/" + customViewName + "/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"
        };

        base.ViewLocationFormats = new string[] {
            "~/Views/{1}/" + customViewName + "/{0}.cshtml",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/" + customViewName + "/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",               
        };

        base.PartialViewLocationFormats = new string[] {
            "~/Views/{1}/" + customViewName + "/{0}.cshtml",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/" + customViewName + "/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml"
        };

        base.MasterLocationFormats = new string[] {
            "~/Views/{1}/" + customViews + "/{0}.cshtml",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{1}/" + customViewName + "/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml"
        };
    }
} 

Note that I did not include *.vbtml because my app only uses cshtml files.

In web.config, add an app setting to specify which custom view folder you want to use:

  <appSettings>
    <add key="CustomViewName" value="Client1"/>
  </appSettings>

 

And finally, set the view engine in the Global.asax application start method

ViewEngines.Engines.Clear();
string customViewName = ConfigurationManager.AppSettings["CustomViewName"];
ViewEngines.Engines.Add(new CustomViewEngine(customViewName));

 

Now, by changing a setting in Web.config, we can show custom views in different deployments of the application.

Keeping it manageable

This can very quickly become difficult to manage. One trick is to use partial views for the portion of a page that might be different between deployments. For example, if the top navigation header is different per deployment, then I would extract the navigation header to a partial view and create custom version of that partial.

This way, my base _Layout.cshtml stays simple and is the same for all deployments.

<body>
    @Html.Partial("_NavHeader")
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>

…and I am able to override the navigation header as needed.

Conclusion

Now we have a simple approach that allows us to have customized views for individual deployments of our ASP.NET MVC application. Keep in mind that this approach only addresses the problem of customizing views between deployments. It does not in any way address the problem of having custom application logic in your controllers or services.

2015 MVP Virtual Conference–May 14-15

I wanted to let you know about a great free event that Microsoft and the MVPs are putting on, May 14th & 15th. A group of great Microsoft MVPs will be sharing knowledge and real-world expertise during a free event, the MVP Virtual Conference.

MVP15_MicrosoftMVP_VC_WebBanner_920x400px

The MVP Virtual Conference will showcase 95 sessions of content for IT Pros, Developers and Consumer experts designed to help you navigate life in a mobile-first, cloud-first world. Microsoft’s Corporate Vice President of Developer Platform, Steve Guggenheimer, will be on hand to deliver the opening Key Note Address.

Why should you attend?

Because the content will be awesome! Go ahead and review the agenda. The conference will have 5 tracks, IT Pro English, Dev English, Consumer English, Portuguese mixed sessions & Spanish mixed sessions, there is something for everyone! Learn from the best and brightest MVPs in the tech world today and develop some great skills!

Be sure to register quickly to hold your spot and tell your friends & colleagues.