Programming the ObjectDataSource Control

Discover Some Advanced Features of the ObjectDataSource ASP.NET Control

The ObjectDataSource control enables developers to associate data or business layer classes with the ASP.NET data binding mechanism. I covered some aspects of the ObjectDataSource control in "Cache In Using the ObjectDataSource Control" specifically, the object instantiation and data caching. Methods of such classes can be designated as methods for data-bound controls to execute common operations, such as select, delete, update, and insert. Like other data source controls, ObjectDataSource supports declarative parameters to allow developers to pass page-level variables to the object s methods.

The ObjectDataSource control is not the panacea of all Web data access issues. It is simply a tool; a powerful tool indeed but not necessarily ideal for every situation. The ObjectDataSource control is particularly suited for declarative programming and, in general, all those scenarios where your information fits well in the control s programming interface. A slogan I usually apply to the ObjectDataSource control is, either easy or nothing. The ObjectDataSource control wraps a good deal of internal tasks, and its overall performance is slightly worse than classic binding. So you might be happy to pay extra for quicker programming; but would you pay more to get less?

In this article, I ll go through other programming aspects, such as paging, sorting, and conflict detection during updates. To start, though, I ll discuss parameter binding.

 

Parameter Binding

The primary goal of the ObjectDataSource control is to create, read, update, delete (CRUD). The methods on the object should map closely to the semantics of CRUD. If your business layer doesn t allow for this, you re better off wrapping your business layer into an intermediate object that adapts the native interface to the requirements of ObjectDataSource. If this sounds like it s costly or impossible, then simply drop ObjectDataSource and resort to classic binding.

Methods bound to properties like SelectMethod, DeleteMethod, InsertMethod, and UpdateMethod can accept any number of parameters. You use ad hoc collections such as SelectParameters, DeleteParameters, InsertParameters, and UpdateParameters to pass parameters to methods. These collections are persisted to the ASPX source file and bind live data to a method s formal parameters in a declarative manner. You declare the source of the parameter data and how it binds to a given formal parameter on the method s signature. Figure 1 lists all supported parameter sources.

Parameter

Description

ControlParameter

Gets the parameter value from any public property of the specified server control.

CookieParameter

Gets the parameter value from the content of the specified HTTP cookie property.

FormParameter

Gets the parameter value from the specified input field in the HTTP request form.

Parameter

Gets the parameter value from the specified constant value.

ProfileParameter

Gets the parameter value from the specified property name in the profile object created from the application s personalization scheme.

QueryStringParameter

Gets the parameter value from the specified variable in the request query string.

SessionParameter

Gets the parameter value based on the content of the specified session state slot.

Figure 1: Parameter types in ASP.NET 2.0.

Each parameter class has a Name property and a set of properties specific to its role and implementation. To understand declarative parameters in data source controls, take a look at the following code snippet:

<asp:ObjectDataSource runat="server" ID="ObjectDataSource1"

   TypeName="Samples.DAL.CustomerManager"

   SelectMethod="Load">

 <SelectParameters>

    <asp:ControlParameter Name="id" ControlId="CustomerID"

          PropertyName="Text" />

 </SelectParameters>

</asp:ObjectDataSource>

The bound object data source contains a method named Load, which takes a single string parameter named id. The data source control automatically invokes the Load method passing the information returned by the ControlParameter object. The value of the parameter is determined by the value of the Text property on the CustomerID control (likely a TextBox). The ID of the control is in the ControlId attribute. You get an exception if the page that hosts the preceding code lacks a control with the specified ID and a public property named Text.

Read operations normally don t require many parameters, and are relatively easy to handle; but what about write operations? To delete a record, you likely need just the unique ID. However, to add or update a record you must pass all required values. Depending on the structure of the record, this can be quite a few values. There s a better approach than simply enumerating a potentially long list of parameter values. You can design your business layer according to the Data Mapper pattern and reason in terms of classes and entities then mapped to tables. In other words, if you re going to create a manager for customer entities, you end up having methods such as those shown here:

public class CustomerManager

{

  public void Save(Customer c) { ... }

  public void Insert(Customer c) { ... }

  public void Delete(Customer c) { ... }

}

The Customer class is an entity class that represents your definition of a customer. Internally, the methods will fall into some raw ADO.NET code to run database commands, as appropriate. How can you tell the ObjectDataSource about this entity class? You use the DataObjectTypeName property:

<asp:ObjectDataSource runat="server" ID="ObjectDataSource1"

    TypeName="Samples.DAL.CustomerManager"

    DataObjectTypeName="Samples.DAL.Customer"

    UpdateMethod="Save"

    SelectMethod="Load" />

Note that when you set the DataObjectTypeName property, any parameters collection for update, insert, and delete operations is ignored. The ObjectDataSource instantiates a default instance of the data object type before the write operation is performed. Next, it attempts to fill the public members of the data object type with the values of any matching input fields found around the data-bound control (say, the GridView) associated with the ObjectDataSource. Because this work is performed using reflection, the names of the input fields in the data-bound control must match the names of public properties exposed by the object in the DataObjectTypeName property. As a result, you can t use complex data types to define the Customer class:

public class Customer {

  public string CompanyName { ... }

  public string ContactName { ... }

  public Address OfficeAddress { ... }

}

