ross dickinson: web and desktop software developer

In case you're using the jQuery unobtrusive ajax library with your ASP MVC3 site, you'll probably find that it can be difficult to submit a nested form in certain situations. The problem comes from this bit of code:

$("form[data-ajax=true]").live("submit", function (evt) {
    var clickInfo = $(this).data(data_click) || [];
    evt.preventDefault();
    if (!validate(this)) {
        return;
    }
    asyncRequest(this, {
        url: this.action,
        type: this.method || "GET",
        data: clickInfo.concat($(this).serializeArray())
    });
});

When your nested form's submit event gets raised, it bubbles up to the parent form as well. You can fix this in two ways. The easiest way is to modify the code above to look like this:

$("form[data-ajax=true]").live("submit", function (evt) {
    if (evt.currentTarget != evt.target) { return; }
    var clickInfo = $(this).data(data_click) || [];
    evt.preventDefault();
    if (!validate(this)) {
        return;
    }
    asyncRequest(this, {
        url: this.action,
        type: this.method || "GET",
        data: clickInfo.concat($(this).serializeArray())
    });
});

This stops the unobtrusive ajax library's handler from submitting the wrong form.

Another way is to add your own submit handler to your nested form that calls evt.stopPropagation(). This tells the event to stop bubbling up and calling other event handlers. This can be tricky, though, because you have to ensure that your handler is called before the unobtrusive handler.

I'm sure you've seen this before:

A potentially dangerous Request.Form value was detected from the client

Ugh! Annoying. So what's dangerous about the request? Could be anything, but let's break down what ASP does in the background when it actually validates the request.

1. Checks postback keys

This is almost inconsequential, but something worth pointing out. The HttpRequest class has a private method called ValidateNameValueCollection that determines whether or not a postback value should be be validated.  Oddly enough, this has nothing to do with checking whether the developer's supplied a way to bypass validation(like by using the ValidateInputAttribute in MVC). All this method does is make sure that that all of the keys are validated, unless the key begins with "__". I'm assuming this is here so fields like "__VIEWSTATE" don't break the validation.

2. Validate input

After the key's been checked, if the matching value isn't null, it's then passed to a series of a functions that check that the value

  1. Doesn't contain any text with an < followed immediately by any letter, !, ?, or / symbol. 
  2. Doesn't contain the text "&#"

That's it.

That's all it does.

I actually think catch-all validation like this is fairly useless for anyone with a little experience in web development, because trying to throw an exception when html tags are sent to the server isn't necessarily going to prevent them from being rendered back out to the client. The values should be sanitized before being sent back to the client, instead.

On an MVC3 project I'm working on, one of the things we do when allowing a user to edit a record s open up an ajax form inside a jQuery UI Dialog. This is done through a really simple setup of

  1. Call controller action.
  2. Controller action returns a PartialViewResult.
  3. Using some custom hooks into the jquery.unobtrusive-ajax library, render the partial view into a dialog.

Seems simple enough, right? This works well, except if the partial view includes script tags. By design, when using $.append, jQuery will remove all the script tags in the created element, then attempt to load them via ajax before appending the main content. This can be a problem if the script you're loading depends on elements inside the partial view's content.

Luckily, there's an easy way to get around this. When you do something like:

var newElement = $('<div><script src='somewhere'></script></div>');

jQuery doesn't return an array with one element, it actually returns with two elements!

