ManuelAbadia.com
ASP.NET components
Navigation
Register or Login

ExtendedObjectDataSource Features

The ExtendedObjectDataSource package is composed of 2 controls that are a replacement of the ObjectDataSource:

  • 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:

SelectCountMethod

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 application.
  • 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 the SelectMethod (an output 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.

Extensibility

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:

class diagram

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:

class diagram

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 Delete method and after the deletion has been performed, a callback notifies the GridView that the delete operation was completed. The type of the callback is:

public delegate bool DataSourceViewOperationCallback(int affectedRecords, Exception ex);


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 data.

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 what ExecuteNonQuery 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:

protected void DataSourceDeleted(object sender, ObjectDataSourceStatusEventArgs e)

{

        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.

Generics support

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 uses generics.

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:

1,00

and if we try to edit the row without changing anything we get the following error:

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.

Constructor Parameters

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:

CompatObjectDataSource - first wizard panel CompatObjectDataSource - second wizard panel

The configuration wizard exposed by the ExtendedObjectDataSource is shown here:

ExtendedObjectDataSource - first wizard panel ExtendedObjectDataSource - second wizard panel

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:

CompatObjectDataSource - Schema information