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 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.