Representing Objects with the ObjectDataSource Control
The ObjectDataSource control includes five main properties:
- TypeName—The name of the type of object that the ObjectDataSource control represents.
- SelectMethod—The name of a method that the ObjectDataSource calls when selecting data.
- UpdateMethod—The name of a method that the ObjectDataSource calls when updating data.
- InsertMethod—The name of a method that the ObjectDataSource calls when inserting data.
- DeleteMethod—The name of a method that the ObjectDataSource calls when deleting data.
An
ObjectDataSource control can represent any type of object in the .NET
Framework. This section discusses several types of objects you might
want to represent. For example, you learn how to use the
ObjectDataSource control with components that represent collections,
ADO.NET DataReaders, DataSets, LINQ to SQL queries, and web services.
You can use
the ObjectDataSource control to represent any object (any class that
derives from the System.Object class). If the object does not support
the IEnumerable interface, the ObjectDataSource control automatically
wraps the object in a new object that supports the IEnumerable
interface. You can even represent an ASP.NET ListBox control with an
ObjectDataSource (not that a ListBox has any interesting methods).
Binding to a Component
Let’s
start with a really simple component. The component in Listing is named
MovieCollection. It contains one method named GetMovies(), which
returns a collection of movie titles.
The ObjectDataSource control includes two properties named TypeName and SelectMethod. The TypeName
property contains the name of the component that you want to represent
with the ObjectDataSource control. The SelectMethod property represents
the method of the component that you want to call when selecting data.
Notice that the GridView control is bound to the ObjectDataSource
control through its DataSourceID property. When you open the page, the
list of movies is retrieved from the MovieCollection component and
displayed in the GridView. The MovieCollection component contains
instance methods. The ObjectDataSource automatically creates a new
instance of the MovieCollection
component before calling its GetMovies() method. It automatically
destroys the object after it is finished using the object. You also can
use the ObjectDataSource control to call shared (static) methods. In
that case, the ObjectDataSource doesn’t need to instantiate a component
before calling the
method.
Binding to a DataReader
Typically, you use the ObjectDataSource control to represent database data. The .NET Framework provides
you with multiple ways of representing data. This section discusses how
you can use an ObjectDataSource to represent a DataReader.
The
different ADO.NET objects are compared and contrasted in “Building Data
Access Components with ADO.NET.” The ADO.NET DataReader object provides
you with a fast, read-only representation of database data. If you need
to retrieve database records in the fastest possible way, then you
should use a DataReader object.
For
example, the component in Listing 16.3, the MovieDataReader component,
returns all the movies from the Movies database table by using the
SqlDataReader object. Notice that the component imports the
System.Data.SqlClient namespace to use this Microsoft SQL
Server–specific ADO.NET object.
Binding to a DataSet
You
also can use the ObjectDataSource when you need to represent an ADO.NET
DataSet. Using a DataSet is slower than using a DataReader. However,
you can perform advanced operations, such as filtering and sorting, on
data represented with a DataSet.
Binding to a LINQ to SQL Query
LINQ
to SQL is the preferred method of data access in the .NET Framework
3.5. The expectation is that you will use LINQ to SQL instead of ADO.NET
to interact with a database. Chapter, “Data Access with LINQ to SQL,”
is devoted to the topic of LINQ.
Binding to a Web Service
Web
services enable you to share information across the Internet. When you
communicate with a remote web service, you use a local proxy class to
represent the web service located on the remote machine. You can use the
ObjectDataSource to represent this proxy class.
If you are
not using Visual Web Developer, you can create a web service proxy class
from the command line by using the Wsdl.exe (Web Services Description
Language) tool. When you click Add Reference, a new folder is added to
your project named App_WebReferences. The App_WebReferences folder
contains a subfolder named LocalServices. Finally, your web
configuration file is updated to include the URL to the TimeService web
service. Now that we have a consumable web service, we can represent the
Web service using the ObjectDataSource control.
If you open
the ShowWebService.aspx page from the CD that accompanies this book,
you receive an error. Before the page will work correctly, you need to
update the web configuration file with the correct path to the web
service on your computer.
Using Parameters with the ObjectDataSource Control
You
can use parameters when calling a method with the ObjectDataSource
control. The ObjectDataSource control includes five parameter
collections:
- SelectParameters—Collection of parameters passed to the method represented by the SelectMethod property.
- InsertParameters—Collection of parameters passed to the method represented by the InsertMethod property.
- UpdateParameters—Collection of parameters passed to the method represented by the UpdateMethod property.
- DeleteParameters—Collection of parameters passed to the method represented by the DeleteParameters property.
- FilterParameters—Collection of parameters used by the FilterExpression property.
- DataBound controls—such as the GridView, DetailsView, and FormView controls—can build the necessary parameter collections for you automatically.
Passing Objects as Parameters
Passing
long lists of parameters to methods can make it difficult to maintain
an application. If the list of parameters changes, you need to update
every method that accepts the list of parameters. Rather than pass a
list of parameters to a method, you can pass a particular object. For
example, you can pass a CompanyEmployee object to a method used to
update an employee, rather than a list of parameters that represent
employee properties.
Using
Parameters with the ObjectDataSource Control If you specify a value for
an ObjectDataSource control’s DataObjectTypeName property, then you can
pass an object rather than a list of parameters to the methods that an
ObjectDataSource represents. In that case, the ObjectDataSource
parameters represent properties of the object.
The
DataObjectTypeName property has an effect on only the methods
represented by the InsertMethod, UpdateMethod, and DeleteMethod
properties. It does not have an effect on the method represented by the
SelectMethod property. There is one important limitation when using the
DataObjectTypeName property. The object represented by this property
must have a parameterless constructor.
Paging, Sorting, and Filtering Data with the ObjectDataSource Control
The
ObjectDataSource control provides you with two options for paging and
sorting database data. You can take advantage of either user interface
or data source paging and sorting. The first option is easy to
configure, and the second option has much better performance. In this
section, you learn how to take advantage of both options. You also learn
how to take advantage of the ObjectDataSource control’s support for
filtering. When you combine filtering with caching, you can improve the
performance of your data-driven web pages dramatically.
User Interface Paging
Imagine
that you want to use a GridView control to display the results of a
database query in multiple pages. The easiest way to do this is to take
advantage of user interface paging. User interface paging is convenient
because you can enable it by setting a single property. However, there
is a significant drawback to this type of paging. When user interface
paging is enabled, all the movie records must be loaded into server
memory. If the Movies database table contains 3 billion records, and you
are displaying 3 records a page, then all 3 billion records must be
loaded to display the 3 records. This places an incredible burden on
both the web server and database server. In the next section, you learn
how to use data source paging, which enables you to work efficiently
with large sets of records.
Data Source Paging
Data
source paging enables you to write custom logic for retrieving pages of
database records. You can perform the paging in the component, a stored
procedure, or a LINQ to SQL query. If you want the best performance,
then you should write your paging logic in either a stored procedure or a
LINQ query. We’ll examine both approaches in this section.
Designer
surface, the Designer creates a new entity named Movy. The Designer is
attempting to singularize the word and it fails badly. You must rename
the entity to Movie in the Properties window. (I hope this Visual Web
Developer grammar bug will be fixed by the time you read this). You are
not required to use LINQ to SQL when you want to implement data source
paging. As an alternative to LINQ to SQL, you can perform your paging
logic within a SQL stored procedure.
The paging
mechanism described in this section is based on the mechanism used by
the Microsoft ASP.NET forums at http://www.asp.net/forums and the XBOX
forums at http://www.xbox.com. Both of these websites handle an
incredible number of message posts every day. The forums software was
written with ASP.NET, and it is available from TelligentSystems
(www.telligentsystems.com) as part of their Community Server product.
User Interface Sorting
If
you need to sort the records displayed by the GridView control, then
the easiest type of sorting to enable is user interface sorting. When
you take advantage of user interface sorting, the records are sorted in
the server’s memory.
Data Source Sorting
Imagine
that you are working with a database table that contains 3 billion
records and you want to enable users to both sort the records contained
in this table and page through the records contained in this table. In
that case, you’ll want to implement both data source sorting and paging.
Filtering Data
You
can supply the ObjectDataSource control with a filter expression. The
filter expression is applied to the data returned by the control’s
select method. A filter is particularly useful when used in combination
with caching. You can load all the data into the cache and then apply
different filters to the cached data.
You learn how to cache data with the ObjectDataSource control in Chapter, “Caching Application Pages and Data.”
Behind the
scenes, the ObjectDataSource control uses the DataView.RowFilter
property to filter database rows. You can find detailed documentation on
proper filter syntax by looking up the DataColumn.Expression property
in the .NET Framework SDK Documentation.
Handling ObjectDataSource Control Events
The ObjectDataSource control supports the following events:
- Deleting—Occurs immediately before the method represented by the DeleteMethod property is called.
- Deleted—Occurs immediately after the method represented by the DeleteMethod property is called.
- Inserting—Occurs immediately before the method represented by the InsertMethod property is called.
- Inserted—Occurs immediately after the method represented by the InsertMethod property is called.
- Selecting—Occurs immediately before the method represented by the SelectMethod property is called.
- Selected—Occurs immediately after the method represented by the InsertMethod property is called.
- Updating—Occurs immediately before the method represented by the InsertMethod property is called.
- Updated—Occurs immediately after the method represented by the InsertMethod property is called.
- Filtering—Occurs immediately before the filter expression is evaluated.
- ObjectCreating—Occurs immediately before the object represented by the ObjectDataSource control is created.
- ObjectCreated—Occurs immediately after the object represented by the ObjectDataSource control is created.
- ObjectDisposing—Occurs before the object represented by the ObjectDataSource control is destroyed.
Notice that most
of these events come in pairs. One event happens immediately before a
method is called, and one event happens immediately after a method is
called. You can handle these events to modify the parameters and objects
represented by an ObjectDataSource control. You can also handle these
events to handle any errors that might result from calling methods with
the ObjectDataSource control.
Adding and Modifying Parameters
You
can handle the Selecting, Inserting, Updating, and Deleting events to
modify the parameters that are passed to the methods called by the
ObjectDataSource control. There are several situations in which you
might want to do this.
First,
if you are working with an existing component, you might need to change
the names of the parameters passed to the component. For example,
instead of passing a parameter named id to an update method, you might
want to rename the parameter to movieId. Second, you might want to pass
additional parameters to the method being called. For example, you might
need to pass the current username, the current IP address, or the
current date and time as a parameter to a method.
Handling Method Errors
You
can handle the Selected, Inserted, Updated, or Deleted events in order
to handle any errors that might result from calling a method. For
example, the page handles the Inserting event to capture any errors
raised when the method represented by the ObjectDataSource control’s
InsertMethod property is called.
Instead of
handling errors at the level of the DataSource control, you can handle
errors at the level of the DataBound control. For example, the
DetailsView control supports an ItemInserted event.
Handling the ObjectCreating Event
By
default, the ObjectDataSource control can represent only components
that have a constructor that does not require any parameters. If you are
forced to use a component that does require parameters for its
constructor, then you can handle the ObjectDataSource control’s
ObjectCreating event.
The ObjectCreating event is not raised when a shared method is called.
Concurrency and the ObjectDataSource Control
Imagine
that two users open the same page for editing the records in the movies
database table at the same time. By default, if the first user submits
changes before the second user, then the first user’s changes are
overwritten. In other words, the last user to submit changes wins.
This
default behavior of the ObjectDataSource control can be problematic in
an environment in which a lot of users are working with the same set of
data. You can modify this default behavior by modifying the
ObjectDataSource control’s ConflictDetection property. This property
accepts the following two values:
- CompareAllValues—Causes the ObjectDataSource control to track both the original and new values of its parameters.
- OverwriteChanges—Causes the ObjectDataSource to overwrite the original values of its parameters with new values (the default value).
When you set the
ConflictDetection property to the value CompareAllValues, you should add
an OldValuesParameterFormatString property to the ObjectDataSource
control. You use this property to indicate how the original values the
database columns should be named.
Extending the ObjectDataSource Control
In
this final section, we examine two methods of extending the
ObjectDataSource control. You learn how to create a custom data source
control by deriving a new control from the ObjectDataSource control. You
also learn how to create custom parameters that can be used with the
ObjectDataSource (and other DataSource controls).
As
an alternative to registering the MovieDataSource control in a page,
you can register the control for an entire application in the web
configuration file within the <pages> element.
Creating Custom Parameter Objects
The
standard DataSource Parameter objects included in the ASP.NET Framework
enable you to represent objects such as query string values, items from
Session state, and values of control properties. If none of the
standard Parameter objects satisfy your requirements, you always have
the option of creating a custom Parameter object.