DCSIMG
May 2008 - Posts - Doron's .NET Space

May 2008 - Posts

Using UpdatePanel to Disable GridView View State

View state. We usually hate it and love it at the same time. It's definitely a creature of comfort: having our controls automatically maintaining state is definitely a useful thing. But it can also be quite hazardous to your page performance - especially when used with a GridView that has many rows. This big-ass hidden field that gets generated is sure to make your users' round-trips quite a pain. And so the million dollar question is...

How do I disable view state and stay alive?

When you go ahead and mark that GridView of yours with EnableViewState="false" you'll notice that you now have to bind to your DataGrid's DataSource on every post-back. Otherwise, how would it know what rows to render? You have to do it, since the entire page is rendered on each and every post-back. And so you need to get those rows back from the Database even though nothing has changed (sure, you can use caching to eliminate this cost, but caching is not always easy to achieve).

But, if you use an UpdatePanel correctly, you can re-render the grid only when it changes, and still leave the view state off. Let's see how we can achieve this. Here's our page markup and code behind:

<form id="form1" runat="server">

    <asp:TextBox ID="FirstName" runat="server"/>

    <asp:TextBox ID="LastName" runat="server"/>

    <asp:Button ID="AddPerson" runat="server" Text="Add" OnClick="AddPerson_Click" />

 

    <asp:GridView runat="server" ID="PeopleGrid">

    </asp:GridView>

  </form>

public partial class PeoplePage : System.Web.UI.Page

{

    private PeopleRepository _peopleRepository =  new PeopleRepository();

 

    protected void Page_Load(object sender, EventArgs e)

    {

       if (!Page.IsPostBack)

       {

           BindGrid();

       }

    }

 

    protected void AddPerson_Click(object sender, EventArgs e)

    {

        _peopleRepository.Add(new Person(FirstName.Text, LastName.Text));

        BindGrid();

    }

 

    private void BindGrid()

    {

        PeopleGrid.DataSource = _peopleRepository.GetAll();

        PeopleGrid.DataBind();

    }

}

All is nice and simple: we have a grid, and two text boxes that allow us to add a person to the grid. Note that we only access the data source once on the very first page-load, and whenever we add a new person. Not on every post-back.

Now, if we wanted to turn view-state off, we'd have to make a few changes. This is how our markup will now look like:

<form id="form1" runat="server">

  <asp:ScriptManager ID="ScriptManager1" runat="server" />

  <asp:UpdatePanel ChildrenAsTriggers="false" UpdateMode="Conditional" runat="server" ID="PeoplePanel">

    <ContentTemplate>

      <asp:TextBox ID="FirstName" runat="server"/>

      <asp:TextBox ID="LastName" runat="server"/>

      <asp:Button ID="AddPerson" runat="server" Text="Add" OnClick="AddPerson_Click" />

      <asp:GridView runat="server" ID="PeopleGrid" EnableViewState="false">

      </asp:GridView>

    </ContentTemplate>

  </asp:UpdatePanel>

</form>

There are a few things to note here. We've added an UpdatePanel, and we changed its UpdateMode to Conditional and its ChildrenAsTriggers property to false. I am doing this in order to manually control exactly when the UpdatePanel gets updated. Since, as you can see, I've turned off the view-state for the grid, I only want it to be updated after the grid was bound - In any other case the grid will be completely empty, since without view-state it can't automatically recover its state.

So when does the UpdatePanel get updated? When we tell it to. Let's change the BindGrid private method.

private void BindGrid()

{

    PeopleGrid.DataSource = _peopleRepository.GetAll();

    PeopleGrid.DataBind();

    PeoplePanel.Update();

}

Note how we manually update the panel whenever we update the grid. The rest of the code is left unchanged. By making sure the update panel is only updated when grid is re-bound, we'll always have rows in our grid, even though view-state is off. This method works beautifully when you have a page with several un-related GridViews. You can turn off view-state for them all, and only update what you need, when you need it.

So, to recap, if you want to turn off view-state for your grid, you need to follow these steps:

  1. Put an UpdatePanel around the grid, make sure you set UpdateMode to Conditional and its ChildrenAsTriggers property to false.
  2. Make sure you manually call the Update method of the UpdatePanel every time you bind the grid to the data, and only then.
  3. Set the EnableViewState property on your grid to false, and enjoy the improved performance!

This way, any post-back that you have on the page that doesn't change the grid will not upload a huge view-state field, and it won't require you hit the data-source again. Also, less data is sent back to the client.

Of course, if you have inline editing in your grid, this is not going to work very well for you, as you'll probably need the state for each row the user edits. The solution for that, is, well... Don't use inline editing. It's not good for you.

If you want to have a look at the entire example code, you can grab it right here.

Posted by dorony | 7 comment(s)
תגים:

Ajax Page Methods Will Always Have Session

In my ongoing process of performance optimization, I noticed the use of an ASP.NET Ajax Page Method in a certain page. It looked something like this:

[WebMethod(true)]

public static bool HasDataBeenUpdated(DateTime lastUpdateTime)

{

   //Checks something in the cache against this date

   //....

}

Page methods are a way to Ajax-ly call a static method on the page, instead of creating a specific web-service for this. As you can see, this method was marked with [WebMethod(true)]. This property is usually used in web services, but ASP.NET Ajax employs it for page-methods as well (it is rather the same thing). The constructor we used for this attribute is defined like this:

public WebMethodAttribute (

   bool enableSession

)

That means that passing true will cause ASP.NET to load the Session for this request, making it more costly. I noticed that the HasDataBeenUpdated method can be changed so it won't need the Session. Since it was being called every 5 seconds from the client, it had to be very efficient. I made the change, and was able to turn off Session by using [WebMethod(false)]. Thing is, it didn't really get turned off. The request was still slower than it should have been. Apparently, if you use a page-method, Session will be loaded even if you explicitly pass false to the enableSession parameter.

The documentation doesn't mention this, but the example there is about Session access, so that should have probably tipped me off. This issue seems rather weird to me. I understand that maybe the defaults should change (web-services methods default to no-session) when you write [WebMethod] and nothing else. Maybe. But since I'm explicitly asking for no-Session mode, the result is surprising.

Personally, I think that if they wanted page-methods to behave differently from web-service methods, they should have used a different attribute for it. Anyway, the solution is simple: I ended up moving the method to an .asmx web-service and calling it instead.

Posted by dorony | 4 comment(s)
תגים:,