The ExtendedObjectDataSource package is composed of 2 controls that are a replacement of
- The CompatObjectDataSource control (CODS).
- The ExtendedObjectDataSource control (EODS).
The CODS control is named this way because its interface its fully compatible with the
ObjectDataSource control although it provides additional benefits that will be explained
in this section. To start using the CODS in your projects, just replace the "asp:ObjectDataSource" tags for "manu:CompatObjectDataSource" and you are ready to go.
The EODS control has many similarities with the CODS
but provides a more orthogonal interface for the select operation and the possibility
to cache data if sorting and filtering are used.
If you have used the ObjectDataSource, the learning curve for the controls present
in the ExtendedObjectDataSource package is minimal (actually close to null).
To understand all the features of the ExtendedObjectDataSource package you need
to understand how the ObjectDataSource works. If you need to understand the ObjectDataSource
in detail, refer to the following tutorial:
To use the ObjectDataSource with custom paging, we need to implement the SelectCountMethod
that returns the total number of rows. The SelectCountMethod
is called after the SelectMethod, so if we want to return the total
number of rows in the SelectCountMethod
we have two main options:
- Execute another query in the SelectCountMethod, affecting the perfomance of our
- Save the total number of rows in the SelectMethod (in an instance field) and return it in the SelectCountMethod. However, this requires our SelectMethod and SelectCountMethod to be instance methods.
This restricts our design, having to add an innecesary method (the
SelecCountMethod). Also the performance suffers because two methods are called using
reflection for each select operation. Does not make more sense to return the total
number of row in the select method?
That is exactly what the EODS lets us do. The EODS
has a property called TotalRowCountParameterName. If we have enabled paging, the control will search for an additional parameter in
parameter) called as specified by the TotalRowCountParameterName
property that will
return the total row count. We can forget about the SelectCountMethod
and now our
select method can be static.
Calling the methods using dynamically generated MSIL
Every time the ObjectDataSource executes a Select, Insert, Update or Delete operation
it needs to explore the type associated with it, find all the methods, iterate trough
them to see if all the parameters of the method match what the control expects, and then invoke the
method using reflection. This operations are
very expensive and can have an impact on our site performance if
we use the ObjectDataSource in many places and have enough traffic.
The CODS and the EODS control cache the information
about the method to call (we can control the caching
information with the properties EnableReflectionCaching,
CacheMethodInfoDuration and CacheMethodInfoExpirationPolicy)
and invoke it using dynamically generated MSIL instead of using reflection,
obtaining a 2000% performance increase.
The ObjectDataSource was not designed with extensibility in mind so if it fits with
our way of doing things, cool, but if it does not, we have to code our own
data source control replacement (a titanic task) or work without data source controls
losing productivity. Even if the ObjectDataSource control fits in our way of doing
things, there are times when we would like to do little adjustments here or there in
order to fit our needs.
The ExtendedObjectDataSource package was designed with extensibility in mind so
we can adapt it or extend it in many ways to maximize our productivity and reusability.
Here is a basic class diagram of the design:
As you can see, if you inherit from the control you can replace the associated view
and the cache. If you inherit from the view you can implement any strategy for the
Select, Insert, Select and Update methods. The controls of the ExtendedObjectDataSource
package have 4 strategies:
- Using simple types without concurrency control.
- Using simple types with optimistic concurrency control.
- Using custom objects without concurrency control.
- Using custom objects with optimistic concurrency control.
Implementing a strategy is not a trivial job because there is a lot of things to do
inside a ExcuteXXX method. To help isolating the complexity and maximize reutilization
and extensibility the different strategies present in the control are implemented
using a substrategy for each different operation:
With the extensibility power of the ExtendedObjectDataSource package you can make
any data source control quickly using the base classes (in fact, the EODS control
inherits from the CODS control).
If you purchase the source code (more than 11500 lines of code and comments) you will
have a lot of helper methods for doing most
common operations to implement a custom data source control similar to the ObjectDataSource.
Advanced filtering and caching support
The ObjectDataSource control filtering support is minimal (only available if our
select method returns a DataSet, DataTable or DataView).
If we specify a FilterExpression and the FilterParameters,
, the FilterExpression is evaluated and the control sets the RowFilter property
of the underlying DataView, so the filtering is done by the DataView and not at the database level.
In the CODS and EODS there is a property called
FilterParameterName. When we set
that property the Select method that the control expects will need another parameter
that will be filled with the evaluated FilterExpression. This filtering does not
expect that the data returned from the Select method is a DataSet, DataTable
or DataView as it expects the select method to do the filtering. If we do not want
to use the advanced filtering, we just leave the FilterParameterName
property empty and the filtering will work like in the ObjectDataSource (Note that in the ObjectDataSource
something similar happens with the SortParameterName, but for the CODS and EODS
this behaviour is extended also for filtering). Also, there is a property called
IndividualFilterParameters that can be used to pass one strongly
typed parameter for each parameter in the FilterParameter collection
to the SelectMethod.
The ObjectDataSource can cache the data returned by the SelectMethod
even if it
is paged as long as we do not sort or filter our data. However, there are times
that this is not what we want so the EODS also supports saving cached data if filtering
and/or sorting are used.
DataObjectType instantiation control
When we use the ObjectDataSource to show, edit and update data (for example using
a GridView) and we are using custom objects for the Insert, Update and Delete method,
the ObjectDataSource instantiates a new object (from type DataObjectTypeName)
when the Insert, Update and Delete methods are called, and then the ObjectDataSource
sets its properties with the values read from the control using the ObjectDataSource.
If we show all the properties of the DataObjectTypeName type there is not problem. However, if
we do not want to show all the properties of the instantiated object in the control, the automatically
created instance of the DataObjectTypeName type will not have all the properties set,
so we can lose information when doing an update.
With the CompatObjectDataSource and the ExtendedObjectDataSource,
we have the option to supply a custom instance to the update method instead of creating
a new one, so the previous scenario will work if we supply an instance with the values read from
the database. Before calling the update method, the CompatObjectDataSource
and the ExtendedObjectDataSource raise the DataObjectCreating
event. To supply a custom instance, the ObjectInstance property
of the event arguments has to be assigned.
The importance of the AffectedRows
If we are using the ObjectDataSource control and our SelectMethod
has paging we
can not let the user edit data using a GridView without having to write some code
to handle events in the ObjectDataSource lifecycle. Let me elaborate... With paging
enabled, if we delete all the rows in the current page, the GridView disappears
instead of going to the previous page as we would probably expect. Why? Well, when
we delete a row in the
GridView, it calls the ObjectDataSourceView's
and after the deletion has been performed, a callback notifies the
that the delete operation was completed. The type of the callback is:
bool DataSourceViewOperationCallback(int affectedRecords, Exception
The first parameter, affectedRecords, plays a key role here. If the Delete operation
has affected one or more records, then the GridView will check if the current page
has any row displayed and set the page to one that has rows, before asking for fresh
By default the ObjectDataSource set AffectedRows
to -1, so, if we do not
explicitly set the affected rows we do not get the results we are expecting. To properly
set the affected rows we can handle the Deleted event and set the event’s AffectedRow
property. If we are using ADO.NET
in our data access layer we can make your Insert,
Update and Delete methods to return the number of affected rows because that is
will return. As we can access to our data access method return
value from the Deleted event, an easy way to handle the deleted event is:
e.AffectedRows = (int)e.ReturnValue;
This is very tedious and we can easily forget to do it. The EODS can automatically
assign the AffectedRows property to the ReturnValue
in the Insert, Update
and Delete operation if the control's property AutoAssignAffectedRows
is true and our method returns an int. This way we can have codeless pages that work properly.
The ObjectDataSource does not have any design time support for generics, and do
not support generic methods.
The EODS and the CODS have full
support for generic types and methods in design time and in runtime. See the design time wizard section
for more information about it. In the samples section you can find a sample that
ObjectDataSource Bug with "incompatible" cultures
If we use an ObjectDataSource with a GridView
and our data has properties that
are printed based on the current culture (decimals, DateTime, etc) we could be
exposed to serious problems if our current culture does not format the types the
same way the InvariantCulture does.
For example if we are using the spanish culture (es-ES) and our data contains a
decimal it will be printed like this:
and if we try to edit the row without changing anything we get the
Input string was not in a correct format. […] 1,00 is not a valid value for Decimal.
The problem is that the ObjectDataSource converts from string to the destination
type using the InvariantCulture instead of the current culture. The CODS and the
EODS use the current culture avoiding this problem.
As this is a serious bug in the ObjectDataSource control you can download a community version of the CODS that fixes this error. The community edition of the CODS is
like the commercial version but it does not allow extending it and caching is not
implemented. The other features of the CODS are present.
Chaging Parameters in the events
With the ObjectDataSource, If we use simple types as the parameters of the class
that performs the Select, Insert, Update and Delete operation, we can add, modify or delete the
parameters collection in the Selecting, Inserting, Updating and Deleting events. However, if we
are using custom types instead of simple types, we are not allowed to add or remove parameters
in the Inserting, Updating or Deleting events and you may wonder why. The ObjectDataSource does
not know the name of the parameter (or parameters if we are using optimistic concurrency
in the update operation) of our custom object. So before the Inserting, Updating
or Deleting event it uses reflection to find out the parameter names and the method
data, so if it the control lets us modify the parameter list, the information that
it has could be obsolete so it will need to use reflection again with the updated
parameter list to be sure that it is calling the proper method.
The CODS and the EODS does not have this limitation because the behaviour in this
case is not similar to the ObjectDataSource. We can add, modify or delete elements
in the parameter list passed to the Inserting, Updating and Deleting events but
we have to keep one consideration in mind: Our custom object parameter name in
the parameter list will be called "object" (if there are two parameters because we are
using optimistic concurrency they will be called "object" and "oldObject").
After the Inserting, Updating and Deleting event, the list will be modified and
the parameter name will be updated to match with our method's parameter name.
By default, if the ObjectDataSource is going to call an instance method
it will create an instance of the type specified by the TypeName property. However,
the instance will be created using the default constructor. If you want to use another
constructor, you have to handle the ObjectCreating event and write the
code to create the instance. The CODS and EODS have a
ParameterCollection called ConstructorParameters to
be able to call any arbitrary constructor without having to write code.
Design Time Wizard
A DataSourceControl performs two main tasks at design time:
- Provide a configuration dialog to quickly configure the data source.
- Provide type schema information to the control using the data source in order to
populate field pickers and to generate default templates based on the schema.
- The previous point forces us save the total row count to return it later, so the
Select method could not be static.
The configuration wizard exposed by the CompatObjectDataSource
is shown here:
The configuration wizard exposed by the ExtendedObjectDataSource
is shown here:
As you can see the wizard lets us configure the methods called by data source controls
in an easy way with just a few clicks and it also provides support to configure
paging, sorting, filtering and optimistic concurrency.
The configuration wizard has an option to avoid persisting the paging, sorting and
filtering parameters in the SelectParameters collection. This is
because some times, those parameters are filled automatically by the control using
the data source (for example, a GridView) and will be added automatically
by the data source control when calling the SelectMethod. If you
are using the CompatObjectDataSource, the SelectCountMethod
will be called with the parameters persisted in the SelectParameters
collection so if the paging parameters are persisted, the SelectCountMethod
will need two parameters that probably it will not use. That's why there is an option
to remove those parameters from the resulting collection.
The CompatObjectDataSource and the ExtendedObjectDataSource
also provide type schema information to the controls using them as their data source.
Because of that, the controls using those data sources can provide schema information
and generate templates based on the schema information provided by the data source
control as the next image shows: