Controls Collection Cannot Be Modified Issue with ASP.NET MVC RC1

asp.net mvc, asp.net 0 comments suggest edit

In my last post, I announced the happy news that the Release Candidate for ASP.NET MVC is available. In this post, I say mea culpa for a known bug within this release.

This bug is a consequence of a change we made in our default template. We know have a content placeholder in the <head> section of the Site.master page.

<head runat="server">
    <asp:ContentPlaceHolder ID="head" runat="server">
        <title></title>
    </asp:ContentPlaceHolder>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>

The benefit here is that it makes it easy to specify view specific scripts, style sheets, and title tags from the specific view, like so.

<asp:Content ID="indexHead" ContentPlaceHolderID="head" runat="server">
    <title>Home Page</title>
    <style type="text/css">
        /* Some style specific to this view */
    </style>
</asp:Content>

Long story short, if the Header control (aka <head runat="server" />) doesn’t see a title tag as a direct child of itself, it renders an empty title tag in order to ensure the page has valid HTML. This results in having two title tags, the one you intended and an empty one.

We put in a fix for this whereby we modify the controls collection of the header control, but the fix itself causes a problem when you have code nuggets within the <head> section. For example, the following causes an exception.

<head runat="server">
    <script src="<%= Url.Content("~/foo.js") %>" type="text/javascript">
    </script>
    <asp:ContentPlaceHolder ID="head" runat="server">
        <title></title>
    </asp:ContentPlaceHolder>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>

The exception this causes is:

The Controls collection cannot be modified because the control contains code blocks (i.e. <% … %>).

We unfortunately didn’t find this until very recently. The current workaround is simple. Place the script tag within a normal PlaceHolder control.

<head runat="server">
    <asp:PlaceHolder runat="server" id="mainScripts">
        <script src="<%= Url.Content("~/foo.js") %>" type="text/javascript">
        </script>
    </asp:PlaceHolder>
    <asp:ContentPlaceHolder ID="head" runat="server">
        <title></title>
    </asp:ContentPlaceHolder>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>

We have one simple solution to this we are bouncing around, and are investigating alternative solutions. We apologize for any inconveniences this may cause.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

19 responses

  1. Avatar for Jamie
    Jamie January 27th, 2009

    Couldn't you just not have the runat="server" part? I took that out of my master pages and they work just fine.

  2. Avatar for Mike G.
    Mike G. January 27th, 2009

    Why is the runat="server" on the header tag?

  3. Avatar for Some guy
    Some guy January 27th, 2009

    I had come up with a work around that had been working for me so far.
    \head runat="server" visible="false" /\
    \head runat="server" \
    \title\ /%=Page.Title ?? "Default Title"%/ \/title\
    \/head\
    / are actually greater/less than, just don't know if they are going to be escaped or not...

  4. Avatar for Some guy
    Some guy January 27th, 2009

    Psh... I mean't the other one to not have a runat tag...

  5. Avatar for Michael Carr
    Michael Carr January 27th, 2009

    I made what I believe is a slight improvement to the default ContentPlaceHolder "head".
    Old default (from your code above):
    <asp:ContentPlaceHolder ID="head" runat="server">
    <title></title>
    </asp:ContentPlaceHolder>
    New default:
    <asp:ContentPlaceHolder ID="head" runat="server">
    <title><%= Page.Title %></title>
    </asp:ContentPlaceHolder>
    Then, for 99% of my views I can set the title like this:
    <%@ Page Title="My Page Title" Language="C#" ... %>
    For the minority of views that need special treatment, I can override the default by implementing a distinct content control for "head".

  6. Avatar for Michael Carr
    Michael Carr January 27th, 2009

    It results in duplicate <Title></Title> tags... foiled again!
    Can you just deactivate all the non-MVC parts of ASP.NET please?? :)

  7. Avatar for Simon
    Simon January 27th, 2009

    for anyone reading this confused about what runat="server" even means in MVC here's a useful stackoverflow post about what the consequences are of runat="server" for MVC. theres some notes in there about TITLE which may or may not be relevant anymore with the change to the template : stackoverflow.com/.../what-is-the-status-of-run...

  8. Avatar for gandjustas
    gandjustas January 27th, 2009

    This problem is solved simply by removing runat="server" attribute.
    Is this attribute necessary?

  9. Avatar for mogadanez
    mogadanez January 27th, 2009

    This issue not new for ASP.NET MVC. this was know from ASP.NET 1.1
    i was write post about it aprox year ago
    blogs.gotdotnet.ru/.../PermaLink.aspx

  10. Avatar for HB
    HB January 27th, 2009

    Apparently Url.Content("~/somepath") and Url.Action("someaction") was changed, because they no longer put a / at the start.
    Is this correct, or is there a new method to use? It's a pain having to go back and change all these methods.

  11. Avatar for freddy
    freddy January 27th, 2009

    As mogadanez said, this issue has been on web forms for quite some time. If you do have a fix, talk with the guys at the web form team, so it is fix in there as well - collaboration :)

  12. Avatar for Steve
    Steve January 27th, 2009

    Hey Phil, can you update your Areas code and blog about a new one using RC1... :)

  13. Avatar for Steve
    Steve January 28th, 2009

    By the way, all my 'UpdateModel' calls that worked perfect in Beta are throwing errors now.
    Any ideas of why this is failing on me now ?

  14. Avatar for Andr&#233;
    Andr&#233; January 28th, 2009

    I'm having the same problem as Steve.
    In beta version I use like this:
    UpdateModel(product, "Product", form);
    In RC1 it doesn't work! How to fix it?

  15. Avatar for Andre
    Andre January 28th, 2009

    Hi all,
    I've found the solution to the "UpdateModel" problem at Scott Gu's blog:
    "UpdateModel accepts a ValueProvider, and there is a ToValueProvider() method on FormCollection that you can use to create one from a FormCollection. However - in general you shouldn't need to-do this. Instead I'd recommend not specifying a ValueProvider within your Controller action and have it update using the default Request.Forms collection. For unit testing purposes, you can then set the Controller.ValueProvider property within your unit test and populate it using the ValueProvider collection class that is built into ASP.NET MVC. You can then invoke the action method within the test and the UpdateModel() call will use the ValueProvider you've populated to set the values."

  16. Avatar for Trevor
    Trevor January 29th, 2009

    Can you explain what this part of the update was meant to provide? I mean I can already have a contentplaceholder in the head section in the beta version. That works just fine for including css and js files? I'm a little confused

  17. Avatar for yazılım
    yazılım March 18th, 2009

    I do get duplicate title tags. So I just use Page.Title="asdad" i my views. It works but i have no idea if this is the right way

  18. Avatar for hugo
    hugo May 9th, 2011

    Thanks for the trick! That solved my problem.

  19. Avatar for Mahdi
    Mahdi July 14th, 2012

    It's a shame that this still exists in the latest versions of ASP.NET :(