Can't databind an IEnumerable of DataRows? HttpException? - asp.net

I have a function that returns a DataTable that I can databind to a DropDownlist or Repeater just fine. However, if I databind an IEnumerable of the DataTable's DataRows, I get an HttpException: "DataBinding: 'System.Data.DataRow' does not contain a property with the name 'some_column'".
repeater.DataSource = ThisReturnsDataTable(); // Works fine
repeater.DataSource = ThisReturnsDataTable.AsEnumerable(); // HttpException
Why is this?
I'm not looking for a solution to the problem, like for example:
repeater.DataSource = ThisReturnsDataTable().AsEnumerable().Select(
x => new {some_column = x["some_column"]});
I simply want to know why the databinding with an IEnumerable of DataRows fails.

I found a pretty good explanation here, although his first solution to the problem, AsDataView(), doesn't seem to work/exist (in 3.5, at least). CopyToDataTable() works swimmingly, though.
.Net DataTables can be very useful when writing data-driven
applications. However, they have one limitation: There is no obvious
way to databind a grid (or other control) to an arbitrary list of
datarows from a table. You can bind to an entire table directly by
setting a DataSource to the DataTable itself, and you can bind to a
subset of a table by creating a DataView with a filter.
In general, you cannot bind to an IEnumerable (eg, a LINQ query);
the databinding infrastructure can only handle an IList (non-generic)
or an IListSource. This is true for any kind of datasource.
Therefore, to bind to any LINQ query, you need to call .ToList(). (Or
.ToArray())
However, when binding to a DataTable, you can’t even use a
List. If you try, you’ll get four columns (RowError,
RowState, Table, and HasErrors) and no useful information. This
happens because the List doesn’t tell the databinding
infrastructure about the special properties of the DataRows. To
understand the problem, some background is necessary
Databinding is controlled by the ListBindingHelper and TypeDescriptor
classes. When you bind to a list, the
ListBindingHelper.GetListItemProperties method is called to get the
columns in the list. If the list implements the ITypedList interface,
its GetItemProperties method is called. Otherwise, it will use
TypeDescriptor to get the properties of the first item in the list.
(this uses reflection)
The DataView class (which DataTable also binds through, using
IListSource) implements ITypedList and returns
DataColumnPropertyDescriptors that expose the columns in the table.
This is why you can bind to a DataView or DataTable and see columns.
However, when you bind to a List, there is no ITypedList that
can return the columns as properties. It therefore falls back on
reflection and shows the physical properties of the DataRow class.
To solve this issue, you need to wrap the list in a DataView so that
you can take advantage of its ITypedList implementation. You can do
that using the AsDataView() method. This method is only available on
the DataTable and EnumerableRowCollection classes; it cannot be
called on an arbitrary LINQ query. You can only get an
EnumerableRowCollection by calling special versions of the Cast,
OrderBy, Where, and Select methods from a DataTable.
Therefore, you can databind to a simple LINQ query by calling
AsDataView() on the query. To bind to a List, or to a more
complicated query, you can use an ugly hack:
List<DataRow> list = ...;
grid.DataSource = datatable.AsEnumerable()
.Where(list.Contains)
.AsDataView();
The AsEnumerable() call is not needed for typed datasets.
You can also call CopyToDataTable(), which will works [sic] on an arbitrary
IEnumerable. However, it makes deep copies of the rows, so
it isn’t helpful if you want the user to update the data, or if you
want the user to see changes made (in code) to the original datarows.
From: http://blog.slaks.net/2011/01/binding-to-lists-of-datarows.html

I could be wrong, but I believe instead of doing the .Where clause, you should be able to do something like this:
DirectCast([datatable].AsEnumerable, EnumerableRowCollection(Of DataRow)).AsDataView()

Related

How to bind to a control within an ASP.NET ListView

