Automating the Righthand Dataset Visualizer build process and a refresh build

Since the last version released I become aware of an issue in the visualizer. If you were working on a project that referenced newer DevExpress assemblies the visualizer might have reported an exception due to the binary incompatibility with its references to the DevExpress assemblies - a assembly binding issue.

(If you want just the binaries you can skip to the end of the article and download them.)

The solution is to use a custom build of DevExpress assemblies. If you have their sources you can build your custom DevExpress assemblies using these useful scripts.

Then I have to use those custom built assemblies with my visualizer but I want to use them only for release build, not for debug. So I created a folder named CustomAssemblies under visualizer solution. I added a reference path to this folder to all of the visualizer projects. Which means that the MSBuild will use assemblies in this folder if they are present or ones from GAC (the original ones) if the folder is empty. Unfortunatelly the reference paths are global to the project and you can't have two different sets for two different configurations.

So the building of the release version looks like: populate CustomAssemblies folder with custom DevExpress assemblies, run MSBuild on Release configuration and at the end clear the CustomAssemblies folder so the debug version works with the original DevExpress assemblies. But there is one more obstacle. The license.licx file lists public key of the DevExpress assemblies and it doesn't match the one found in custom version. So I have to replace all occurences of the original public key with my custom version public key before the build and restore the originals after the build. Problems solved.

The actual release process involves also {SmartAssembly} which merges all assemblies into a single file and signing it with a certificate+timestamping and finally zipping the rather large result. Because I am not a masochist I decided to create a FinalBuilder project that does all of this automatically for me (except for building custom DevExpress assemblies).

Let me know if there are still problems! (12.67 mb) (12.67 mb)

Read more about Righthand DataSet Visualizer here.

About strongtyped datasets, DBNulls, WPF and binding

The mix of strongtyped datasets, DBNull values, WPF and bindings will likely yield exceptions of invalid cast type when binding nullable columns.

Imagine you have a table with a single, nullable, column Description. You would bind it against a TextBox (or any other control) with this syntax: {Binding Description}. When binding is performed against a row that has a DBNull value (remember, ADO.NET uses DBNull.Value for null values) in that column you get an Unable to cast object of type 'System.DBNull' to type 'System.String' exception.

The reason for that exception is quite simple. Let’s take a look at how ADO.NET implements the strongtyped Description column:

   1: [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
   2: [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "")]
   3: public bool IsDescriptionNull() {
   4:     return this.IsNull(this.tableXXX.DescriptionColumn);
   5: }
   7: [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
   8: [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "")]
   9: public string Description {
  10:     get {
  11:         try {
  12:             return ((string)(this[this.tableXXX.DescriptionColumn]));
  13:         }
  14:         catch (global::System.InvalidCastException e) {
  15:             throw new global::System.Data.StrongTypingException("The value for column \'Description\' in table \'XXX\' is DBNull.", e);
  16:         }
  17:     }
  18:     set {
  19:         this[this.tableXXX.DescriptionColumn] = value;
  20:     }
  21: }

The obvious problem is that WPF binding goes straight to Description property without checking IsDescriptionNull() method (as your code should) and then property getter fails to convert DBNull.Value to a string. And no, at this point even a converter won’t help you because the value is read before converter is even invoked.

But fear not, the solution is a very simple one. Just add square brackets around Description in binding syntax, like this: {Binding [Description]}. Looks similar but it is different. The former syntax uses strongtyped property to access the data while the later uses the DataRow’s this[string] accessor instead which returns an object and thus no exceptions are raised due to the DBNull.Value to string conversion. Furthermore a DBNullConverter value converter can come handy as well:

   1: public class DBNullConverter: IValueConverter
   2: {
   3:     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   4:     {
   5:         if (value == DBNull.Value)
   6:             return DependencyProperty.UnsetValue;
   7:         else
   8:             // return base.Convert(value, targetType, parameter, culture);
   9:             return value;
  10:     }
  12:     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  13:     {
  14:         if (value == null)
  15:             return DBNull.Value;
  16:         else
  17:             // return base.ConvertBack(value, targetType, parameter, culture);
  18:             return value;
  19:     }
  20: }