asp.net List(of CustomObject) design - asp.net

I've got a page that will bind a List(of CustomObject) to a gridview. The twist is that 'CustomObject' could be one of 3 different types of object (CustObjA, CustObjB, CustObjC). The page will not know what type of objects it is going to display until after the postback occurs (after there is a trip to the database to retrieve the objects). I build my grid dynamically from the codebehind depending on what the current Object type is. I'm finding that my code-behind is beginning to need a lot of "if typeOf object is..." statements, and I'd like to get away from that. Maybe a List isn't the way to do it - looking for any suggestions. I'm using vb.net/vstudio 2010/webforms.
thanks.

Why not use inheritence so CustObjA, B, C have the same superclass or inteface?

Simply implement an interface, that gives you access to data and receive a list of objects of such type.

Related

NHibernate and ASP.NET Binding to a IList of <Products>?

I have recently started to use Nhibernate and i am quite happy with it until i needed to BIND to ASP.NET controls. I was having major issues binding a gridview to a collection of Products (IList). In the end i was forced to right a small routine to convert my IList to a DataTable. Once it was in datatable it worked flawlessy.
Now has come the time to bind a standard Dropdownbox to 1 field of a collection (IList) of Products but it appears i am having issues again.
So this has brought me to the conclusion that i must be doing something wrong?
I can't believe that it isn't possible to BIND ASP.NET controls to a collection (IList) of a class (in my case products) that is returned from NHibernate.
I would really appreciate any feedback anyone has on the situation... I am at a loss
Thank you
The problem is not that you can't bind, because you can. Generally issues like this come about when you're binding at the wrong time.
NHibernate supports laziness. So if your query is lazy, and properties on the returned objects are lazy, then the values won't be pulled from the database until the items and properties are referenced. If you bind these to controls in the UI, then the values won't be extracted until the page gets rendered.
At this point there is a good chance that you have already closed your database connection.
The simple solution is to make sure that the data you're binding to is not lazily loaded.
Create a List<T> or BindingList<T> object and pass the IList object from the query into the constructor. If the IList object is not a generic list, you can use LINQ, ilistObject.Cast<T>().ToList().

How to use FormView in order to insert complex entity framework objects

I'm trying to use formview in order to do insert of a new entity object (called Customer)
Customer has a reference to another entity called Address.
How can I fill both of them in the same formview?
Thanks.
After looking more into it, it seems like the problem is in the ConvertProperties method of EntityDataSourceView.
Using reflector I've found out that the problem was in the line:
PropertyDescriptor pd = propertyDescriptors.Find(str, false);
(this codeline takes the inserted property name, and convert it to a descriptor)
when str = "Address.Address1" the function returns null.
I've took a look at propertyDescriptors.properties and it seems like Address.Address1 doesn't exist. Only Address.ID and Address exist.
I took a look at the population of propertyDescriptors and it seems like there is no way to change that. Or in other words, it seems like there is no solution to the problem.
bummer.
Actually there is a solution. Flatten the two objects into one. Create a CustomerViewModel object that includes all of the fields of the two objects. Then at databinding bind to the CustomerViewModel.

Advice for Building a dynamic "Advanced Search" Control in ASP.NET

alt text http://img3.imageshack.us/img3/1488/advancedsearch.png
I'm building an "Advanced Search" interface in an ASP.NET application. I don't need SO to write this thing for me, but I'm stuck on a specific problem regarding dynamic controls and ViewState. I would like some direction for how to approach this. Here's my situation:
Ingredients:
A serviceable set of API objects representing entities, fields, and searches, which handles constructing a search, generating SQL, and returning the results. So that's all taken care of.
ASP.NET 3.5
Desired Interface Functionality:
(1) On initial page load, the interface gets a preconfigured Search object with a set of SearchCriterion objects. It binds them into a set of controls (see image above.)
Some search items are simpler, like:
Field (DropDownList) | Operator (DropDownList) | Value (TextBox)
Search Criterion controls for some field types have important information stored in viewstate, like:
Field (DropDownList) | Operator (DropDownList) | Value (DropDownList) where the "Value" dropdownlist is populated by a database query.
Some fields are lookups to other Entities, which causes a chain of field selectors, like:
Field (DropDownList) Field (DropDownList) | Operator (DropDownList) | Value
(2) The user modifies the search by:
Adding and Removing search criteria by clicking respective buttons
Configuring existing criteria by changing the Field, Operator, or Value. Changes to Field or Operator will require the control to reconfigure itself by changing the available operators, changing the "Value" input control to a different type, or adding/removing DropDownLists from the "Fields" section if Lookup-type fields are selected/unselected.
(3) Finally, the user hits "Search" to see their results.
The Problem:
As you probably already know if you're answering this question, controls added dynamically to the page disappear on postback. I've created a UserControl that manipulates the control collection and neatly accomplishes step (1) above as you can see in the attached image. (I'm not concerned about style at this point, obviously.)
However on Postback, the controls are all gone, and my Search API object is gone. If I could get the dynamically generated control collection to just play nice and stick in ViewState, I could examine the controls on postback, rebuild the Search object, then handle control events neatly.
Possible Solutions
I could make the Search object serializable and store it in viewstate. Then on page load I could grab it and reconstruct the control collection at page load time. However I'm not sure if this would play nicely with controls raising events, and what happens to the viewstate of Drop-down lists that contain data from the database - could I get it back? It's highly undesirable for me to have to re-query the database on every postback.
I could develop a custom server control (see this link) for this kind of thing... but that is a new topic for me and would involve some learning, plus I'm not totally sure if a custom server control would work any more nicely with non-fixed control collections. Anybody know about that?
I was thinking that I might be able to accomplish this using databound controls - for example I could bind my criterion collection to a repeater which has a fixed control collection (maybe hide the non-used "value" controls, use an inner repeater for the "Field" drop-down lists). Then all the information would stay in ViewState... right?
Any new ideas would be greatly appreciated.
thanks for your help.
b.Fandango
I've been coding for about a day and I got this working beautifully using the third option I suggested in my question - old-school databound controls. Actually I only thought of the idea when I was forced to write out the question in detail - doesn't that just happen to you all the time?
I put my SearchCriterionControl into an asp:Repeater and bound it to my object collection. For the Field Chooser I put an asp:DropDownList inside a nested asp:Repeater and bound the Field array to that. Everything works beautifully, keeps state, actually required very little code. So I never had to dynamically add controls to the page, thank goodness.
Thanks for your suggestions, Ender, Matt and andrewWinn.
Since no one else has taken a stab at this for 2 hours, I'll throw my hat in the ring with a solution that does not rely on viewstate at all (or the ASP.NET model of postbacks).
What if you grabbed all the input values with jQuery and instead of doing a post-back did a post against the page (or a new results.aspx page)? Or, you could make the entire thing asyncrhonous and do an Ajax request against a web method, get fed the results, and populate on the client side as needed?
The unfortunate thing here is you have to reconstruct which type of controls were used to figure construct your search query since that data wont be passed with the viewstate. But I imagine you were already going to have to do some kind of translation of your input data into a query form anyway.
Read here for more information about using jQuery to hit an ASP.NET page method. Remember - page methods must be static (it's an easy oversight).
I'm not sure what you're doing server side to construct your query - but I would highly recommend LINQ. I did a similar "advanced search" function previously, and after a few different attempts found that LINQ was a wonderful tool for this problem, regardless of whether I was hitting SQL with LINQtoSQL or just hitting an in-memory collection of objects.
This worked so well because 1) LINQ is deferred execution and 2) A LINQ query returns another queryable object. The implication here is that you can chain your LINQ queries together as you construct them from your input, instead of having to do a single massive clause translation to SQL or whatever backstore you are using (one of my attempts was constructing SQL clauses with strings, but still passing input data via SQLParameters for SQL injection protection - it was messy and complicated when hand crafted LINQ was orders of magnitude easier to understand and implement).
For example:
List<string> data; // or perhaps your a DB Context for LINQtoSQL?
var query = data.Where(item => item.contains("foo"));
if( {user supplies length search option} )
query = query.Where(item => item.Length < 5);
// etc, etc.
// LINQ doesn't do anything until the query is iterated, at which point
// it will construct the SQL statement without you worrying about details or parameter binding
foreach(string value in query)
; // do something with the results
Because of deferred execution and the queryable return type, you can concatenate LINQ queries to this expression all day long and let it worry about the implementation details (such as converting to a SQL query) at execution time.
I can't provide you with the exact steps that you will need to do, but I HIGHLY suggest looking into asp.net page life cycle. I created a user control as a DLL one time. I had to capture postback data at specific steps in the lifecycle and recreate and rebind the data at other steps. Additionally thinkgs like viewstate are only available at certain points also. I know that I had to override On_init, On_prerender and some other methods.
Sorry I couldn't be more help, but I don't have the code with me (its with an old employer). I hope this helps.
If you are adding controls to the controls tree dynamically, you need to add them on postpack as well. Just call the method that builds the control on Page_Load or Page_Init and the controls should stay on the page on postback.

ASP.Net: Is it possible to skip databinding of an element if an error occurs?

I use a lot of repeaters for different elements of our sites, and I've always wondered if there was a way to have the repeater skip an element if an exception occurs instead of having the whole page crash?
In particular, I've inherited a system from another developer that using a similar design, however he didn't include any kind of validation for his business objects, and it a single property is missing, the whole thing goes up in smoke.
Any suggestions?
Thanks in advance!
The simplest suggestion I can offer is the check the validity of the data before it's passed to the repeater. I don't believe there's any way to get the stock repeater to skip a data element on error.
The other approach is to build your own repeater, inheriting from the base Repeater, to add that functionality but I've no sample code to offer. Perhaps someone else may be able to help there.
The way I see it, you have at least three options.
You could create a custom repeater control that inherits System.Web.UI.WebControls.Repeater and override the databinding behaviour to be more try-catchy (probably fail silently on databinding errors). You couldd then easily replace all instances of the standard Repeater with this new one.
You could filter your datasources before databinding to remove items you know are going to cause problems beforehand. This option may be quite laborious and something of an iterative process.
You could try adding default values to the business objects, so that the properties you're binding to return a default instance rather than null (not nice either).
That's my thoughts anyway.
One question - you say "when a property is missing". Do you mean he's using a style of databinding syntax that offers no compile-time checking and is referencing properties that don't exist, or is referecing properties that are null?
Edit
OK, so you're referencing properties that are null. If you have access to the code for the business objects you could modify them so they return a new, non-null instance (this is the third option I gave).
You don't say if you're using .net 3.5, but I'll assume you are. You could add a new property "IsValidForDataBinding" on to each of your business objects. In the getter logic you could check each of the necessary properties and sub-objects to check for validity, non-nullness etc and return a bool. When you come to bind your repeater, write a simple linq statement that filters-out the invalid items (i.e. where IsValidForDataBinding = false). Having said that, I still think that writing a derived repeater control could be your easiest option.
Have you tried using string.isnullorempty("the string") to check for a value before referencing the property?
Here's a reference: MSDN

Gridview sorting challenge when moving from Winforms to ASP.NET 2.0 Webforms

I have a problem with Gridview sorting that is similar to others but I'm binding to a collection object as opposed to a data table.
The existing business rules and data access layers of an application follow the pattern of having an object and, if you need a collection of objects of that type, to have another class inheriting CollectionBase and implementing IBindingList.
For desktop applications, it was easy to databind a gridview to one of these objects and there weren't any problems with turning on column sorting. Everything was 'in state' in the desktop app's presentation layer.
Now that code is being moved to a new web application (ASP.NET 2.0, VB codebehind pages).
I've played around with what I had to do to only have certain columns of the collection show up in the gridview and the gridview looked pretty good. When I turned on 'allow sorting', that's when the problems showed up.
I'm getting the error about not having a .Sorting method, etc. In researching this, I found all sorts of solutions that were easily implemented with dataviews if my source was a data table. But it's not - it's a collection. I tried to "cheap shot" a datasource by converting the collection to an XML memory stream and them trying to .ReadXML back into a dataset but that didn't work [Root element is missing error was as far as I got in the dataset.ReadXml(ioTemp) where ioTemp was the System.IO.MemoryStream used in the xml serializer].
Because of the old desktop apps, I've never had to worry about sorting a collection since the gridview handled it once it was loaded. In fact, it's a 'standard' that the collection's .SortProperty, .SortDirection and .ApplySort all through NotSupportedExceptions (I inherited this code from programmers long gone).
Is there an easy way to convert the collection to a data table or a way to sort the collection without having to go back to the database each time? Object Data Sources won't work becuase of the intricate rules in how the objects are built - the wizards in VS2005 just can't handle what we need to do (grabbing data from several tables conditionally to make an object).
Thanks in advance.
Have you considered client side sorting instead?
I have used the jquery tablesorter plugin in the past with ASP Gridviews.
http://tablesorter.com/
I had a similar issue and i needed to implement IComparable on the objects. Basically to sort a collection of objects you need a way to distinguish their order. The IComparable interface has one method called Compare which allows the .Net framework to work out the order of the objects when you sort them. You need to implement this method yourself to get the sort method to work.
Google results
You don't mention the error message so i cant be sure if this is the case, can you post the error?
EDIT :
In regards to your comment; you can implement multi column sorting, it just requires more work. You can specify the fields to sort the collection by and then use this information within the CompareTo Method.
Have a look at this
Given that you apparently are populating the grid with a collection of your own objects, this sounds like a perfect job for Linq for Objects. With just a little elbow grease you can achieve what is effectively an SQL Select statement against your collection. Very cool stuff.
http://www.hookedonlinq.com/LINQtoObjects5MinuteOverview.ashx
Also, do you really just want to sort the data in the grid? If so, then'd definitely pursue using Linq against your objects. However, rarely does sorting the contents of the grid really answer the problem ("sorting the grid" usually translates into changing the access path of the data used to fill the grid.) Browser apps aren't like Windows apps and don't have a full-time connection to the underlying data source to make things happen quite as magically as the DataGridView in Windows makes things seem.
You can put link buttons with an On_Click event as the header's of each column.
When the event is triggered, figure out which header was clicked on (one method per header or a commandArgument value). Once that is know, do a .orderBy or .OrderByDescending by on the collection of objects, and put the result back in as datasource of the gridview and databind on that.
In the year since I originally asked this question, I managed to get a new 'standard' implemented so that collections of business objects were now generic lists.
So now a "Collection class" that is little more than a "Inherits List(Of MyBusinessObject)" with a Sort Method that looks like this (performance wasn't an issue):
Public Overloads Sub Sort(ByVal strPropertyName As String, ByVal strDirection As String)
Dim arSortedList As New ArrayList
For Each item As MyBusinessObject In Me
arSortedList.Add(item)
Next
arSortedList.Sort(New CaseInsensitiveComparer(Of MyBusinessObject)(strPropertyName, strDirection))
For intI As Integer = 0 To arSortedList.Count - 1
Item(intI) = arSortedList(intI)
Next
End Sub
This seemed to work perfectly with the methodology used by the GridView for firing events.

Resources