søndag, januar 14, 2007

Using the ObjectDataSource with Singleton and/or Interface instances

When using Visual Studio 2005 to configure the ObjectDataSource, you will only be presented the option to bind against classes that implement the class System.Web.UI.WebControls.ObjectDataSource. The ObjectDataSource will pr. default create its own instance of your ObjectDataSource class using reflection and use this that instance to retrieve objects that are to be bounded to you data control (for example the GridView).


As a consultant I often see developers having their own data retrieval classes, but when they want to use them in an ObjectDataSource, the class is wrapped in another class implementing the System.Web.UI.WebControls.ObjectDataSource only to satisfy Visual Studio. This leads to twice the complexity – and replication of code. The technique cowered in this blog post might be trivial to some, but I believe that the point cannot be made clear enough.


A better approach is to configure the ObjectDataSource tag to make use of the already present data retrieval class and tell the ObjectDataSource to use exact the instance of you class that you want it to use. This can be necessary if you have decided to only expose interfaces to you client, or if you data access class is implemented as a GoF Singleton or Factory design pattern (thus not letting the ObjectDataSource create an instance of your data retrieval class).


<asp:ObjectDataSource ID="_transactionData" runat="server"
    
TypeName="MailMonitorServer.Locator.ITransactionLocator"
    
DataObjectTypeName="LinkTrackerServer.ITransaction"
    
SelectMethod="Find" 
    
OnObjectCreating="ObjectDataSource_ObjectCreating" 
    
OnSelecting="ObjectDataSource_Selecting" 
    
EnableCaching="false" >
</asp:ObjectDataSource>


This code shows how I use the ITransactionLocator to locate transactions. The thing to notice here is that the ITransactionLocator (TypeName attribute of the ObjectDataSource) is in fact an interface (we cannot create instances of it from the client application) and the only way of getting an instance of this interface is through a Singleton pattern (more about that later).


The DataObjectTypeName defines that the type of the objects that are returned by the ITransactionLocator are of type ITransaction. In order to instruct the ObjectDataSource to use exactly your instance of the ITransactionLocator, and therefore not use reflection to create its own instance, I hook into the OnObjectCreating event.


protected void ObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) {
    
e.ObjectInstance = InstanceHelper.Instance.TransactionLocator;
}


Here I assign the singleton instance of my locator class to the arguments ObjectInstance property. This way the ObjectDataSource uses only this instance.


In this case the ITransactionLocator’s Find method needs a special parameter (not covered by the standard <selectparameter> tags. To customize the parameter sent to the Find method I hook into the OnSelecting event.


protected void ObjectDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e) {
    
IDealer dealer = (IDealer) Session["dealer"];
    e.InputParameters.Add("account", dealer.Account);
}


In this manner I also have complete control of the parameters that are sent to my ITransactionLocator.


I hope this little code will give you a new idea of now flexible the ObjectDataSource implantation really is.

5 kommentarer:

Anonym sagde ...

Too bad you loose design time support when you do this

Anonym sagde ...

This post was exactly what I was looking for, and I mean it literally: I googled "ObjectDataSource Singleton"

You've saved me a lot of time, thank you for sharing this!

Anonym sagde ...

thanks!

Anonym sagde ...

great explanation, thank you so much! i had exactly the problem with singleton pattern and found the solution here :)

Anonym sagde ...

Good work.
I've found a problem if the interface used for the factory inherits from another (ex. share same factory structure for more objects). In this case ObjectDataSource throw not found method exception, probably because reflection used by ODS doesn't check inherited memembers.