I ran into a brick wall earlier today when I was attempting to create a controller with an Edit() action. I wanted to edit database records with LINQ to SQL.
Here’s the controller action:
Listing 1 – Edit() action
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Product productToEdit)
{
try
{
_context.Products.Attach(productToEdit, true);
_context.SubmitChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
This action accepts a Product as a parameter. The Product class is a LINQ to SQL class generated by the LINQ to SQL Designer.
The Product is attached to the LINQ to SQL Data Context and the SubmitChanges() method is called to persist the changes to the database. When I first wrote this Edit() action, I got the error in Figure 1 when I ran my application.
Figure 1 – Error when editing

According to the exception, I either need to add a Version property to my Product class or I need to change the update policy.
Let’s discuss the second option first. If you don’t care about concurrency conflicts then the easiest option is to disable update checks when performing a database update. You disable update checks on a property by property basis. Open the LINQ to SQL Designer, open the property sheet for an entity, and change the value of the Update Check property to Never (see Figure 2).
Figure 2 – Changing Update Check to Never

(HINT: You can shift-click and select all of an entity’s properties in the designer at once and change the Update Check property simultaneously for all of the properties).
After you make this change, the Edit() action in Listing 1 will no longer throw an exception. When you invoke the Edit() action, the SQL UPDATE command in Listing 2 executes in the background.
Listing 2 – SQL with no concurrency checks
UPDATE [dbo].[Products]
SET [Name] = @p1, [Description] = @p2, [Price] = @p3
WHERE [Id] = @p0
If, in fact, you are concerned about concurrency then you need to add a version column to your database. That way, LINQ to SQL can determine whether or not the current version of an entity was retrieved from the database before or after changes were made to the database. The easiest way to add a version column to a table is to add a timestamp column. After you create the timestamp column, delete and add the database table back onto the LINQ to SQL Designer.
For example, the Product entity in Figure 3 includes a Version column.
Figure 3 – Entity with Version column

Now, here is the tricky part. You need to make sure that when the default model binder creates the Product class for the Edit() action that the default model binder includes the Version property. You need to include the Version property, as a hidden form field, in the Edit view. Furthermore, you need to register a custom model binder for deserializing the hidden form field.
The Edit view with the hidden form field is contained in Listing 3. The value of the Version property is converted to a string before being rendered as the value of a hidden form field.
Listing 3 – Views\Home\Edit.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<LSMvcApplication.Models.Product>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit</h2>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<%= Html.Hidden("Version", Convert.ToBase64String(Model.Version.ToArray())) %>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextBox("Description", Model.Description) %>
<%= Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Price">Price:</label>
<%= Html.TextBox("Price", String.Format("{0:F}", Model.Price)) %>
<%= Html.ValidationMessage("Price", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
When the Edit view in Listing 3 is rendered to the browser, the following hidden form field is rendered:
<input id="Version"
name="Version"
type="hidden"
value="AAAAAAAAB+Q=" />
Unfortunately, our work is still not done. The default model binder won’t deserialize the Version property because it is a System.Data.Linq.Binary property. Therefore, we need a custom model binder. Luckily, the ASP.NET MVC Futures project includes a LinqBinaryModelBinder that does exactly what we need.
You can download the ASP.NET MVC RC 1 Futures from the following address:
http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=22359
After downloading the futures, remember to add a reference to the futures assembly. Finally, register the LinqBinaryModelBinder in the Global.asax file like this:
Listing 4 – Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Data.Linq;
using Microsoft.Web.Mvc;
namespace LSMvcApplication
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(Binary), new LinqBinaryModelBinder());
}
}
}
The LinqBinaryModelBinder is registered within the Application_Start() event handler. After you register the handler, the Edit() action will work correctly. When the Edit() action is invoked, LINQ to SQL will execute a SQL UPDATE query like the following:
Listing 5 -- SQL Update with concurrency
UPDATE [dbo].[Products]
SET [Name] = @p2, [Description] = @p3, [Price] = @p4
WHERE ([Id] = @p0) AND ([Version] = @p1)
If you retrieve a Product, and someone else modifies the product in the database before you submit the edited product, then you get the exception in Figure 4. You can trap this ChangeConflictException in a try…catch block and display something more friendly to a user such as “Someone else has edited this record.”
Figure 4 – Concurrency conflict
