Skip navigation

Cache In Using the ObjectDataSource Control

Object Instantiation and Data Caching

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 2.0

 

Cache In Using the ObjectDataSource Control

Object Instantiation and Data Caching

 

By Dino Esposito

 

One of the hot new features of ASP.NET 2.0, data source controls are far from being the panacea of all data access pains. First and foremost, data source controls are not the only way to bring data into an application. A data source control is just one of many controls in the ASP.NET toolbox, and should be used when it proves to be the right tool for the job you have to do.

 

A data source control exists as a counterpart to a data-bound control, so that the process of binding can be run more intelligently and with less glue code. My personal philosophy is that you should stop using data source controls as you start facing difficulties. Either make it easy or use nothing. As with many emphatic sentences, this one also has some truth to it but it should not be taken literally, either. Basically, data source controls are complex and sophisticated mechanisms; as such, they are delicate at times, and breaking them from code might be easier than you expect.

 

In this article, I ll bring the ObjectDataSource control to the forefront and delve into some of its little-known features regarding object instantiation and data caching.

 

Quick Refresher

A data source control is mostly a wrapper around a data-source-view class. In general, a data source control represents one or more named views of data and each view manages a collection of data. All built-in data source objects, though, implement only one default view. However, this is the exception. A data source view manages its data through SQL-like operations such as SELECT, INSERT, DELETE, and UPDATE. Optionally, a data source view can implement a number of extra capabilities, such as sorting and paging. In ASP.NET 2.0, data source controls come in two flavors: tabular and hierarchical. The table in Figure 1 details all default data source controls.

 

Class

Description

AccessDataSource

Connects to an Access database and uses the Jet 4.0 OLE DB provider to operate.

ObjectDataSource

Connects to a custom .NET business object that returns data.

SiteMapDataSource

Hierarchical control; connects to any provider that supplies site map information.

SqlDataSource

Connects to an ADO.NET data provider that returns relational data. You specify the name of the provider and the connection string through properties.

XmlDataSource

Hierarchical control; connects to XML data with or without schema information.

Figure 1: Details of the default data source controls.

It is essential to know that SqlDataSource is not a data source control for SQL Server data. Instead, it can manage any relational data obtained through a variety of ADO.NET data providers, including those for OLE DB and ODBC data sources.

 

ASP.NET 2.0 data-bound controls have been refactored to support binding through data source controls. In particular, all standard data-bound controls support binding through data source objects; only new data-bound controls, such as GridView and DetailsView, though, support two-way data binding via data source objects. To exemplify, you can populate a DataGrid or a Repeater using a data source control. You can t edit in-place the contents of a grid row, though. For this to happen, you must upgrade the DataGrid to the GridView control.

 

ObjectDataSource and Your BLL/DAL

The key difference between the two controls is in the level of abstraction they support and require. There s virtually no data abstraction with SqlDataSource. You must indicate connection string and command text in ASP.NET pages. This might be acceptable for short-lived, quick-and-dirty pages, but it is hard to push for an enterprise-level application with its own business logic (BLL) and data access layer (DAL). ObjectDataSource encapsulates data access details and security in a set of objects most likely your existing BLL/DAL.

 

Objects you plan to use with ObjectDataSource need to meet a number of requirements (so you can t plug any objects in). In particular, the object must expose a default parameterless constructor, be stateless, and have methods that easily map to select, update, insert, and delete semantics. In addition, the object must perform updates one item at a time as batch update scenarios are not explicitly supported. However, it is essential to note that such a requirement applies exclusively at the API level. In other words, your object must have an Update method that doesn t require any further action to persist changes. This fact alone doesn t prevent you from using batch update policies in your DAL. If your DAL does use batch update policies, then it is up to you to trigger the extra tasks that will actually move updated records from memory to disk. This can be done either adding some UI (for example, a Save to DB button) or scheduling a background service that does that periodically.

 

The bottom line is that managed objects that work well with ObjectDataSource are designed with this data source class in mind.

 