// Assuming you have Firebug or Chrome, you can use console.debug:
console.debug(newElement.length); // This will equal 2
console.debug(newElement[0]; // This will be the main content with the script tags removed
console.debug(newElement[1]); // This will be the script tag.

So jQuery's actually nice enough to give us back everything all organized before anything happens. If you need to load the scripts after the elements have been added to the DOM, you need to manually append everything instead of just calling $.append(newElement). Here's how to do it:

// Create a parent container to hold our content. If
// you already have a parent attached to the DOM,
// you can use that instead.
var $parent = $('<div></div>');

// Create an array to hold on to our script references.
var scripts = [];

// Append any elements that aren't scripts,
$newElement.each(function() {
	var el = $(this);
	// tagName is always all caps.
	if (el.prop('tagName') === 'SCRIPT') {
		scripts.push(el);
	}
	else {
		d.append(el);
	}
});

// If your parent element isn't attached to the DOM yet, you need to do
// that now before adding the scripts, otherwise any scripts that reference
// elements from the html fragment will fail.

$(document).append($parent);

// Now we just need to append each script from our scripts array.
// jQuery does this synchronously so the scripts will still load
// in the same order they were originally in.
$(scripts).each(function(i, script) {
	d.append(script);
});

TIP: Because your new elements will be displayed before your scripts are loaded, you should consider hiding them before the scripts are appended, and then show them afterwards.

As an ASP.NET developer, most people end up at the ASP.NET Forums at one time or another. While I'm sure Microsoft wants their forums to be the be-all end-all of ASP question-and-answer sites, the truth is that the advice given is generally lacking in quality and coherency. Now, I am generalizing a bit. I primarily hang out in the Web Forms subsection, so my experience is limited to this area.

So, what's wrong with the forums?

The replies always strike me as a case of "firsties!", with other users clamoring to get their answer in first. They see their personal user point count, displayed on every single post, as something of pride. I'm all for getting your answer in first, assuming it's right. Frequently, the first several answers to a post can be blatantly wrong. Either the user didn't understand the original question to begin with, or they post code samples with error laden syntax. I get the impression that the user who wrote the answer didn't actually see if the answer they supplied would work.

There's also the issue of users who do nothing but link to other sites that "might help them". I'm all for referencing external resources, except:

  1. Far too often, and this is a problem that's global to pretty much any web forum, links become stale and no longer work. What could have been an answer for another user months/years later is now lost since the user didn't post anything but a link.
  2. The person answering should atleast summarize what the link is about. At least indicate that they understand what the information in the link was talking about. I've seen people who just haphazardly link to things without even reading it, including links to WinForms documentation(wrong part of the framework), or even links to java documentation(not even remotely useful for the question at hand, which specifically had to do with capabilities of csharp and the .NET framework).

Microsoft would really benefit from having moderators who can cleanup the painfully wrong answers from the site. Or even, instead of only allowing the original poster to mark a post as the answer, let everyone else upvote and downvote posts in terms of quality. I can't think of how often I've seen the accepted answer not actually work, but a post further along actually does.

In the mean time, I stick to StackOverflow in order to get quality responses to questions. Though, I do try to help out on the ASP.Net forums.

I was wracking my brain the other day while trying to figure out how to get an editor template to properly bind to the ViewModel when using Html.EditorFor(m => m.ModelProperty). On the clientside it seemed to visually render ok, but something was amiss. What was wrong?

  1. The rendered input tag was missing the unobtrusive validation attributes that I was expecting.
  2. The name value of the input tag wasn't "ModelProperty" like I was expecting, but "ModelProperty.ModelProperty".

So why was this happening? Apparently, when inside an editor or display template, calling methods like Html.TextBox or Html.Label with the model's property name in it causes the template to bind to the dynamic ViewModel created for that template's instance, instead of the original ViewModel that the property originally came from.

To get around this, all you need to do from your template is call Html.TextBox(""), Html.Label(""), or any of the other helper methods. Just passing in an empty string fixes the problem!

Noticed this annoying quirk today while working on a custom WebForms control. Calling EnsureID will not populate the ClientID property if the control's effective ClientIDMode is set to Static. No idea why! EnsureID will still populate the ID properties, but if you need the ClientID for client-side javascript, you'll need to find other ways around. I opted to go this route:

public override string ClientID
{
    get
    {
        EnsureID();
        if (!string.IsNullOrWhiteSpace(ClientID))
        {
            return ClientID;
        }
        if (!string.IsNullOrWhiteSpace(ID))
        {
            return ID;
        }
        throw new InvalidOperationException("Where's the ClientID for this DateTimePicker?");
    }
}

If you're looking to reduce the amount of sql calls your ASP WebForms page uses, you may wanna check that you're not calling the Controls property on any class that inherits from CompositeDataBoundControl. The Controls getter does the following:

  1. Calls EnsureChildControls.
  2. Calls CreateChildControls.
  3. Calls EnsureDataBound.
  4. Performs whatever data binding needs to occur.

This can eat up resources if you have a data source that's returning hundreds of thousands of rows prior to setting limiting parameters!

Just a quick note. ASP validators(like RequiredFieldValidator) do not work when validator's ClientIDMode is set to a value other than AutoID. This happens either by explicitly setting a value, or the validator inheriting it from a parent control. The only work around, so far, is to make sure your validator has AutoID set.

I spent this afternoon working on a custom VirtualPathProvider for two websites we have at work. We need to share resources(master pages, themes, scripts, etc) between the two and I figured embedding resources in a separate project would be the best bet. The VirtualPathProvider implementation takes any old path its given(like "/resources/style.css") and looks to see if there's an embedded resource that matches. This worked out great after I got around some annoying implementation details(MSDN documentation on VirtualPathProvider is kind of terrible).

Then I got stumped. On some pages everything would work as expected. The page would include all the referenced embedded resources and load without a hitch. On other pages, though, the same reference would result in an "Directory not found" HttpException for no apparent reason. After tearing my hair out trying to find something wrong with my VirtualPathProvider, I went to look at the full stacktrace of what was wrong. I probably should have done that in the first place seeing as my problem wasn't specifically being thrown by my VirtualPathProvider class.

[HttpException (0x80070003): Directory 'C:\Solutions\Website\Resources\Dir' does not exist. Failed to start monitoring file changes.]
System.Web.FileChangesMonitor.FindDirectoryMonitor(String dir, Boolean addIfNotFound, Boolean throwOnError)
System.Web.FileChangesMonitor.StartMonitoringPath(String alias, FileChangeEventHandler callback, FileAttributesData& fad)
System.Web.Caching.CacheDependency.Init(Boolean isPublic, String[] filenamesArg, String[] cachekeysArg, CacheDependency dependency, DateTime utcStart)
System.Web.Caching.CacheDependency..ctor(String[] filenames)
System.Web.Script.Services.WebServiceData.GetWebServiceData(HttpContext context, String virtualPath, Boolean failIfNoData, Boolean pageMethods, Boolean inlineScript)
System.Web.Script.Services.PageClientProxyGenerator.GetClientProxyScript(HttpContext context, IPage page, Boolean debug)
System.Web.UI.ScriptManager.RegisterServices()
System.Web.UI.ScriptManager.OnPagePreRenderComplete(Object sender, EventArgs e)
System.Web.UI.Page.OnPreRenderComplete(EventArgs e)
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

What's ScriptManager doing? Beats me, but I looked into it and came across this: HttpException when serving a page with a ScriptManager using a Virtual Path Provider

It turns out that, prior to ASP 4.0, the ScriptManager control has a bug that causes it to ignore any custom VirtualPathProvider classes whenever it does whatever it does internally. This only happens when the ScriptManager's EnablePageModes property is set to true. Setting this to false immediately fixed my problem. That may not solve everyone's problem, though. There's a hotfix available for this specific issue, though I can't vouch for it.

 

I discovered this bug/feature today. While working on a new page for work, I was having an issue where none of the child controls inside an ascx user control were instantiating during runtime, leading to all sorts of null reference exceptions. I wasn't quite sure why, as the custom control worked fine on every other page.

After a bit of tooling around, I found out that the problem was in how I registered the control at the top of the page. The problem was here:

<%@ Register Assembly="CompanyName" Namespace="CompanyName.Controls" TagPrefix="companyname" %>

I was trying to register the project's controls namespace so that I didn't have to register each control that I used individually. Visual Studio didn't complain, nor did the compiler. I was able to put in any of the types and it worked out fine. The big mystery is that, for reasons unknown to me, the user controls would instantiate on the page normally, but none of their child controls would. 

Switching the register back to

<%@ Register Src="~/Controls/UserControl.ascx" TagName="UserControl" TagPrefix="companyname" %>

And everything worked fine again.

The only thing I can figure out is that this is specific to user controls that have a designer file. External control libraries still work fine.

Update!

I finally realized why this was happening. Because the "ascx" part of a user control is a template, the template doesn't get loaded unless it's registered specifically. You can get around this by going: CustomControl.LoadTemplate("ascx path")!

About the author

rossisdead is a 26 year old web and desktop software developer from New Jersey. He has two cats and likes long walks on the beach.

On Stackoverflow

On Stackoverflow Careers

On Codeplex