WPF DataBinding and DBNulls
April 17th, 2008 | by rob |After spending a few hours with ADO.NET, typed dataset, and some WPF bindings I was feeling like I’d been 5 rounds with Mike Tyson.
Well, it wasn’t that bad, but it just felt that bad.
The trouble is the behaviour of ADO’s typed datasets when dealing with value types that can be NULL on the database. The implementation of this in ADO leaves much to be desired when interfacing with the real world.
Take the code I was working on today. I created a value converter binding that converts null values to Visibility.Collapsed. As an example think of an address label that has 4 lines of address - if lines 2 & 3 are missing I want to collapse them so line 4 appears immediately under line 1.
The example above work ok, because strings are reference types and you can set how ADO.NET’s typed dataset behaves when the column hold a null value.
The default behavior is to throw an exception when you attempt to read the value. Not ideal.
If you try to change the behaviour for Value Types such as the Int32 above you’ll get the standard error message that the property value is invalid for that type :
So, if you have a WPF binding that is bound directly to the contents of a ADO.Net data row, the binding will fail with an exception - not really what i’d like. Reference types are ok, we can simply change the default behaviour of NullValue for the column to (Null) and all will be well.
Since there’s no way to change the behaviour of ADO.Net’s properties, we must resort to using an object to wrap the data row (or if you’re feeling adventurous, you could always add to the partial strongly typed datarow - but you’d need to use a different name to represent your type)
Using a standard C# Class to wrap the data row, we’re basically mimicking the Active Record pattern (wrapping a Database Column with a custom business object). Well, almost, we still have to resort to using the rest of ADO to add our class to a dataset for writing out to the Database (unless we go for something like Castle Active record
)
With a little code like the following we can wrap our column :
public class TestClass { private ExampleDS.CustomersRow dataRow; public int? CustomerNumber { get { if (dataRow.IsCustomerLastOrderNumberNull()) return null; else return dataRow.CustomerLastOrderNumber; } set { if (value.HasValue) dataRow.CustomerLastOrderNumber = value.Value; else dataRow.SetCustomerLastOrderNumberNull(); } } }
So, by using nullable types, we can convert the nasty DBNull into a real null making the interface to our data consistent across both value types and reference types.
Now when you bind to the data in your own class (after implementing INotifyPropertyChanged I hope!), you wont see any exceptions being thrown when the binding engine attempts to retrieve a real value type from a database column containing a DBNull!
If application is small and you’re binding to dataset data directly (even though the ADO classes do support property notification changes - See Beatriz Costa’s blog for more about that) then it may not be worth the extra effort in making the data’s returned values consistent, but on a large application, its almost certainly worth making custom business object , or using one of the many ORM tools like iBatis,nHibernate or Castle’s Active Record, depending on your circumstances!
4 Responses to “WPF DataBinding and DBNulls”
By littlesteps on Oct 6, 2009 | Reply
Hello,
A better way is to use a ValueConverter.
Its aim is to convert data correctly to data binding without any wrapper. Typically, you can have a ValueConverter upon your DataRow class to convert your null value to an “Empty” string, or you can decide to return an icon (ImageSource) corresponding to a DBNull, etc.
The philosophy of WPF and data binding invites the developer to have loosely coupled GUI.
Let’s have a look to Bea Stollnitz’s blog (aka Bea Costa): http://bea.stollnitz.com
She has a wonderful blog on databinding!
Regards,
LittleSteps
By rob on Oct 6, 2009 | Reply
Hi,
That wasn’t the issue - if you read the post I’d written a value converter, but if you bind directly to a value type ADO will throw an exception when it *reads* the value for the value converter. the VC comes in too late. You have to avoid the exception before that happens, and you can only change the way ADO.net handles DBNULLs when you’re dealing with a ref type.
so you’re back to wrapping the offending type column with a custom business object to check for dbnulls.
thanks
Rob
By mattreek on Jan 25, 2010 | Reply
As far as I know only DataRowView implements INotifyPropertyChanged.
In this case: How could you implement INotifyPropertyChanged to ensure the Binding also gets notified when properties of the wrapped dataRow are changed? Is this possible with this approach?
thanks
matt
By Rob on Jan 25, 2010 | Reply
Hi Matt.
I’ts been almost 2 years since I wrote this post,(I dont know why the comments show Oct2009!) and i’ve been on an ASP.Net contract for over 12 months so haven’t paid much attention to the problem I first encountered. I would imagine in the newest versions of WPF/ADO there would be an infinitely more elegant way of handling this! Sorry I cant be much help at the moment!
Rob