It is not uncommon and, indeed, recommended that you design your BLL/DAL independent from the ObjectDataSource control. When you have a middle-tier that completely satisfies your requirements, you start reasoning about ways to connect it to an ObjectDataSource. In doing so, you typically face two issues: adapting the API and deploying the BLL/DAL.

 

Because ObjectDataSource has expectations on the API of the bound class, it might happen that objects in your BLL/DAL do not comply with the ObjectDataSource rules. For example, mapping CRUD operations on ObjectDataSource to methods in the class might not be immediate. The pattern Adapter comes to the rescue. This pattern helps when class A and class B must communicate, but lack matching interfaces. In this case, you create a class C that wraps class A into an interface that class B understands.

 

The same pattern is helpful when you deploy your middle-tier and the ASP.NET application on separate servers. ObjectDataSource can t directly call into a remote object. Once again, you wrap the bound object in a local proxy object that uses .NET Remoting, WCF, Web services, or whatever else to connect to the BLL/DAL. Figure 2 illustrates this scenario.

 


Figure 2: Using ObjectDataSource with a remote/firewalled object.

 

Object Creation

The bound BLL/DAL class is instantiated each and every time the ObjectDataSource control is created; that is, for each page request. For each page request, the BLL/DAL class goes through the standard cycle: create, use, dispose. As you can see, no state is automatically maintained except any state the class itself saves internally to persistent media, such as files or databases. If the BLL/DAL class has static methods, some state can be maintained through static members. In this case, though, be prepared to handle possible concurrent access to these members.

 

The ObjectDataSource control first looks for the default constructor of the class. If such a parameterless constructor can be found, then the object is created, consumed, and disposed of. Otherwise, ObjectDataSource fires a server-side event (ObjectCreating):

 

private void OnObjectCreating(object sender,

 ObjectDataSourceEventArgs e)

{

   // i.e. Need to use a non-default ctor

   CustomerManager cm;

   cm = new CustomerManager( ... );

   // Pass the object on

   e.ObjectInstance = cm;

}

 

By handling this event, the page code can work around a couple of relevant issues. First, the event handler can use any non-default constructor the BLL/DAL may have. Second, you can retrieve a valid class instance from an internal cache; for example, the ASP.NET Cache object. In this way, you create just one instance of the BLL/DAL object and reuse it across requests. Just the repeated creation and disposal of a class instance, in fact, might affect the overall performance of the system.

 

The ObjectCreating event is fired to give the page a chance to manually create the instance. When the event returns, ObjectDataSource checks the ObjectInstance property of the event argument class. If the property is not null, ObjectDataSource will use the instance returned by the event instead of creating an instance on its own. The ObjectCreated event is also fired later to let the page author access any public members of the object and perform additional initialization.

 

It is worth noting that the behavior of ObjectDataSource is slightly different if static methods are used. Actually, one of the first tasks that ObjectDataSource performs after initialization is discovering information about the method to invoke. Static methods are invoked immediately; otherwise, an instance of the BLL/DAL object must be created first, either via the default constructor or the ObjectCreating event. In any case, as it is fairly easy to guess, methods and constructors are invoked via reflection. When done, ObjectDataSource signals it is going to free up the BLL/DAL object. It does that through the ObjectDisposing event. If you re going to reuse that instance, simply cancel the dispose operation, as shown in Figure 3.

 

private void OnObjectCreating(

 object sender, ObjectDataSourceEventArgs e)

{

 // Check if an instance already exists in the Cache

 CustomerManager manager = Cache["CustomerManager"]

  as CustomerManager;

 if (null == manager)

         manager = new CustomerManager();     

 e.ObjectInstance = manager;

}

private void OnObjectDisposing(

 object sender, ObjectDataSourceDisposingEventArgs e)

{

 // Get the business object and check if it

 // exists already in the Cache

 CustomerManager manager = e.ObjectInstance

  as CustomerManager;

 CustomerManager temp = Cache["CustomerManager"]

   as CustomerManager;

 if (null == temp)

     Cache.Insert("CustomerManager", manager);

 // Cancel, so that the object won't be disposed

 // if it implements IDisposable

 e.Cancel = true;

}

Figure 3: Caching BLL/DAL Objects.

 

