Marcin On ASP.NET

Keeping my eye on the dot

Posts
  • Marcin On ASP.NET

    MVC 3 Project Upgrade Tool

    • 24 Comments

    We have just released the final version of ASP.NET MVC 3. Read the official MVC 3 release information or download MVC 3.

    To help you upgrade your MVC 2 projects to MVC 3 we have released an upgrade tool. You can download it here: http://aspnet.codeplex.com/releases/view/59008. (This is an update of the tool that Eilon Lipton previously previewed on his blog and now we are giving it a more permanent home on our CodePlex site).

    April 11 Update: We made a small update to the tool that allows you to skip the backup step. You might want to consider skipping the backup step if you use source control or if your solution is very big.

    January 14 Update: We have updated the file on codeplex to fix an issue with converting solutions that had Web Sites or other solution items. If you are having problems please make sure you download the tool again from the codeplex site.

    The upgrade tool only supports Visual Studio 2010 projects targeting .NET 4. Upgrading both MVC 2 and MVC 3 Beta (or RC) projects is supported.

    The tool does not support Visual Studio 2008 solutions, MVC 1 projects, or projects targeting .NET 3.5. Those projects will first have to be upgraded using Visual Studio 2010 and/or retargeted for .NET 4:

    • If you have a Visual Studio 2008 solution you can simply open it with Visual Studio 2010 to initiate the solution upgrade process.
    • If your projects are targetting .NET 3.5 read how to target .NET 4.
    • If you have a MVC 1.0 project you can use the old version of this tool to convert it to MVC 2.

    MVC upgrade tool screenshot

    The usage of the tool is quite simple:

    1. If you use a source control system make sure your files are checked out and writable
    2. Run the executable
    3. Select your Visual Studio 2010 solution file that contains your project
    4. Review the changes for each item in the Item details pane
    5. Click on the Convert button to initiate the conversion process
    6. Review the results in the Conversion log pane


    During the conversion process the tool will:

    1. Create a backup of the entire solution.
    2. Update all class library projects (including Test projects) that reference System.Web.Mvc.dll to reference version 3.0.
    3. Update all Web Application projects that reference System.Web.Mvc to reference version 3.0 and add references to System.Web.Helpers.dll and System.Web.WebPages.dll.
    4. Change all MVC 2 Web Application projects to be MVC 3 projects (this affects which version of the 'Add View' dialog you get etc) 
    5. Update all MVC Web Application root Web.config files:
      1. Update references to System.Web.Mvc version 3.0.
      2. Add an assembly binding redirect entry for System.Web.Mvc.
      3. Add assembly and namespace references to System.Web.Helpers and System.Web.WebPages.
      4. Add the “ClientValidationEnabled” and “UnobtrusiveJavaScriptEnabled” settings to the <appSettings> element with value “false” if they were not already present (these settings can be used to control the unobtrusive client validation and unobtrusive Ajax features).
    6. Update all MVC Web Application views Web.config files (~\Views\Web.config as well as the views Web.config files for all Areas):
      1. Update references to System.Web.Mvc version 3.0
      2. Add a <configSections> entry required for Razor configuration in MVC
      3. Add the <system.web.webPages.razor> entry required for Razor support in MVC
      4. Add the “webPages:enabled” setting to the <appSettings> element with value “false” (required to prevent .cshtml or .vbhtml files in the Views folder from being directly accessible from a web browser)
    7. Add the following JavaScript libraries to the MVC Web Applications (only if they did not already exist):
      • jQuery 1.4.4 (jquery-1.4.4.js, jquery-1.4.4-vsdoc.js, jquery-1.4.4.min.js)
      • jQuery UI 1.8.7 (jquery-ui.js, jquery-ui.min.js)
      • jQuery Validation 1.7 (jquery-validate.js, jquery-validate-vsdoc.js, jquery-validate.min.js)
      • Microsoft Ajax (MicrosoftAjax.js, Microsoft.Ajax.debug.js)
    8. Overwrite MVC-specific JavaScript files in MVC Web Applications:
      • MicrosoftMvcAjax.js, MicrosoftMvcAjax.debug.js, MicrosoftMvcValidation.js, MicrosoftMvcValidation.debug.js, jquery.unobtrusive-ajax.js, jquery.unobtrusive-ajax.min.js, jquery.validate.unobtrusive.js, jquery.validate.unobtrusive.min.js
    9. Add jQuery UI theme files (stylesheet and images)


    This is an unsupported utility and there is a possibility that it might not work correctly for your solution. Specifically, the tool has the following limitations:

    • Read-only project files are not supported (make sure you check out your solution if you use source control)
    • Very long file paths might cause problems
    • Solutions with project files outside of the solution folder might cause problems
    • The tool will not upgrade your application code to account for any potential runtime breaking changes
    • Non-standard project files (e.g. missing MVC project type GUID) will not be handled correctly
    • Once again, VS 2008 solutions, MVC 1 or .NET 3.5 projects are not supported
    • The tool will also not modify any Web Sites in your solution


    However, if you run into problems let me know and I will see if we can get them addressed.

  • Marcin On ASP.NET

    Adding custom metadata providers in ASP.NET 3.5 Extensions Preview

    • 12 Comments

    5/22/08 Note: this post talks about an outdated ASP.NET release. Please have a look at this updated dynamic metadata provider sample.

    One of the most powerful elements of the new ASP.NET Dynamic Data feature we are working on is the ability to associate metadata (for example, range validation or formatting) with your data model. You can then query the model and use the provided information to customize the processing of your data. In the recently released ASP.NET 3.5 Extensions Preview this annotation is done by declaring special metadata attributes on the partial classes representing the entities in the model. The initial implementation is a bit limited, however, because the metadata association can only be done using CLR attributes and those attributes have to be declared on the class (even if they pertain to the class's properties):

    [Range("Quantity", 0, 10000)] 
    [Range("Price", 0, 20000)]
    partial class Product {
    }

    A better approach

    The MVC Toolkit (which incorporates a number of modifications to the Preview that will be included in later milestones – download here) contains an alternative implementation. This implementation has the following improvements:

    • A pluggable metadata model based on providers.
    • New metadata attributes that can be declared on the properties of a special metadata "buddy" class.
    • An alternative reference provider implementation based on XML files.

    The best way to familiarize yourself with the new model is to look at the samples included with the MVC toolkit. However, here are a few points to get you started:

    1. The registration of a metadata handler is done on a DataContext type using the MetadataHandlerRegistration class. This needs to be done only once per application, for example in the Global.asax file:
      protected void Application_Start(object sender, EventArgs e) {
          MetadataHandlerRegistration.Register(
      typeof(MyDataContext),
      MetadataHandlerRegistration.XmlMetadataProvider); }
      Note: Only Linq to SQL models are supported in this version. Future releases will support both Linq to SQL and ADO.NET Entities.
    2. MetadataHandlerRegistration contains references to 2 different metadata providers: buddy class-based and XML file-based.

    Buddy class metadata

    Buddy class metadata is a model in which you associate a "buddy class" with your actual data model class and declare the metadata attributes on properties of the buddy class. "Why do I need to declare a parallel class?", you ask. The simple answer is that the current versions of C# and VB have no concept of partial properties.

    This implementation supports two ways of associating the buddy class:

    1. Explicit: using an attribute on the model partial class that points to the type of the buddy class
    2. Implicit: using a name matching pattern (append "_Metadata" to the name of the model class and look for a class with that new name in the current namespace/assembly)

    Using the explicit option looks like this:

    [MetadataClass(typeof(Product_Metadata))]
    partial class Product {
    }
    
    public class Product_Metadata {
        [Range(0, 10000)]
        public int Quantity { get; set; }
    }

    Comment out the MetadataClass attribute and you get the implicit option.

    XML file metadata

    An alternative metadata provider uses an XML file as the metadata store. Currently the file location is hard-coded to be ~/metadata.xml. A very basic metadata.xml file could look like this (a slightly bigger one is included in the sample blog project of the MVC Toolkit):

    <metadata xmlns="http://tempuri.org/metadataFile"
        xmlns:m="http://tempuri.org/metadataElements"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://tempuri.org/metadataFile ../Schemas/MetadataFile.xsd
    http://tempuri.org/metadataElements ../Schemas/MetadataElements.xsd"> <metadataAttributes> <metadataAttribute tagPrefix="m" assembly="MVCToolkit" namespace="Microsoft.Web.DynamicData.Metadata"/> </metadataAttributes> <tables> <table name="Product"> <columns> <column name="Quantity"> <m:Range Minimum="0" Maximum="100000"/> </column> </columns> </table> </tables> </metadata>

    The metadataAttributes section defines metadata attribute mappings. These are used by the logic that creates a CLR attribute type instance from an XML element. For example, the m:Range element will be converted into the class Microsoft.Web.DynamicData.Metadata.RangeAttribute in the MVCToolkit assembly. The attributes on the element are automatically converted and set on the properties of the attribute type instance. You can add your own metadata attribute collection by defining a new metadataAttribute entry.

    The tables section allows you to define metadata on all of the tables and their columns that make up your model.

    As a final note, most of the attributes on the root metadata element are there to support IntelliSense in your XML editor. The relative URLs work in the sample Dynamic Data MVC blog project. You might have to change some of the paths if you decide to move the XSD files around or if you create a new project in a different location.

    Implementation details

    The underlying implementation uses the TypeDescriptor mechanism from System.ComponentModel. It is basically a pluggable layer on top of standard reflection. Have a look at the code to learn more.

    Conclusion

    This post discusses elements that will be incorporated in some way or another in future releases of ASP.NET Extensions. However, some of the implementation details might change. We will certainly have detailed documentation for the final release.

    In the meantime feel free to explore the provided code and make sure you give back any feedback you might have on how these features can be improved. You can post your comments/questions at:

    ASP.NET 3.5 Extensions Preview discussion
    ASP.NET Dynamic Data discussion

    Additional resources

    Visit these blogs for additional tips on ASP.NET 3.5 Extensions and Dynamic Data:

    David Ebbo's ASP.NET blog
    Scott Hunter: ASP.NET and .NET Musings

  • Marcin On ASP.NET

    Razor, Nested Layouts and Redefined Sections

    • 10 Comments

    In a recent post I introduced a technique for dealing with optional Razor sections and default content. In this post I will expand upon that technique and describe a way of working with sections across nested layout hierarchies. If you are not familiar with sections, layout pages, or my technique then go ahead and read that post to catch up.

    One aspect of the relationship between layout pages and sections in Razor that a fair number of people might find surprising is that a section defined in a content page can only be used in its immediate layout. There is implicit scoping going on that prevents certain use cases. Take the following example:

    <!DOCTYPE html>
    <html>
    <body>
    @RenderSection("SubSection")
    @RenderBody()
    </body>
    </html>
    @{
        Layout = "~/Views/_MasterLayout.cshtml";
    }
    <div>
    @section SubSection {
    <h1>Title</h1>
    }
    @RenderBody()
    @RenderSection("ContentSection")
    </div>
    @{
        Layout = "~/Views/_SubLayout.cshtml";
    }
    <div>
    <p>Main content</p>
    @section ContentSection {
    <div>Footer</div>
    }
    </div>

    In the above example you can certainly call RenderSection("SubSection") in _MasterLayout.cshtml, as well as call RenderSection("ContentSection") in _SubLayout.cshtml. However, it is impossible to call RenderSection("ContentSection") from _MasterLayout.cshtml because the file rendering the section and the file defining the section are not directly related. Essentially sections are limited to the Content-Layout scope and are not accessible to other layout pages outside of that scope.

    Redefining sections

    You can work around this by essentially redefining the section in the intermediate layout. 

    @{
        Layout = "~/Views/_MasterLayout.cshtml";
    }
    <div>
    @section SubSection {
    <h1>Title</h1>
    }
    @RenderBody()
    @section ContentSection {
      @RenderSection("ContentSection", required: false)
    }
    </div>

    Now you are able to reference “ContentSection” from _MasterLayout.cshtml. However you should be aware that you are not overriding “ContentSection" in the same sense as overriding methods in a child class. You are actually defining a new section named “ContentSection” in the SubLayout-MasterLayout scope that renders the section named “ContentSection” from the Content-SubLayout scope. The fact that the names match is incidental. The names certainly do not have to match.

    Things get even more complicated when you want to use the IsSectionDefined method to conditionally provide default content for optional sections. Because it’s necessary to propagate “ContentSection” by redefining the section in _SubLayout.cshtml you can no longer depend on IsSectionDefined returning the expected result.

    Conditionally redefining sections via RedefineSection

    Fortunately not everything is lost. What you want to do is to conditionally redefine a section. Building on the RenderSection helper method from my previous post I created the RedefineSection more helper methods to aid in this scenario:

    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);
            }
        }
    
        public static HelperResult RedefineSection(this WebPageBase page,
                                string sectionName) {
            return RedefineSection(page, sectionName, defaultContent: null);
        }
    
        public static HelperResult RedefineSection(this WebPageBase page,
                                string sectionName,
                                Func<object, HelperResult> defaultContent) {
            if (page.IsSectionDefined(sectionName)) {
                page.DefineSection(sectionName,
                                   () => page.Write(page.RenderSection(sectionName)));
            }
            else if (defaultContent != null) {
                page.DefineSection(sectionName,
                                   () => page.Write(defaultContent(_o)));
            }
            return new HelperResult(_ => { });
        }
    }

    The RedefineSection method conditionally redefines a section (that is it redefines in only if a section was already defined in a content page). The second overload also allows you to provide a default content template that will be used if the content page did not define the section. Using this code you can write the following pages:

    <!DOCTYPE html>
    <html>
    <body>
    @RenderSection("TitleSection", required: false)
    @RenderBody()
    </body>
    </html>
    @{
        Layout = "~/Views/_MasterLayout.cshtml";
    }
    <div>
    @this.RedefineSection("TitleSection",
                          @<h1>Default SubLayout title</h1>)
    @RenderBody()
    </div>
    @{
        Layout = "~/Views/_SubLayout.cshtml";
    }
    @section TitleContent {
    <h1>Title</h1>
    }
    <p>Main content</p>

    In the above example Content.cshtml defines the “TitleContent” section. _SubLayout.cshtml redefines that section but also provides some default markup in case the content page does not have the “TitleContent” section defined. Finally, _MasterLayout.cshtml consumes the section indicating that it is optional – this means that the entire site will still work even if the content page does not define the section and the intermediate layout does not provide a default value.

    Hope you find the above technique useful in your complex layout pages. Please let me know if you encounter any issues and whether these methods are valuable enough that they should be added to the framework for the next version.

  • Marcin On ASP.NET

    ASP.NET MVC 3 Performance – on par with MVC 2

    • 9 Comments

    Now that the final version of MVC 3 has been released I think information about the performance characteristics of this latest installment would be very useful for those weighing the pros and cons of updating their MVC 2 applications. In this post I will talk about what you might expect when moving to MVC 3. The short answer is things should be about the same.

    Of course that short statement is not accurate because the only correct answer to such performance questions is: “it depends”. It depends on what your application does and which features it is using: database connections, business logic, output caching, html helpers, etc. I say this because in a moment I will present some numbers and they might not correspond to what you would see in your application. But they should provide you with a good starting point.

    The more detailed answer is that some applications will perform better while others will perform worse. That is because as we added new features (dependency injection, improved filters, etc.) some components of MVC 3 got slower than in MVC 2 simply because they are now doing more work. At the same time we have introduced targeted performance optimizations which improved other components. The only definite way you can know the impact on your application is if you carefully measure performance yourself. At the end of this post I will provide a rough list of some of the things that have gotten faster or slower.

    The Setup

    The application I will use for the tests is the default “Internet Application” project template that ships with every installation of MVC. While quite small, it is still a pretty good choice because it uses a wide variety of MVC features. This means that the test exercises a large part of the framework without focusing on any individual features.

    For my tests I have the following applications:

    1. MVC 2 Aspx Internet Application project running against MVC 2 runtime
    2. MVC 2 Aspx Internet Application project running against MVC 3 runtime
    3. MVC 3 Aspx Internet Application project running against MVC 3 runtime
    4. MVC 3 Razor Internet Application project running against MVC 3 runtime

    I modified each of these applications in the following ways to ensure consistency and remove unnecessary noise from the results:

    • Set debug=”false” in the root Web.config file.
    • Changed application to build in Release mode.
    • Changed the FormsAuthenticationService and MembershipService classes to return hardcoded values (I do not want to test database connection latency).

    For each of the applications I executed the following sequence of requests and recorded the throughput (number of requests per second). The sequence ran for about 1 hour in our performance testing lab (meaning that the results should be amortized with respect to any other OS tasks that might have occurred on the server).

    1. GET /Home/Index (arrive at site)
    2. GET /Account/LogOn (click on “log on” link)
    3. POST /Account/LogOn (attempt to log on with invalid credentials)
    4. GET /Account/Register (click on “register” link)
    5. POST /Account/Register (submit the registration form)

    The Results

    ApplicationRequests/second (higher is better)% change (wrt baseline)
    1. MVC 2 Aspx on v2 8135 (baseline) n/a
    2. MVC 2 Aspx on v3 7987 -1.82%
    3. MVC 3 Aspx on v3 7457 -8.33%
    4. MVC 3 Razor on v3 7136 -12.28%

    Note: The above results were captured on a 12-core server and should only be used for comparative purposes. Absolute RPS values on your own machine might de vastly different but their relative distribution should remain the same. 

    Applications 1 vs. 2

    As you can see from comparing the results for Application 1 and Application 2 the difference is quite small. In fact –1.82% falls within the margin of error. Application 1 and Application 2 are the same identical application, except 1. references System.Web.Mvc.dll version 2.0 and 2. references System.Web.Mvc.dll version 3.0. These results are why we say that MVC 3 is on par with the performance characteristics of MVC 2.

    Of course followers of Scott Guthrie will point out that with the RC2 release he claimed that MVC 3 was faster than MVC 2. While that was true at the time it is not correct any longer for RTM. We had to undo a pretty significant performance fix related to model metadata caching because it caused some functional bugs (after all it does not matter how fast your application is if it does not actually work correctly). However, we will have that improvement available as an add-on in MVC Futures and we will roll it into MVC 4.

    Applications 2 vs. 3

    Comparing Application 2 with Application 3 shows that 3. is –6.64% slower. However, in this case the runtimes are the same. The difference is in the project templates used. The project template that ships with MVC 3 has more functionality enabled by default. Most notably it has unobtrusive client-side validation enabled; MVC 2 project templates did not have client-side validation enabled. The extra time is taken up inspecting the model metadata and emitting additional markup and javascript code. Of course while page rendering will be slower, the overall result of enabling client-side validation should be reduced load on the server as the majority of invalid inputs will be caught on the client thus avoiding the need for unnecessary roundtrips.

    Applications 3 vs. 4

    The comparison of Application 3 and Application 4 will be of interest to those looking at adopting the new Razor view engine. Application 4 is –4.3% slower than Application 3. This shows that using Razor does have a performance cost. However, that difference is not that big and is outweighed by the elegance and increased readability of the new view engine. Razor is also a new technology and I am sure that as it matures we will be able to optimize it even further.

    The Changes

    At the beginning of this post I promised that I would list how the various features or components of MVC changed in their performance characteristics. This list is compiled from our internal performance tests as well as change logs. It is meant to be taken as informational only. Your results may vary and depends on how you actually use each feature and what else you have happening in your application

    Things that got faster (in no particular order):

    • Expression caching (internal subsystem that helps in the performance of methods that accept lambdas)
    • URL generation
    • Action lookup
    • Filter invocation
    • TagBuilder

    Things that got slower (also in no particular order):

    • Things for which Dependency Injection entry points have been exposed
    • Auto-generated editor and display helpers
    • Model binding

    Of course I am not going to tell you by how much things have changed because it depends... you will have to measure yourself.

  • Marcin On ASP.NET

    ASP.NET MVC Performance Tips

    • 9 Comments

    Database access is usually the largest performance bottleneck of web applications. But once you’ve optimized and cached your database queries as much as possible here are 3 quick tips that might help you squeeze out a few more requests out of your ASP.NET MVC applications and web servers.

    Depending on which features you are using they might not always apply to your application and their benefit will not be that significant (a few % points improvement at most - sorry I don't have more specific numbers as I'm writing this from memory). I can't guarantee how much improvement you will see as it depends so much on what your application does. But I hope this will provide some leads for you to investigate. Just remember to always measure for your specific scenario.

    Disable unused view engines

    When: Your application is using built-in display/editor templates (via the Html.EditorFor or Html.DisplayFor methods) and you are using only one type of view engine.

    Why: ASP.NET MVC is generally very good about caching file lookup hits (views, partials, display/editor templates). However one scenario that is not cached is the use of the built-in (default) display/editor templates (where all file lookups are misses).

    Any time you use the Html.EditorFor or Html.DisplayFor methods and you do not have a matching template file MVC will generate the appropriate markup for you based on the model’s metadata. However, before that happens it checks all the view engines to find a template file. The more view engines you have registered the longer this will take and since the miss is not cached the process will have to be repeated the next time a page is requested. If you are using only one view engine in your project you should remove the other ones in Global.asax. The easies way to do that is with the following code:

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new RazorViewEngine());

    Avoid passing null models to views

    When: You pass in a null model to a view that uses strongly-typed html helpers (such as Html.TextBoxFor). This frequently happens in Insert scenarios.

    Why: Strongly-typed html helpers such as Html.TextBoxFor(m => m.Name) will try to emit the value of the model using the provided expression. However when something along the expression chain is null a NullReferenceException will be thrown when the expression gets evaluated. MVC’s expression evaluator catches the exception but on a page with multiple such html helpers the cost of the exception adds up. You can avoid that cost by always passing an instance of the model to the view:

    public ActionResult Insert() {
        // return View(); // here the model instance defaults to null
        return View(new Product());
    }

    Uninstall URL Rewrite if you don’t use it

    When: Your IIS server has the URL Rewrite module installed but none of the applications on the server are using it and you are running ASP.NET MVC 3.

    Why: When performing URL generation (for example via a method like Html.ActionLink) in some cases MVC checks to see if the currently requested URL has been rewritten by the URL Rewrite module. If that is the case the result is processed so that it correctly matches the URL requested by the client.

    The act of checking if a URL has been rewritten has a non-trivial cost (because it involves checking server variables). ASP.NET MVC 3 checks to see if URL Rewrite is turned off and can cache that fact thus avoiding the need to inspect server variables for each request. If URL Rewrite is turned on MVC will have to check server variables even if no rewriting happened for a particular request so if you are not using URL Rewrite you should turn it off (Note that MVC 2 always performs this check so turning URL Rewrite off will not make a difference).

Page 1 of 5 (25 items) 12345