The CompanyName and ContactName members have good chances to match an input field in the bound control because they represent individual values. The same can t be said for the OfficeAddress member, which is declared of a custom type like Address. If you go with this schema, all the members in Address will be ignored and any related information won t be carried into the Save method. Instead, all the members in the Address data structure should become members of the Customer class.

When it comes to setting parameters, you can also rely on the Updating, Deleting, and Inserting events fired by the ObjectDataSource control. When using ObjectDataSource with an ASP.NET 2.0 made-to-measure control (i.e., GridView), most often the binding is totally automatic and you re apparently kept off the business. If, for whatever reasons, the ObjectDataSource control doesn t load all the data its method needs to perform the operation, in the Updating (or similar) events, you kick in and complete the list of input parameters for the method:

protected void Updating(object sender,

   ObjectDataSourceMethodEventArgs e)

{

   Customer cust = (Customer) e.InputParameters[0];

   cust.CustomerID = "...";

}

Where should you normalize input params and assign default values? Parameters can be given default values both declaratively and programmatically through the Updating, Deleting, and Inserting events. The best, though, is that you set the DefaultValue property of the parameter to null and deal with unspecified parameters in the business or data access layer.

 

Conflict Detection

In a situation in which multiple users have read/write access to the database, what should be the behavior of the update/delete methods if the record they attempt to work on has been modified?

The ObjectDataSource control uses the ConflictDetection property to determine what to do when performing update and delete operations. The property is declared of type ConflictOptions (an enum type). The default value is OverwriteChanges, which means that any operation happens regardless of whether values in the row have changed since they were last read. This approach is also known as last-win. Alternatively, you can use the CompareAllValues option. In this case, the ObjectDataSource control passes its update and delete methods as well as the original data read from the database. As you can see, though, changing the value of ConflictDetection is not enough. Your data access layer must be written to handle conflicts properly. In other words, the ConflictDetection property simply indicates how many parameters are passed to the methods. But still, the data access layer is in charge of detecting and handling conflicts by using proper SQL code. Finally, note that if DataObjectTypeName is used, Update and Delete methods must take two parameters for old and new values, respectively.

 

Paging and Sorting

The ObjectDataSource control provides an infrastructure for paging and sorting; but actually, any such operations should be implemented in the data access layer in the class bound to ObjectDataSource.

Let s consider paging first. Three properties participate in paging: EnablePaging, StartRowIndexParameterName, and MaximumRowsParameterName. EnablePaging is a Boolean property that enables and disables paging. The default value is false, meaning that paging is not turned on automatically. The property StartRowIndexParameterName indicates the name of the formal parameter in the Select method that accepts the value for the first record to retrieve. Likewise, the property MaximumRowsParameterName sets the name of the formal parameter in the Select method that accepts the value for the maximum number of records to retrieve. Figure 2 shows the details. It is assumed that the Select method has overloads that support paging and sorting. How paging and sorting are implemented in the data access layer is up to the programmer.

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"

   TypeName="ProAspNet20.DAL.Customers"

   EnablePaging="true"

   SortParameterName="sortExpression"

   StartRowIndexParameterName="firstRow"

   MaximumRowsParameterName="totalRows"

   SelectMethod="LoadByCountry">

 <SelectParameters>

  <asp:ControlParameter Name="country" ControlID="Countries"

       PropertyName="SelectedValue" />

  <asp:ControlParameter Name="totalRows" ControlID="PageSize"

       PropertyName="Text" />

  <asp:ControlParameter Name="firstRow" ControlID="FirstRow"

       PropertyName="Text" />

 </SelectParameters>

</asp:ObjectDataSource> 

Figure 2: Paging and sorting in ObjectDataSource.

Sorting capabilities of the ObjectDataSource pass through the SortParameterName property. As usual, the property indicates the name of the formal parameter in the Select method that accepts the value for the field to sort by.

How does the ObjectDataSource control use the paging and sorting information? What s the purpose of passing the name of formal parameters? Commonly, the ObjectDataSource receives the input for paging or sorting its data from a bound control; for example, a GridView. The GridView will also pass the index of the first record to retrieve and the size of the page; for sorting, the GridView will pass the sorting expression. Internally, the ObjectDataSource uses reflection to prepare the call to its data source object. In doing so, it needs to know which parameters in the method s prototype are to be bound to paging and sorting information.

 

Conclusion

Data source controls such as SqlDataSource and ObjectDataSource are simply programming tools and should be used only if they re right for the job you re trying to accomplish. From a functional perspective, classic binding and data source controls are nearly identical. Classic binding provides a slightly better performance as it uses no reflection but requires you to write more code. Should you opt for SqlDataSource or ObjectDataSource? SqlDataSource is designed for use in two-tier applications where the user interface layer communicates directly with the data tier. In contrast, ObjectDataSource is ideal for three-tier scenarios where you connect the user interface to the middle tier. ObjectDataSource supports advanced features, such as caching, paging, and sorting as long as these features are supported in the middle tier. As a result, with ObjectDataSource you must have a strong and well designed business layer. That s the key point. First, you make up your business layer and then you adapt it to work with ObjectDataSource, creating a wrapper object if this is the case. It doesn t work the other way around.

 

Dino Esposito is a Solid Quality Learning mentor and author of Introducing ASP.NET 2.0 AJAX Extensions and Programming Microsoft ASP.NET 2.0-Core Reference, both from Microsoft Press. Based in Italy, Dino is a frequent speaker at industry events worldwide. Join the blog at http://weblogs.asp.net/despos.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish