I’m building a booking system at the moment and have tried to use ObjectDataSource controls as much as possible to keep things nice and clean with respect to architecture, as controls goes however the ObjectDataSource is not without its fair share of ‘gotchas’.
One such gotcha I discovered was in relation to passing ListBoxes in as parameters to an ObjectDataSources Select method (I assume it’s the same for Insert/Update/Delete methods). There seems to be no direct way to do this. I tried a lot of variations for a) defining the ListBox based parameters for the ODS and b) defining the corresponding method signature for the select method.
<asp:ControlParameter ControlID=”lstArea” Name=”areaSelection” PropertyName=”Items” />
<asp:ControlParameter ControlID=”lstArea” Name=”areaSelection” PropertyName=”Items” Type=”Object” />
The first parameter above requires a select method which has a parameter called areaSelection and is of type string or string, which will compile fine but will always only contain the first selection in the listbox which for a multi select listbox is useless.
When I selected ‘Items’ as the PropertyName property and had the type empty or as Object I made a little bit of progress. In this case the ODS passed a ListItemCollection to the select method. I was actually able to read all the items in the collection from within my business object select method however I kept on getting a run time error:
Type ‘System.Web.UI.WebControls.ListItemCollection’ in Assembly ‘System.Web, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ is not marked as serializable.
when the page tried to load (after the ODS had done it’s work). Not sure why this is happening but it might be to do with the fact that your not really supposed to be using anything from the UI.WebControls namespace in classes.
Passing ListBox selected items into your business objects via ObjectDataSource Selecting event
I gave up declaratively trying to add a ListBox associated parameter into my ODS (and thus indirectly into my business object method) and decided to hook into the Selecting event of the ObjectDataSource class instead and see what could be done programmatically. The Selecting event fires just before an ObjectDataSource calls your BLL select method. In it you can access an InputParameters dictionary object which allows you to add/edit and remove parameters before the actual call to your BLL is made.
Your declarative parameters do not have to match your method signature (this will compile in Visual Studio) but you’ve got to ‘fix’ your params in the Selecting method otherwise you’ll get an exception. The method signature of your select method must correspond to your ODS final parameters after you’ve made changes in the Selecting event. Here’s what my Selecting method looks like
Debug.WriteLine(temp.ToLocalTime()); //System.Diagnostics required
StringCollection areaSelections = new StringCollection();
for (int i = 0; i < lstArea.Items.Count; i++)
Debug.WriteLine(”selected? : ” + lstArea.Items[i].Selected.ToString());
Debug.WriteLine(”params = ” + e.InputParameters.Count.ToString()); //will output 2
I wrote the date (including seconds) so I can easily tell one button press output from another. Instead of trying to have my business object work with a ListItemCollection (which the purists will tell you is wrong and is perhaps why it didn’t work) I simply looped through the ListBox control directly and added any selected values to a StringCollection (System.Collections.Specialized) object called areaSelections. I then simply added the areaSelections StringCollection to the ODS input parameters using the key ‘areaSelections’.
My BLL select method must now have as input a parameter named ‘areaSelections’. Its type can be Object (as everything inherits from Object) in which case you’ll have to cast it to use it or it can be passed in as a StringCollection directly. My select method signature is:
//contact DAL to actually get members
//use areaSelections… do what you want!
In this case the parameter named ‘name’ has been declaratively added during design time using Visual Studio.
The problem with this approach is that the Selecting method only gets called when the ODS deems it necessary to actually go back to the DB to get a fresh load of the data. If the ODS is using caching and the relevant data is in the cache it will not get called but perhaps more relevant in this case is the definition of ‘relevant data’. ODS only seems to look at data associated with your declaratively defined parameters and not the data associated with controls not tied to parameters.
In my case I’m seeking to filter members by their name and/or location. I have declaratively assocated a parameter (named ‘name’) with a TextBox control which the ODS will examine on postback and if it changes it will go back to the DB (thus calling Selecting in the process). The ListBox however is not associated with a parameter so it is not on the ‘radar’ of the ODS. This means that if I was to change the items in the ListBox and then cause a postback by pressing a search button (for instance) the ODS will not see any change to the existing view (assuming I didn’t change the name textbox) and thus will not go back to the DB and refresh the data. What I did to circumvent this was to explicitly call the Select() method of the ODS in its Load event. This means data will be retrieved on every postback regardless of caching settings and changes to controls. Obviously this is not great but so far I’ve been unable to determine a better way, even storing the selected values of the ListBox as a string in a hidden field and associating that control with a declaratively defined control didn’t help much.
I’d love if anyone could share a better way of dealing with ListBoxes in the context of the ObjectDataSource control.