I have a list that I need to bind to a List I get from an API. The list looks like this:
struct DataItem { int level; string name; Guid key };
List<DataItem> myList = API.GetList();
ListView1.DataSource = myList;
ListView1.DataBind();
All this works fine for display. However, the table must edit the level value. I am unsure how to make that happen. I have tried event handlers on the listView, but they are never called. I have tried a text box for the level field (with both Bind and Eval) and an event handler OnTextChanged, but the event handler is never called. (I have tried with various combiniations of AutoPostBack and ViewState enabled.)
How can I programatically edit this data structure?
Two way data binding you are trying to implement here won't work like this - List doesn't implement INotifyPropertyChanged (someone correct me if I'm wrong).
You may consider using a plain old DataTable which can be two-way-bound out-of-the-box. If performance is not a highly critical issue, converting your List to a DataTable (and back, depending on what you want to do with the modified data) is simple enough, rather than struggling with custom implementations of list types.

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.

Data binding support in O/RM tools, how to detect

I want to verify that a generated class (single entity or collection) from an O/RM tool is data binding compatible.
I read that supported data binding types in WCF are: one time, one way, two way, one way from source in WCF. But how about "old school" .NET 1.1 data binding ?
It looks kind of difficult to check in code what kind of data binding support there is. You also have difference in runtime and design time data binding support. When reading some webpages I read different kind of implementations: implement IList, IComponent, INotifyPropertyChanged, IBindingList.... pffffff I don't know exactly where to look for...
You can databind to virtually any class. Let's imagine you create a very simple class, with a few properties, say for instance, Person with a Name and Age. I am talking about a plain simple class with absolutely nothing fancy about it.
If you create an instance of Person, you can do several things with it, and I will assume you are working with Windows Forms, but this mostly applies to other frameworks:
- You can bind its properties to properties of controls.
- You can bind it to datagrids, lists, etc. In the former case you can set mappings of which properties bind to which columns. In the latter, which property is displayed in the list, which property is the value selected by the user.
- Even better, you can bind it to a bindingSource.
Binding a single instance to a grid or a list isn't that useful, so what usually is done is that you create a list of instances and bind those to the grid. Even more correct is to bind the list to a bindingsource and the grid to the bindingsource also.
You can see a good article here about how to do all this.
Now, about all the interfaces you mention, they all only add more value to the databinding experience. Let's talk about a few of them.
INotifyPropertyChanged. Person is not less "databindable" than any other object if it does not implement this interface. What instances of Person are not able to do, however, is notify the controls their properties are bound to that the latter have changed. Try this: Bind the Name property of a Person instance to the Text property of a TextBox. Create a button that when clicked changes the value of that instance's Name. You will see the TextBox does not update after clicking the button. If, on the other hand, you implement INotifyPropertyChanged and have the setter of the Name property raise the PropertyChangedEvent that is defined by the interface, after repeating the experience, you will see that the textbox is updated automatically.
IEnumerable. If instead of a single Person, you want to databind not to a set of people, you can create a list of people and databind to that list. Let's take for instance, List lst = new List(); How do the databinding controls like datagrid, bindingSource, etc., know you want to bind to a set of Person(s) and not to the properties of lst itself? It is because List implements IEnumerable. So, whenever you bind these controls to an instance of anything that implements IEnumerable, the controls know that you intend to bind not to the properties of the list, but to the instances the list refers to. How do they know the type of objects the list contains? To be more generic and support any type of IEnumerable implementation, they just check the type of the first element in the list and assume all others are equal.
IBindingList: Even if Person implements IPropertyChanged, if you group instances of Person into a List bind that list to a control and, by code, change the value of a property of one of the instances, you will see nothing happen in the screen. This happens because Person is notifying, not the binding source, but the list. But the list wasn't made for databinding, so it is not listening, nor propagating the event to the control. Lists that implement IBindingList, like BindingList, offer a better databinding support precisely by listening to the PropertyChangedEvent events of their contents and propagating them up to the databound control.
I am affraid I have given you no way of determining if an object is databoundable, because virtually all them are, but I hope I've given you a way of determining different levels of databinding support (INotifyPropertyChanged and IBindingList). I am assuming you know how to check for these via reflection.
Any instance of a class with properties is data bindable. (in fact instances of any class with fields or properties at all are data bindable)
Using reflection in .NET makes it very easy to discover/use data in an object. (at a small performance cost)
In order to answer this question you'd need to provide the specific usage scenarios you'll be encountering.
Rui gives some good explanation of different common data binding patterns, but each of them is for solving specific problems.
The right answer is always dependent on the context.
:)

Why is the DataSourceSelectArguments sealed?

Does anybody know the logic behind making DataSourceSelectArguments sealed?
I've implemented a custom DataSource (and related classes) for some custom business objects and custom WebControls. When thinking in filters (like in a grid) I discovered that the DataSourceSelectArguments is sealed. Surely, I'm missing something. (Maybe the logic is related to the fact that is nonsense to ask the DB again, just for filtering?, just a guess.)
Sorry for the delay, I was on holydays. :)
The problem is that a DataBoundControl such as ListView has a SortExpression property, but not a FilterExpression. It is fine to implement a sortable grid/list with a ListView by means of a IButtonControl WebControl that fires a PostBack and a Command event. Then you use the SortExpression or the Sort method and pass a sort expression that will fill the DataSourceSelectArguments.SortExpression and pass it to the DataSource which can construct the apropiate SQL statement (in my case) to retrieve the Data from the DB. This allows for separation between the Data and the WebControl that displays it, IMHO.
Following this pattern I was about to implement a filter by filling an extra parameter object in my DataSourceSelectArguments with the requested filter and I will have called Sort, which would have passed this arguments object to the DataSource, where I would have constructed the appropiate select clause.
I've finally solve it by "coding" the filter information in the SortExpression, but I find it ugly (for the name, in the first place: sort != filter), and I was wondering if there's a more appropiate way of doing this or if I'm missing something that is more subtle.
Edit:
Maybe a better approach would be to override ListView's PerformSelect method and ask my own implementation of the DataSourceView if it can filter, then call a special ExecuteSelect method that accepts a special DataSourceSelectArguments with a filter object. Taking care not to do anything that will break when someone use the custom ListView with a non-enhanced DataSourceView, of course.
My guess is because the class is a dumb data transfer object merely used to pass arguments to a method.
This class itself doesn't have any operations defined on it, thus what sort of polymorphism would you expect? For example, the existing methods will only know about the properties of this class, which are all settable, so there's no need to override the properties. If you added new properties, they would get ignored.
For your own method, can you create your own Arguments class that just happens to have all the same properties?

Resources