Caching Data

As mentioned, ObjectDataSource doesn t automatically cache the BLL/DAL object across requests, but it can optionally cache the results of the Select method the data to return to data-bound controls. For this to happen, you only have to set up a few properties: EnableCaching, CacheDuration, and CacheExpirationPolicy. You don t need to modify your business and data layers to enjoy caching, because caching takes place one level up (at the ObjectDataSource gate). Basically, when the data-bound control invokes the Select method, ObjectDataSource first checks if there s any valid data in the cache. If not, it serves the request by invoking the back-end object. Otherwise, the request is served with existing data and your BLL is happily bypassed.

 

When you set the EnableCaching property to true (the default is false), the data returned by the Select method are stored in the ASP.NET Cache object and reused over and over again until it expires. CacheDuration and CacheExpirationPolicy properties let you control the expiration of data. In particular, CacheDuration sets for how long the results of the Select method should stay cached. You assign CacheDuration a time (in seconds). CacheExpirationPolicy, on the other hand, defines the expiration policy with respect to the duration. If set to Absolute, then the data in the cache becomes obsolete after the specified period. If set to Sliding, instead, each successful access to the cached data automatically renews the caching period. For example, in case of sliding expiration and a duration of 30 seconds, data gets stale if not accessed for more than 30 seconds. In case of absolute expiration, instead, data gets stale 30 seconds after storage, no matter how many times it has been read meanwhile.

 

ObjectDataSource creates an entry in the ASP.NET Cache to store data. A unique entry is created for every combination of CacheDuration, CacheExpirationPolicy, TypeName, SelectMethod, and SelectParameters values. This means that multiple ObjectDataSource controls in the same application will use the same data if they connect to the same BLL/DAL object and call the same method with the same parameter with the same caching settings.

 

An application that uses cached data returns data generally faster than an application that has to hit the database each time. However, with caching enabled there s no guarantee that data you serve is up to date. The first consideration to make is, are you really sure you can t afford caching data for just a few seconds? If you set up a cache duration of 5 seconds, it means that users will see data with a maximum delay of 5 seconds. Are you (and your users) really sure this violates requirements? How many requests can you serve much faster in, well, 5 seconds of server time?

 

The second consideration relates to the meaning of the data. Imagine a scenario where you cache relatively static data for a long time; for example, customer information or product descriptions. No matter for how long the data is fixed, you might want to see changes show up soon. In this case, you might consider adding a dependency mechanism that invalidates cached data when real data in the back-end data store changes.

 

The CacheDependencyKey property indicates a user-defined cache entry that is linked to the ObjectDataSource entry that contains cached data. By programmatically changing the value in the user-defined key, you invalidate any cached data so that the next request will be served going back to the database and return fresh data:

 

// Invalidate programmatically any cached data and refresh

Cache[ObjectDataSource1.CacheKeyDependency] = new object();

GridView1.DataBind();

 

If cached data come from a database, you can also set up a direct dependency between cached data and a database table. The SqlCacheDependency property of the ObjectDataSource control binds the cached data to changes in the specified table of the specified database. You indicate the database using the DatabaseEntry:Table format.

 

Finally, keep in mind that ObjectDataSource allows you to cache any types of data, including DataTable and custom collections. However, you should not cache objects that retain resources or state that cannot be shared to service multiple requests. Want an example? You should never, ever, cache an open SqlDataReader object.

 

Conclusion

In ASP.NET 2.0, all data-bound controls support two binding mechanisms. One is the classic binding also available in ASP.NET 1.x and done through enumerable data source objects. The second mechanism relies on data source controls. Data source controls are regular server controls with no user interface that intelligently cooperate with the data-bound control trying to anticipate the next user s request. ObjectDataSource is the most interesting of the data source controls because it can bridge your presentation layer to the BLL/DAL, even remotely. The ObjectDataSource control takes advantage of existing tiers and, overall, promotes a domain-based design over a purely tiered design. In this article, I focused on some programming aspects of ObjectDataSource. I ll delve into more in future columns. Stay tuned.

 

Dino Esposito is a Solid Quality Learning mentor and the 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