My thoughts…

A mix of many topics.

Archive for April, 2008

Using a Primary Key with ASP.NET Data Controls

Posted by Rebecca Chernoff on April 27, 2008

It is common in ASP.NET to be working with data.  Using data on the page immediately brings up several concerns to be aware of.  One issue many people are aware of is how to have access to the data that you, as the developer, need – without letting the user have access to the data.  The main example of this is the primary key of your data.  If there is a GridView displaying a list of users for a community site, the users should not be able to see the primary key for anyone.  However, if a row is edited, having the primary key of that user is vital.  Having access to the primary key is extremely important in this scenario.  There are a couple different ways to have access to fields in code-behind that are not visible to the user.  These methods are the topic for this article.

Let’s just get straight to the code now, shall we?  If you want to follow along, create a new website in Visual Studio 2008.  Then, added an xml file to the App_Data folder called Holidays.xml.  This xml contains all the federal holidays that the U.S. Government recognizes for the year 2009.

<?xml version="1.0" encoding="utf-8" ?>
<holidays>
	<holiday>
		<id>1</id>
		<name>New Year's Day</name>
		<date>01/01/2009</date>
	</holiday>
	<holiday>
		<id>2</id>
		<name>Martin Luther King, Jr. Day</name>
		<date>01/19/2009</date>
	</holiday>
	<holiday>
		<id>3</id>
		<name>Washington's Birthday</name>
		<date>02/16/2009</date>
	</holiday>
	<holiday>
		<id>4</id>
		<name>Good Friday</name>
		<date>04/20/2009</date>
	</holiday>
	<holiday>
		<id>5</id>
		<name>Memorial Day</name>
		<date>05/25/2009</date>
	</holiday>
	<holiday>
		<id>6</id>
		<name>Independance Day (observed)</name>
		<date>07/03/2009</date>
	</holiday>
	<holiday>
		<id>7</id>
		<name>Labor Day</name>
		<date>09/07/2009</date>
	</holiday>
	<holiday>
		<id>8</id>
		<name>Thanksgiving Day</name>
		<date>11/26/2009</date>
	</holiday>
	<holiday>
		<id>9</id>
		<name>Christmas</name>
		<date>12/25/2009</date>
	</holiday>
</holidays>

In order to display the holidays, I am using a very basic GridView. I am not using a DataSource control, rather I’ve chosen to bind the GridView to a collection in code-behind.  I’ve also set up a label to display information about the currently selected row.

<asp:Label runat="server" ID="lblSelected"
    Text="Please select a holiday." />

<asp:GridView runat="server" ID="gvHolidays"
    AutoGenerateColumns="False"
    AutoGenerateSelectButton="True"
    OnSelectedIndexChanging="gvHolidays_SelectedIndexChanging">
    <Columns>
        <asp:BoundField HeaderText="Name"
            DataField="Name" SortExpression="Name" />
        <asp:BoundField HeaderText="Day"
            DataField="Day" SortExpression="Day" />
        <asp:BoundField DataField="Date" HeaderText="Date"
            SortExpression="Date" />
    </Columns>
</asp:GridView>

In the code-behind, I pull the holidays out of the xml file and stuff them into the GridView.  I am using a couple of new features in .net 3.5.  If you don’t understand the syntax, don’t worry.  Trust me that the holidays from my xml file are being bound to the grid.  You can bind the grid however you want.

One important piece here is that my holidays object does include the Id field.  There is no column for the Id field in the GridView, because as the topic of this article dictates, the user cannot have access to the primary key data. For now though, this is just the setup. The good stuff comes in a minute.

protected void Page_Load(object sender, EventArgs e) {
    gvHolidays.SelectedIndexChanging += new GridViewSelectEventHandler(gvHolidays_SelectedIndexChanging);
    if (!IsPostBack) {
        XDocument xDoc = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Holidays.xml"));
        var holidays = from h in xDoc.Descendants("holiday")
                       orderby h.Element("date").Value
                       select new { Id = Int32.Parse(h.Element("id").Value), Name = h.Element("name").Value, Day = h.Element("day").Value, Date = DateTime.Parse(h.Element("date").Value) };
        gvHolidays.DataSource = holidays;
        gvHolidays.DataBind();
    }
}

void gvHolidays_SelectedIndexChanging(object sender, GridViewSelectEventArgs e) {
    int rowIndex = e.NewSelectedIndex;
    lblSelected.Text = "You selected RowIndex " + rowIndex;
}

At this point, we have a GridView that looks like the following:  When a row is selected, the GridViewSelectEventArgs provides us with the index of the row that was selected in the NewSelectedIndex property.

Basic GridView

This is all fine and dandy, but now that the border of the puzzle is put together, it is time to get to the mess that is the rest of the puzzle.  My DataSource includes the Id field, but this isn’t something that the user should have access to.  This field needs to be available server-side, but should not be readable on the client-side.  We have 3 options for accomplishing this.

