10/22 Update: This post was written before this feature became a part of NuGet.This feature is now a part of NuGet and the latest version of this information can be found on the NuGet docs site.There is also a sample project available.
Over the last few years the ASP.NET team has been including more and more libraries into the various project templates that we ship with Visual Studio and other products. The latest MVC 3 templates come with jQuery, jQuery UI, jQuery Validation, Modernizr, and Entity Framework. All this built-in functionality is great but one remaining snag is that upgrading all the packages by hand can be a pain. Of course the NuGet package manager is an excellent tool that solves the package upgrade problem but so far having project templates with preinstalled NuGet packages was not possible.
This post describes a new feature that will be shipping in NuGet 1.5: preinstalled NuGet packages. Since it is not documented anywhere at this time the purpose of this post is to describe how to create a VS project template with NuGet packages. If you play around with it please also provide feedback so we can make changes/improvements.
Authoring VS project templates is out of scope of this article but you can read more on how to create a project template directly using Visual Studio or using the Visual Studio SDK.
NuGet 1.5 is not out yet but the feature has already been checked in and you can try it out by grabbing a recent NuGet preview build and installing the Visual Studio add-in (note that you might have to remove the previous version via the Extension Manager first).
Preinstalled packages work using project template wizards. A special wizard gets invoked when the project gets instantiated. The wizard loads the list of packages that need to be installed and passes that information to the appropriate NuGet APIs. The project template needs to specify where to find the package nupkg files. Currently two package repositories are supported:
A frequent question is why not support downloading the nupkg files directly from http://nuget.org. We decided not to support such an option because users expect project templates to instantiate quickly and downloading files from the internet would slow things down. Also, it would not work on a plane or in other situations where a connection is not available.
To add preinstalled packages to your project template you need to:
WizardExtension
<WizardExtension> <Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</Assembly> <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName> </WizardExtension>
<WizardData> <packages> <package id="jQuery" version="1.4.4" /> </packages> </WizardData>
<package>
id
version
The remaining step is to specify the repository where NuGet can find the package files. As mentioned earlier, two package repository modes are supported:
The approach I recommend for deploying Visual Studio project templates is through a VSIX package (read more about VSIX deployment here). The VSIX method is preferable because it allows you to package multiple project templates together and allows developers to easily discover your templates using the VS Extension Manager or the Visual Studio Gallery. On top of that you can easily push updates to your users using the Extension Manager automatic update mechanism.
<packages repository="extension" repositoryId="MyTemplateExtension.46431780-a0c7-44e0-83c7-5dbd985f8e49"> ... </packages>
The repository attribute specifies the type of repository (“extension”) while repositoryId is the unique identifier of your VSIX (i.e. the value of the ID attribute in the extension’s vsixmanifest file).
repository
repositoryId
ID
You need to add your nupkg files as custom extension content and ensure that they are located under a folder called Packages within the VSIX package. You can place the nupkg files in the same VSIX as your project templates or you can have the packages be located in a separate VSIX if that makes more sense for your scenario (just note that you should not reference VSIXs you do not have control over since they could change in the future and your project templates would break).
If packaging multiple projects is not important to you (e.g. you’re only distributing a single project template), a simpler but also more limited approach is to include the nupgk files in the project template zip file itself.
However, if you are bundling a set of project templates that relate to each other and share NuGet packages (e.g. you are shipping a custom MVC project template with versions for Razor, Web Forms, C#, and VB.NET), we do not recommend adding the NuGet packages directly to each project template zip file. It needlessly increases the size of the project template bundle.
<packages repository="template"> ... </packages>
The repository attribute now has the value “template” and the repositoryId attribute is not longer required. The nupkg files need to be placed into the root directory of the project template.
Solution quick links:
The new ASP.NET page framework built on Razor (which is available in MVC 3) provides a facility for content pages to contribute named fragments of markup to their layout pages which the layout page can then render in an arbitrary location using the RenderSection method.
RenderSection
For example, the following content page declares an "ExtraContent" section:
@{ Layout = "~/Views/Shared/_Layout.cshtml"; } @section ExtraContent { <div>Some extra content</div> } <div>The main content</div>
And the following layout page renders it:
<!DOCTYPE html> <html> <head></head> <body> @RenderBody() @RenderSection("ExtraContent") @RenderSection("OptionalContent", required: false) </body> </html>
You can even declare that a section is not required like the “OptionalContent” section in the example above.
But what if you want to have some default content for your optional sections?
The IsSectionDefined method returns true if a child content page defined a section. You can use that to decide whether to render a section or some other content:
IsSectionDefined
<!DOCTYPE html> <html> <body> @RenderBody() @if (IsSectionDefined("OptionalContent")) { @RenderSection("OptionalContent") } else { <div>Default content</div> } </body> </html>
Just remember that you need to use the @RenderSection() syntax (you need the @ character) so that the contents of the section is actually printed to the output. Without that character you will get an exception with the following message:
@RenderSection()
The following sections have been defined but have not been rendered for the layout page "~/Views/Shared/_Layout.cshtml": "OptionalContent"
Razor provides a way to pass inline markup templates into function calls. This is a powerful mechanism that allows you to combine custom markup fragments with general purpose display logic. In order to make the following example work you will need to drop the following code into your project:
using System.Web.WebPages; using System; public static class SectionExtensions { private static readonly object _o = new object(); public static HelperResult RenderSection(this WebPageBase page, string sectionName, Func<object, HelperResult> defaultContent) { if (page.IsSectionDefined(sectionName)) { return page.RenderSection(sectionName); } else { return defaultContent(_o); } } }
This code essentially wraps the previous example in a reusable extension method. All you need now is to provide the default content markup. With it you can write views like the following:
<!DOCTYPE html> <html> <body> @RenderBody() @this.RenderSection("OptionalContent", @<div>Default content</div>) </body> </html>
For the second parameter you need to use the @ character to trigger the Razor parser to go into markup mode. Once in markup mode you can do anything that you do in the main body of the page.
Just remember that since this is an extension method you need to call it off the this object. Otherwise the C# compiler will not be able to locate it and you will get a compilation error.
this
If you like this technique read the follow up: Razor, Nested Layouts and Redefined Sections
12/10 Update: In MVC 3 RC 2 SkipRequestValidationAttribute got renamed to AllowHtmlAttribute. I have updated the examples below.
SkipRequestValidationAttribute
AllowHtmlAttribute
A little while ago I wrote a blog post describing granular request validation that shipped in MVC 3 Beta. However, since then we have changed the API for this feature and that post is no longer valid. In this post I will present the new API which is usable in the recently-shipped MVC 3 Release Candidate.
But first: a quick refresh on request validation and why it’s great to make it granular. Request validation is a feature of ASP.NET that analyzes the data that a browser sends to the server when a user interacts with your site (such as form or query string data) and rejects requests that contain suspicious input that looks like html code (basically anything with a ‘<’). This protects you from HTML injection attacks such as cross-site scripting (XSS). It is enabled by default, however in previous versions it was an all-on-or-off feature, meaning that if you want to be able to accept HTML-formatted input from your users in just one field you had to completely turn this protection off. This in turn meant that you now had to validate every bit of data that came from the client.
In MVC 3 we are introducing a new attribute called AllowHtmlAttribute. You can use this attribute to annotate your model properties to indicate that values corresponding to them should not be validated. Let’s take this User model and UserController as an example:
public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } [AllowHtml] public string Description { get; set; } [AllowHtml] public string Bio { get; set; } } public class UserController { [HttpPost] public ActionResult Update(User user) { // update user database } }
I have annotated the Description and Bio properties to indicate they should not be request-validated. Now when the Update action method gets invoked these two properties on the User object will not be validated and any HTML they might contain will be passed straight through to the action method. However, everything else will still go through request validation and requests that contain suspicious content in the Name or Email fields will get rejected.
AllowHtmlAttribute can only be applied to properties of a model class. For other request validation scenarios the existing ValidateInputAttribute is still helpful. For example, you can use it to disable request validation for action methods that bind to a loose collection of parameters:
ValidateInputAttribute
[ValidateInput(false)] public ActionResult Update(int userId, string description) { }
Now when the parameters of the Update method get bound request validation will not be performed. You can apply ValidateInput to action methods as shown above or to the entire controller to affect all of its action methods.
ValidateInput
ValidateInput is also more usable in MVC 3. In MVC 2 running on .NET 4 you had to set requestValidationMode="2.0" in order to turn request validation off. In MVC 3 this is no longer necessary.
requestValidationMode="2.0"
6/25 Update: This sample has become part of the Dynamic Data Futures project on codeplex. You can still use this code but you should check out the Futures project as it contains a lot more. The key conecpts are the same, though some names or APIs might have changed.
Note: This post is part of a series, see the list of other Dynamic Data samples.
A while back I posted some sample code for adding custom metadata providers. Since then much has changed in the Dynamic Data runtime and it is time to post an updated version. This sample illustrates how to write a custom metadata provider that allows you to programmatically add metadata attributes to an in-memory store before you register your model with the Dynamic Data runtime in Global.asax.
This sample should work with the latest preview release of Dynamic Data.
Download the metadata provider sample solution, extract to your preferred location, and open in Visual Studio 2008 SP1. The solution contains two projects: a sample website and a small library project that contains the metadata provider code.
Run the included website and go the Products list page. Once there hit Edit on any of the rows. You will be taken to a details view for the row you are editing. Change the UnitsInStock field to a negative value and move out of the field: a range validation error appears. Go back to the same field, clear it, and move out again: a required validation error appears.
The website scaffolds a small subset of the Northwind sample database. The UnitsInStock column on the Product table is decorated with two attributes: RequiredAttribute and RangeAttribute. However, the unique thing here is that each attribute is coming from a different source.
The RequiredAttribute is declared in the default Dynamic Data way using a metadata proxy class:
[MetadataType(typeof(Product_MD))]public partial class Product { private class Product_MD { [Required(ErrorMessage="This field is required [from MetadataType]")] public object UnitsInStock { get; set; } }}
The RangeAttribute is added to the InMemoryMetadataManager class in Global.asax:
InMemoryMetadataManager.AddColumnAttributes<Product>(p => p.UnitsInStock, new RangeAttribute(0, 1000) { ErrorMessage = "This field must be between {1} and {2} [from InMemeroyMetadataManager]." });
The AddColumnAttributes function shown here has a way to strongly type the property references (as opposed to writing something like AddColumnAttributes("UnitsInStock", ...)) using a simple lambda expression. This provides for some nice IntelliSense support that would not be available if you were referring to properties using simple strings.
In order to have the Dynamic Data runtime pick up the metadata attributes added to InMemoryMetadataManager you need to modify your model registration call in Global.asax:
model.RegisterContext(typeof(NorthwindDataContext), new ContextConfiguration() { ScaffoldAllTables = true, MetadataProviderFactory = (type => new InMemoryMetadataTypeDescriptionProvider(type, new AssociatedMetadataTypeTypeDescriptionProvider(type)))});
The MetadataProviderFactory property of ContextConfiguration lets you specify a metadata provider factory method that is used by the Dynamic Data runtime to obtain an instance of a TypeDescriptionProvider that acts as the source of metadata attributes for a given table (type). The sample above also illustrates TypeDescriptionProvider chaining, which is what supports the fact that the table receives metadata from two sources: the InMemory provider adds its metadata to the results returned by the AssociatedMetadataType provider and returns a combined collection.
At this point it is tempting to consider a dynamic metadata provider that would let you add, remove, and modify attributes throughout the lifetime of the application and not just during the application's startup phase. For example, you could imagine having some sort of administrative interface that would let you turn on and off whether a field is required or modify its description, even after the model has already been registered.
While it would seem that such a scenario would require minimal changes to the InMemoryMetadataManager class there is one detail about Dynamic Data that makes this a lot more complicated: the current Dynamic Data runtime fetches the metadata upon model registration and then caches it inernally for the lifetime of the app domain. This means that any changes that you make to the metadata through InMemoryMetadataManager after the model has been registered will be ignored.
This is a limitation of Dynamic Data that we will address in future versions. For the time being the only way to get around this would be to unload the app domain. However, this is means that you will need to find a place (such as a database) to store the new metadata while the app restarts.
My old custom metadata provider sample had an implementation of an XML-based metadata provider but the current one does not. The reason for this is that since the December CTP we have added support for even more attributes and some of the attributes have become a lot more complex. Because much of the old sample had to do with deserializing CLR attributes from an XML representation instead of anything specific to Dynamic Data I decided not to develop the old XML provider any further. However, if somebody ever wrote the appropriate XML parsing code it could easily be used to populate the InMemoryMetadataManager with the right values, essentially resulting in an XML metadata provider. Just remember about the domain unloading mentioned earlier.
Earlier today Sam Saffron from the StackExchange team blogged about the performance of view lookups in MVC. Specifically, he compared referencing a view by name (i.e. calling something like @Html.Partial("_ProductInfo")) and by path (@Html.Partial("~/Views/Shared/_ProductInfo.cshtml")). His results indicate that in scenarios where a page is composed from many views and partial views (for example, when you are rendering a partial view for each item in a list) it’s more efficient to reference your views using the full path instead of just the view name.
@Html.Partial("_ProductInfo")
@Html.Partial("~/Views/Shared/_ProductInfo.cshtml")
I know that bit of code quite well and it seemed strange to me that such a performance difference would exist because view lookups are cached regardless of how you reference them. Specifically, once an application is warmed up both the Razor and WebForms view engines use the lookup parameters to retrieve the cached results. If anything the cache key produced from a view path is usually longer than the one produced from a view name but even that should not have a measurable impact.
While I cannot explain the differences that Sam is seeing (and since I know he is seeing them in production too I’m quite sure he has the application configured correctly for performance testing) I thought this would be a good opportunity to present the basics of MVC view lookup optimizations as well as a few additional techniques that people might not be familiar with.
Hopefully you won’t go replacing all of your view references with full paths. Using view names is still easier and more maintainable. And before you do any perf-related changes you should always measure your application. It might turn out that it is fast enough for your needs already.
You should always make sure that your application is compiled in Release mode and that your web.config file is configured with <compilation debug="false" />. That second part is super-important since MVC will not do any view lookup caching if you are running your application in debug mode. This helps when you are developing your application and frequently adding/deleting view files, but it will kill your performance in production.
<compilation debug="false" />
This might seem like obvious advice, but I have seen even experienced devs get bitten by this.
I’ve mentioned this before but it’s worth repeating. The MVC framework supports having multiple view engines configured simultaneously in your application and will query each in turn when it is trying to find a view. The more you have the longer the lookups will take, especially if the view engine you are using is registered last. In MVC 3 we register two view engines by default (WebForms and Razor) and in all versions you might have installed 3rd party view engine such as Spark on nHaml. There is no reason to pay the performance price for something you are not using so make sure you specify only the view engines you need in your Global.asax file:
protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new RazorViewEngine()); ... }
By default (when running in Release mode, of course) MVC will cache the results of the lookups in the application cache available via HttpContext.Cache. While this cache works great and helps us avoid having to check for view files on disk there is also a cost associated with using it (this includes the cost of a thread-safe lookup as well as all the additional cache management such as updating entry expiration policies and performance counters).
HttpContext.Cache
To speed things up you could introduce a faster cache in front of the application cache. Fortunately all view engines deriving from VirtualPathProviderViewEngine (that includes WebForms and Razor) have an extensibility point via the settable ViewLocationCache property.
VirtualPathProviderViewEngine
ViewLocationCache
So we can create a new class that implements the IViewLocationCache interface:
IViewLocationCache
public class TwoLevelViewCache : IViewLocationCache { private readonly static object s_key = new object(); private readonly IViewLocationCache _cache; public TwoLevelViewCache(IViewLocationCache cache) { _cache = cache; } private static IDictionary<string, string> GetRequestCache(HttpContextBase httpContext) { var d = httpContext.Items[s_key] as IDictionary<string, string>; if (d == null) { d = new Dictionary<string, string>(); httpContext.Items[s_key] = d; } return d; } public string GetViewLocation(HttpContextBase httpContext, string key) { var d = GetRequestCache(httpContext); string location; if (!d.TryGetValue(key, out location)) { location = _cache.GetViewLocation(httpContext, key); d[key] = location; } return location; } public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) { _cache.InsertViewLocation(httpContext, key, virtualPath); } }
and augment our view engine registration in the following way:
protected void Application_Start() { ViewEngines.Engines.Clear(); var ve = new RazorViewEngine(); ve.ViewLocationCache = new TwoLevelViewCache(ve.ViewLocationCache); ViewEngines.Engines.Add(ve); ... }
This TwoLevelViewCache will work best in views that call the same partial multiple times in a single request (and should hopefully have minimum impact on simpler pages).
You could go even further and instead of the simple dictionary stored in httpContext.Items you could use a static instance of ConcurrentDictionary<string, string>. Just remember that if you do that your application might behave incorrectly if you remove or add view files without restarting the app domain.