Archives for: March 2005, 15
15/03/05
Using .NET 1 nullable types with data binding
In this post I showed how nullable types can be simulated in .NET 1, especially for use with XPO. Prompted by Miha MarkiÄ, I had a look at the possibility of data binding with those simulated nullable types.
I found that while the implementation I had suggested could be used without problems in a read-only situation, it obviously didn't contain any logic that would enable editing the types via data-bound controls. Nevertheless, this is easy to do by creating a custom TypeConverter. Like this:
public class NullableInt32TypeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
if (value is string) {
try {
int intVal = Convert.ToInt32(value);
return new NullableInt32(intVal);
}
catch (FormatException) {
return new NullableInt32( );
}
}
return base.ConvertFrom(context, culture, value);
}
}
To activate this type converter for the NullableInt32, you have to decorate the NullableInt32 class with an attribute:
[TypeConverter(typeof(NullableInt32TypeConverter))]
public class NullableInt32 : Nullable {
public NullableInt32( ) : base(false) { }
...
Now you can bind a collection of objects, which contain properties of the type NullableInt32, to a grid and edit the value for the NullableInt32 property. Just enter an invalid or empty string to set the value to null.
Update: Miha pointed me to this obviously more complete implementation of nullable types for .NET 1. I haven't had a close look at them because my platform is .NET 2 anyway, so I can't say anything about the implementation, but obviously the main purpose of this post was to show a way of implementing this kind of thing with XPO. I guess that information in usable with the sourceforge implementation of nullable types just the same.
Nullable types in XPO without .NET 2
In a previous article, I showed how nullable types, as implemented in version 2 of the .NET framework, can be persisted using XPO. But what if .NET 2.0 is not yet an option? .NET 1.1 doesn't have support for nullable types and there's no such feature in XPO, either.
If somebody comes from the database world, it's certainly understandable that types that can also be null are something he's accustomed to. On the other hand, XPO is about persisting objects, and in that world, no ordinary value types have ever been able to have the value null. Anyhow, I do think it's a nice feature to be able to set ints to null, certainly so when persistence to databases is considered.
So, I've created a nullable type together with, once again, a value converter, that allows to use nullable value types in .NET 1.1. Here's the code for a base class and the NullableInt32:
public abstract class Nullable {
public Nullable(bool hasValue) {
this.hasValue = hasValue;
}
private bool hasValue;
public bool HasValue {
get { return hasValue; }
set { hasValue = value; }
}
public override string ToString( ) {
return hasValue ? "" : "(null)";
}
}
public class NullableInt32 : Nullable {
public NullableInt32( ) : base(false) { }
public NullableInt32(Int32 value) : base(true) {
this.value = value;
}
private Int32 value;
public Int32 Value {
get { return value; }
set {
this.value = value;
HasValue = true;
}
}
public override string ToString( ) {
return HasValue ? Value.ToString( ) : base.ToString( );
}
}
When writing these classes, I made the design decision not to implement the object's value in the base class. Of course, each derived class needs more code because of that, but I avoid a lot of boxing/unboxing (David Cumps has some useful info on that), which might be worthwhile in this context.
The other decision I made was to use a class as opposed to a struct. Using a struct would be a good idea because we wouldn't need to add a line to the constructor of the data object to get the object initialised. But then we'd have to implement all the functionality in every single nullable type, because there's no such thing as an "abstract base struct". By the way, the .NET 2.0 nullable types are implemented using structs, but that's only made possible by the .NET 2 support for Generics. Something to add to my recent article, I guess :-)
Now we need a value converter to properly store this type into a single field in the database:
public class NullableInt32ValueConverter : ValueConverter {
public override Type StorageType {
get { return typeof(Int32); }
}
public override object ConvertToStorageType(object value) {
NullableInt32 val = value as NullableInt32;
return val.HasValue ? (object) val.Value : null;
}
public override object ConvertFromStorageType(object value) {
NullableInt32 result = new NullableInt32( );
if (value != null)
result.Value = (Int32) value;
return result;
}
}
A data class that uses this type might look like this:
public class DataClass : XPObject {
public DataClass( ) {
intVal = new NullableInt32( );
}
NullableInt32 intVal;
[ValueConverter(typeof(NullableInt32ValueConverter))]
public NullableInt32 IntVal {
get { return intVal; }
set { intVal = value; }
}
}
When this data class is persisted, it will use a single database field that's set to null if the nullable int field doesn't hold a value.
Of course, this implementation isn't as elegant as that in .NET 2.0, which makes use of the advantages of Generics and native nullable types. But it's certainly usable if you tuck away the different NullableXXX classes (created like the one above) and the value converters in a helper assembly. It's also possible to register a value converter for a specific type globally, so you don't have to decorate each property with the ValueConverterAttribute. In that case, just make sure to call the registration method before you do any persisting or loading on the XPO data.
Session.DefaultSession.Dictionary.RegisterValueConverter( new NullableInt32ValueConverter(), typeof(NullableInt32));
Update: Miha pointed me to this obviously more complete implementation of nullable types for .NET 1. I haven't had a close look at them because my platform is .NET 2 anyway, so I can't say anything about the implementation, but obviously the main purpose of this post was to show a way of implementing this kind of thing with XPO. I guess that information in usable with the sourceforge implementation of nullable types just the same.