Try #1:  BoundField with Visible=”False”

This option might be what you try first.  Don’t!  It won’t work.  When a BoundField is not going to be rendered (i.e., when Visible=”False”), the value does not make a round-trip between the client and the server.  This makes it impossible to get the value of our primary key from the server when we react to a Selecting, Updating, or Deleting event.

Try #2:  TemplateField with Visible=”False”

After hearing that a BoundField will not work, it might seem that a TemplateField won’t work either.  In fact, it will work.  A TemplateField is really just the controls you put inside the template.  This means that the controls themselves will handle the Visible property.  If a Label s put iinside a TemplateField, then it will provide the primary key.  There is one caveat I should mention for this method.  If you are adding multiple TemplateFields, while it won’t increase the size of your html markup, it will increase the size of your ViewState.

<asp:TemplateField Visible="false">
    <ItemTemplate>
        <asp:Label runat="server" ID="lblId"
            Text='<%# Eval("Id") %>' />
    </ItemTemplate>
</asp:TemplateField>

Once the TemplateField is in the GridView, pulling out the primary key is a trivial matter.

protected void gvHolidays_SelectedIndexChanging(object sender, GridViewSelectEventArgs e) {
    int rowIndex = e.NewSelectedIndex;
    int primaryKey = Int32.Parse(((Label)gvHolidays.Rows[rowIndex].FindControl("lblId")).Text);
    lblSelected.Text = "You selected RowIndex " + rowIndex + " which has a primary key of " + primaryKey;
}

Doing it this way gets a little messy because the return object from FindControl must be cast into a Label. Then, the Text of the Label must be parsed into an int. Not what I would call clean. There is an easier way…

Try #3:  Using DataKeyNames and DataKeys properties

I’d like to offer the final method, which is the one that I recommend.  The data controls in ASP.NET 2.0+ have a DataKeyNames property.  When this property is used, a DataKeys collection will be created that contains the values specified as a DataKeyName. Let’s update our GridView to use the DataKeyNames property.

<asp:GridView runat="server" ID="gvHolidays"
    AutoGenerateColumns="False"
    AutoGenerateSelectButton="True"
    OnSelectedIndexChanging="gvHolidays_SelectedIndexChanging"
    DataKeyNames="Id"
    <Columns>
        <asp:BoundField HeaderText="Name"
            DataField="Name" SortExpression="Name" />
        <asp:BoundField DataField="Date" HeaderText="Date"
            SortExpression="Date"
            DataFormatString="{0:D}" />
    </Columns>
</asp:GridView>

Notice that I have set DataKeyNames=”Id”. In my code-behind, the DataKeys collection will now exist.

protected void gvHolidays_SelectedIndexChanging(object sender, GridViewSelectEventArgs e) {
    int rowIndex = e.NewSelectedIndex;
    int primaryKey = (int)gvHolidays.DataKeys[rowIndex].Values["Id"];
    lblSelected.Text = "You selected RowIndex " + rowIndex + " which has a primary key of " + primaryKey;
}

Simple, right?! Since the DataKeyNames property only contains one field, it is ok to use gvHolidays.DataKeys[rowIndex].Value. It is good practice to not use this shortcut because if the code is changed later to put another field into the DataKeys collection, the existing code needs to be changed. Yes, multiple fields can be put into the DataKeyNames property – comma separate them. A caveat though: don’t put too many fields into the DataKeys collection because it will increase the weight of the page.

It is extremely important to recognize that both of these methods have issues you must be aware of if you are using Updating and Deleting in your GridView. Read this article!!! Really these issues only come into play if you have ViewState turned off. Even if you are not currently doing this, read this article. Maybe down the road when you are, you will remember this article. 😛

Posted in ASP.NET | 15 Comments »

Churned Ajax Toolkit

Posted by Rebecca Chernoff on April 24, 2008

It has been quite a while since I have had the time to work on a personal project of this (*crosses fingers*) magnitude.  I am excited to present my newest project:  the Churned Ajax Toolkit!  This toolkit will grow into a library of controls that serve as a supplement to the Ajax Control Toolkit.  The inclusion of controls will largely be driven by the community.  The initial release was posted yesterday so head on over to the project page and check it out!  A live demo is also available.

This initial release contains a single control:  TextEditExtender.  This control extends a textbox, but displays it as a label when the textbox does not have focus.  This in-place editing provides a more friendly user experience.  The control is customizable by setting various css properties.  You can view the documentation on the Discussions page at the project page.

The release contains 3 files:

  • ChurnedAjaxToolkit_1.0.0.0:  The source code, demo website, and the dll.
  • ChurnedAjaxToolkit_1.0.0.0_DLL:  The dll only.
  • ChurnedAjaxToolkit_1.0.0.0_DemoSite:  The demo website only.

Enjoy!

Posted in ChurnedAjaxToolkit | 1 Comment »