Ajax Form and Button’s Value

I encountered a bug similar to the one posted on stackoverflow.com while working on my latest pet project. I figured out an elegant workaround and I was writing the post when I found out that it might have not been a bug after all.

The HTML of the page where the bug manifested didn’t validate. I fixed it. And the bug disappeared.

The bug occurred when we submitted a form asynchronously using Ajax.BeginForm(). When the form was submitted, the value of button clicked was not submitted along with other input fields’ values. But turned out, based on my observation, the “bug” only manifested when the HTML of the page didn’t validate.

The following is an example to show that the value of button clicked is submitted as expected when a form is submitted asynchronously.

Consider this form where a site visitor can vote for or against a subject. The subject to vote on is the statement “I love ASP.NET MVC!” and a site visitor can vote for or against the statement by clicking the + or - button respectively.

Voting Page

The code for the view (say, Vote.aspx) looks like the following.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Vote</title>
</head>
<body>

<%using (Ajax.BeginForm("Vote", "Voting", new AjaxOptions { UpdateTargetId = "message" }))
  { %>
    <%= Html.Hidden("itemId", "1")%>
    <p>I love ASP.NET MVC!</p>

    <input type="submit" name="voteValue" value="+" />
    <input type="submit" name="voteValue" value="-" />
<%} %>

<p id="message"><%= TempData["message"] %></p>

<script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftAjax.js")%>"></script>
<script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.js")%>"></script>

</body>
</html>

The code for the controller (say, VotingController.cs) is the following.

using System.Web.Mvc;

namespace Examples.FormWithMultipleSubmitButtons.Controllers
{
    public class VotingController : Controller
    {
        public ViewResult Vote()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Vote(int itemId, string voteValue)
        {
            switch(voteValue)
            {
                case "+":
                    TempData["message"] = "You voted up.";
                    break;
                case "-":
                    TempData["message"] = "You voted down.";
                    break;
                default:
                    TempData["message"] = "Your vote was not recognized.";
                    break;
            }

            if(Request.IsAjaxRequest())
            {
                return Content(TempData["message"].ToString());
            }
            else
            {
                return View();
            }
        }
    }
}

When the site visitor clicks the + button, the form will be submitted asynchronously and the message “You voted up.” will appear as expected. Likewise, when the - button is clicked, the message “You voted down.” will appear.

Voting Up Message

Voting Down Message

Using Your Physical, Full-size Keyboard with Windows Phone 7 Emulator

When you click your mouse on one of the input fields in a form, you will get the onscreen keyboard on Windows Phone 7 emulator. It’s worse than using your thumbs on your real phone because you actually has to click one character at a time with your mouse.

Now, while you still can see the onscreen keyboard, press the [Pause/Break] key (to the right of [Scroll Lock] key, at least on my keyboard–my computer keyboard). If you do it right, the onscreen will disappear and you can do your form entry with your physical, full-size keyboard.

Verbose WHERE Clause with LINQ to SQL

Table Persons

If you come from the world of always-use-stored-procedures or at least you always write your own SQL query, you’d be curious how LINQ to SQL translates your C# code to SQL query. Say you have table Persons with fields PersonId, FirstName, LastName, and Age. PersonId is the primary key of the table Persons.

In this example, we want to update a particular row (PersonId = 1) with a new Age value. So, we write the following block of code in our application.

Entities.DataContext context = new Entities.DataContext();

int personId = 1;
Entities.Person person =
    context.Persons.SingleOrDefault(p => p.PersonId == personId);
person.Age = 24;
context.SubmitChanges();

To see the generated SQL query, we want to redirect the log of DataContext object to console.

Entities.DataContext context = new Entities.DataContext();
context.Log = Console.Out;

int personId = 1;
Entities.Person person =
    context.Persons.SingleOrDefault(p => p.PersonId == personId);
person.Age = 24;
context.SubmitChanges();

Now, when we run the application, you will see the SQL queries generated by your code in console window. The SQL query will look something similar to the following.

UPDATE [dbo].[Persons]
SET [Age] = @p4
WHERE ([PersonId] = @p0) AND ([FirstName] = @p1) AND ([LastName] = @p2) AND ([Age] = @p3)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [John]
-- @p2: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Smith]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [23]
-- @p4: Input Int (Size = 0; Prec = 0; Scale = 0) [24]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.4926

If you’re used to writing your own query, you probably wonder why the WHERE clause is verbose (or wordy, an English major will say). You see the query tries to find the row to update by matching every single field in the table with their old values. “Well, that’s dumb, ain’t it?” you say. “We can find the row to update by matching only the value of the primary key, PersonId.” Here’s how.

Properties Window

  1. Go to your Solution Explorer in Visual Studio and double click the .dbml file.
  2. Click the property PersonId on the class diagram.
  3. Go to Properties window (it’s on bottom right on default window setup) and find the property Update Check. The current value should be Always.
  4. Change it to Never.

Repeat step 2-4 for all properties (FirstName, LastName, and Age).

Update the application to update the Age to 25 (previously 24). Rebuild and re-run the application. You will see the update statement that you probably always write.

UPDATE [dbo].[Persons]
SET [Age] = @p1
WHERE [PersonId] = @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [25]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.4926

So, what’s the purpose of the property Update Check? The property description says it all. It’s to control the frequency of optimistic concurrency checking.