ASP.NET MVC Tip #49 – Use the LinqBinaryModelBinder in your Edit Actions

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

clip_image002

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

clip_image004

(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

clip_image006

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 – ViewsHomeEdit.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

clip_image008

Discussion

  1. Goran Gagic says:

    You could also work with an int Version column.
    Al you have to do then is create a trigger in sql:
    Update t set t.[Version] = t.[Version] + 1
    FROM Products.Product as t INNER JOIN Inserted ON t.ProductId= Inserted.ProductId

    After that you don’t need to base64 encode your hidden field and you don’t need to use a binder from the future assembly.

    But there is a security problem with both our solutions. Somebody could fake our version field and effectivly force a last one wins even if we don’t want this behaviour.

  2. @Goran – Nice idea about using a custom integer version column.

  3. cigarettes says:

    Dear friend We can offer all kinds of cigarettes. We offer safe delivery and best service,low price and good quality.
    Communicate with us.You will be very satisfied! Hope we can do long-term business in near future.
    Welcome to our website for more details
    http://www.cigarettes-home.com
    Best regards! Thank you !

  4. Hello,welcome my blog. my blog is about wholesale watches,such as Day Date watches and Daytona watches. could you give me some suggestion? i

    shold thank you very much.

  5. Nice information! I will use this information in my application. Thanks.

  6. Blue Eyes says:

    very nice post.. thanks!!!

  7. Thanks! I Rally needed this one.

  8. Somebody could fake our version field and effective force a last one wins even if we don’t want this behavior.Hope we can do long-term business in near future.

  9. vijay says:

    Thank you very much.

  10. seotips says:

    Nice information!

  11. linkexchnage says:

    This information is useful. Thanks.

  12. raj-seo says:

    Very nice post

  13. raj-seo says:

    Thanks you for info.

  14. raj-sem says:

    Thanks you for info.

  15. shiva-seo says:

    Rally needed this one.

  16. bookmarking says:

    This information is useful.

  17. blog-seo says:

    Very nice INFO post.

  18. key-phrases says:

    Nice, informative post.

  19. website says:

    Thanks you for share this info.

  20. semantics says:

    Rally nice info this one.

  21. designe says:

    Thanks for providing these four useful.

  22. googleseo says:

    very useful in any application.

  23. adsense says:

    Thanks you for sharing.

  24. seo-sem-tips says:

    best tool for the job.

  25. Great Stuff. I totally agreed with you.

  26. education says:

    Great Post! very useful in any application.

  27. will says:

    great script

  28. fre w3 Thanks you for sharing.

  29. staje says:

    we supply jewelry rings
    jewelry necklace
    earring jewelry
    amber jewelry
    pearl jewelry
    crystal jewelryon our site china-jewelry-supplier.com welcome

    and we wholesale 925 silvery|
    sterling silver|
    silver crystal| at silver-jewelry-wholesaler.com

    another we have jewellery wholesale|
    fashion jewellery|
    silver jewellery| at jewellerywholesales.com

    sterling silver 925|
    silver 925|

  30. kein says:

    have asked if you need one to go from New York to Chicago. And, with the new law, I can give a nice short answer to questions about what you need to cross the border. You need a passport.prior learning | High School

  31. Great Post! very useful in any application. homeschool online | get diploma

  32. Keep posting!!! very usefull info!!!
    design-portfolio

  33. altdesire says:

    usefull info, thanks alot!!
    Altdesire terms

  34. Coincido con las apreciaciones de mis compa?eros Ariel, Marcelo, José, Eduardo: ?Qué premios tan pobres para una labor que implica una dedicación a pleno!rolex replica| Ana María además explica lo difícil que nos resulta innovar con tecnología nueva obligados a “perfeccionarnos” con rolex replicas|cursos desactualizados. Diariamente damos todo de nosotros a cambio de abrir las mentes de quienes se educan. Ense?amos el valor de la DIGNIDAD.cheap watches| Y también ense?amos que esa DIGNIDAD nos compromete a NO MENDIGAR. Que nuestro trabajo VALE y no es sentirnos “el último orejón del tarro”.replica rolex| Aceptar ese “premio” sería borrar con el codo lo que hemos escrito con la mano. Echaría por tierra las aspiraciones de nuestros alumnos.
    Esteban, me ha gustado mucho tu ponencia. Es muy clara, toca todos los temas claves, – o eso creo 🙂 – y además es entretenida. replica watches|, Para que una chica tan simpática como Celina se anime a escribir algo chulo ha debido verdiscount watches|, Leo prácticamente todo lo que escribís y subís a estas páginas ?cheap handbags|y lo cierto es que me parece que el material tiene mucha calidad!burberry handbags| Y no sólo hablo de conocimientos teóricos, sino también de las prácticas que analizáis y comentáis.
    Como ya digo que me ha gustado mucho, cheap louis vuitton| he preparado otra versión – también en .pdf – donde se remarcan en color (rojo-violeta) los puntos que, personalmente, me parecen más importantes.marc jacobs handbags|, Pero, ejem… ?no sé cómo subirlos a esta página como archivos adjuntos! Si el moderador – Carlos, saludos – lo permitiera, claro está 🙂prada handbags| Por lo demás, intentaré colaborar en lo que me sea factible con vds., cheap rolex watches| como hace tiempo le comentara a Carlos… pero no me vendría mal que un alma caritativa me dijera cómo se suben esos archivos jeje cheap watch|, quisiera contar con el vídeo de historietas que ya fue pasado donde hacían una línea histórica de la misma gracias.cheap rolex|Coincido con las apreciaciones de mis compa?eros Ariel,tag heuer replica| Marcelo, José, Eduardo: ?Qué premios tan pobres para una labor que implica una dedicación a pleno!louis vuitton handbags| Ana María además explica lo difícil que nos resulta innovar con tecnología nueva obligados a “perfeccionarnos” con cursos desactualizados.gucci handbags| Diariamente damos todo de nosotros a cambio de abrir las mentes de quienes se educan.iwc watches| Aceptar ese “premio” sería borrar con el codo lo que hemos escrito con la mano.

  35. elite says:

    Thank you very much for this information. Good post thanks for sharing. I like this site

    remapsremaps – remappingremapping – engine remapengine remap –
    chip tuningChip tuning – ecu remapecu remap – remappedremapped – remapremap

  36. bilgi says:

    Thank you very much for this information. Good post thanks for sharing. I like this site

    pırlanta tektaşpırlanta tektaş
    gümüş yüzükgümüş yüzük
    asfalt ankaraasfalt ankara
    kale kapıkale kapı
    kale kapıkale kapı
    serigrafi baskıserigrafi baskı
    ilke web tasarımweb tasarım
    gümüş takıgümüş takı
    bayan çantasıbayan çantası
    elektronik notebookelektronik notebook
    Gaban Kale KapıGaban Kale Kapı
    Evil eyeEvil eye

  37. bilgi says:

    Thank you very much for this information. Good post thanks for sharing. I like this site

    Nazar boncuğuNazar boncuğu
    FermuarFermuar
    Araç MuayenesiAraç Muayenesi
    Böcek İlaçlamaBöcek İlaçlama
    Haşere İlaçlamaHaşere İlaçlama
    sac kaynaksac kaynak
    sac kaynağısaç kaynağı
    postişpostiş
    civatacivata
    Somunsomun
    bayrakbayrak
    türk bayrağıtürk bayrağı
    yabancı bayrakyabancı bayrak
    kale çelik kapıkale çelik kapı

  38. Sharon says:

    Thanks, this post is very helpful!
    discount codes | promotional codes are more important than ever to people trying to save money on their shopping online.Such as Asda Discount Codes | Tesco Voucher Codes | Argos Discount Codes | Comet Discount Codes all the UK retailers seem to be gearing up to offer us big discounts, offers and incentives to get on and shop. Diapers Coupon Codes|Newegg promo codes and Musicians Friend Coupon Codes and Kohls Coupon Codes and Sears Coupon Codes A minute or two online can save you big money.

  39. free games says:

    Great post thanks!

  40. 4 It’s lucky to know this, if it is really true. Companies tend not to realize when they create security holes from day-to-day operation.

  41. wow gold says:

    October 7 news today, a rocket was finally ushered in the new seasons first pre-season games, in the long off-season period, although this is only preseason, but still attracted a lot of fan’s attention.wow gold The Rockets and Spurs away game lead all the way, and ultimately more than 84 defeated 99 opponents to the new season, be made a good start. Carl Landry scored 19 points and seven rebounds,World of Warcraft Gold Chase Budinger scored 15 points and Luis Scola had 13 points; while the Spurs also have several new impressive performance.
    The new season, the Rockets will suffer from a few well-known problem, wow power levelingalong with Yao Ming and Tracy McGrady’s absence, the Rockets need to find a new boss to lead the team. In fact, the leader in the NBA, wow power levelingand not just on the floor play as simple as that, how to inspire the morale of players in the locker room in the proper co-ordination work, these are a team leader should do. World of Warcraft GoldWhether youre disgusted with the team there is the boss Rockets now urgent need for such a player to come forward.
    A preseason game, of course, does not enable the new season our team rocket boss definitive. However, wow goldat least from today’s game, we can see some clues. Who may eventually also become the most powerful rocket boss new season, he became a special wheat-free season without Yao, the Rockets team leaders do?10.08C

  42. bar stools says:

    I use it also when I am editing and it is my favorite

  43. Thank you very much for this information. Good post thanks for sharing. I like this site