Minimize a DropDownList’s ViewState

Let's say you have a very large DropDownList with lots of values and text.  We need to maintain ViewState in this DropDownList so that we can retrieve the selected value on a post back.   Of course, this means that now the ViewState contains the data for every single value in the list, both values and text included.   Even though the page itself may be fairly simple and lightweight, the result of having this simple DropDownList on the page is that the page size is quite large and the amount of data passed back and forth on a postback is very large as well.

If you have an efficient database, and/or if you are caching the data anyway, you might not mind re-loading the list items each time the page posts back to eliminate the need for the page itself to hold all of this ViewState data. 

However, if you turn off ViewState on the DropDownList, you will notice that it now does not remember the selected value on post backs.  The solution is very simple -- just manually set the DropDownList after re-loading it to the value from the HTML form post.

To do this, first we disable ViewState on the DropDownList (EnableViewState="False").  Then, instead of a typical PageLoad() method like this:

 

        protected void Page_Load(object sender, EventArgs e)

        {

            if (!IsPostBack)

            {

                loadList();              

                // other stuff here

            }

        }

      
we would load the list every time the page posts back:

        protected void Page_Load(object sender, EventArgs e)

        {

            loadList();     

 

            if (!IsPostBack)

            {

                 // other stuff here

            }

        }

       
And, in that loadList() method, instead of simply doing this:

        void loadList()

        {

            dlTest.DataSource = // get data from DB

            dlTest.DataBind();

        }

       
you also would check to see if it has a posted value:

        void loadList()

        {

            dlTest.DataSource = // get data from DB

            dlTest.DataBind();

 

            string r = Request[dlTest.UniqueID];

            if (r != null)

                dlTest.SelectedValue = r;

        }


That loads the DropDownList each time and ensures that the value set is what was posted back.  Thus, the control will now "remember" the selected value on each postback, but without requiring any data stored in the ViewState at all. 

It would also be easy to implement this logic in a control that inherits from DropDownList; say, a LightDropDownList.  Techniques like this will of course work on other controls as well, such as a ListBox or a CheckedList.

With fast database connections and performance, as well as data caching, sometimes persisting data on the page is not the optimal way to go.  Like always, there are different ways to skin a cat and sometimes simple little tweaks like this can have huge benefits on performance.

Update:  See the comments for other options are that even easier/better.  Also in the comments, Richard has provided a link to this excellent post on ViewState that I highly recommend checking out.  It's long, but very informative and quite entertaining as well.   Thanks for the great feedback.

But it’s not missing. It’s just … invisible, that’s all.

Microsoft Access has a pretty handy boolean property that you can set for any "text" column in your tables:

AllowZeroLength

True means that empty strings ('') are allowed in the column, False means that they are not.

This is actually quite nice, because by using this along with the Required (i.e., disallow NULLS) property, you can ensure that your column has an actual, non-Null, non-Empty String value without the need for any additional constraints.

SQL Server does not have this property, but we can easily achieve the same effect by using a CHECK constraint:

create table foo
(
    column1 varchar(100) not null check (column1 <> '')
)

This enforces that column1 is not only never null, but it also cannot be an empty string.  For example:

insert into foo (column1) values ('')

Msg 547, Level 16, State 0, Line 2
The INSERT statement conflicted with the CHECK constraint "CK__foo__column1__6E8B6712". The conflict occurred in database "PlayGround", table "dbo.foo", column 'column1'.
The statement has been terminated.

However, we need to remember that there is another side effect of this check constraint:  SQL Server ignores any trailing spaces when comparing two values, so this effectively also means that a non-empty string that consists of all space characters is also not valid in your table.  To demonstrate:

insert into foo (column1) values (space(10))

Msg 547, Level 16, State 0, Line 2
The INSERT statement conflicted with the CHECK constraint "CK__foo__column1__6E8B6712". The conflict occurred in database "PlayGround", table "dbo.foo", column 'column1'.
The statement has been terminated.

Now, this can be a good thing or a bad thing depending on what you are looking for.  Overall, I'd say it's a good thing -- to me, a string of 30 spaces is just as bad as a NULL or an empty string in a column that is required, such as a project code or a first/last name.  Of course, what ultimately is best for any specific situation depends on the requirements.

If you decide that you do want to disallow only empty strings but strings consisting of all spaces are valid, then you can write your check constraint like this:

create table foo
(
    column1 varchar(100) not null check (column1 + 'x' <> 'x')
)

By appending 'x' to the end of the column's potential value and comparing the result with 'x', we are sure that if the column contains at least one space this check will be TRUE, but we are still not allowing empty strings.

So, don't forget about handling empty and/or "invisible" strings in your columns.  It won't generally satisfy your client if you argue that "the username isn't missing -- it isn't null.  It is just ... invisible!" 

After all, you don't want to end with "invisible" payments on your invoices!