In XPO, an ORM product by Developer Express, it’s possible to use a custom “value converter” to persist information when the standard mapping techniques are not sufficient. This provides a flexible approach that can be used in many different scenarios.
Nullable types
Although the Developer Express knowledge base provides an article that shows how to persist a bitmap, I had never actually used that approach. With .NET 2.0, I was thinking about persisting nullable types and I found that XPO 1 doesn’t have support for that out of the box. The support guys sent me a sample showing how a value converter can be used to extend the mapping to include nullable types. I extended that code to create a generic value converter that can be used for any nullable type. Here’s the code for that:
public class NullableValueConverter<T> : ValueConverter
where T: struct {
public NullableValueConverter( ) {
storageType = typeof(T);
}
private Type storageType;
public override Type StorageType {
get { return storageType; }
}
public override object ConvertToStorageType(object value) {
Nullable<T> nullableValue = (Nullable<T>) value;
return nullableValue.HasValue ? (object) nullableValue.Value : null;
}
public override object ConvertFromStorageType(object value) {
return (Nullable<T>) (value == null ? null : value);
}
}
Now how do you use that thing? It’s simple: just decorate a property that has a nullable type with the ValueConverterAttribute
:
private int? intVal;
[ValueConverter(typeof(NullableValueConverter<int>))]
public int? IntVal {
get { return intVal; }
set { intVal = value; }
}
With newer versions of XPO (I’m not sure if that feature is in any released version yet, if it isn’t and you want it (and you are a customer), feel free to send mail to support@devexpress.com) you can also register the value converter for a type globally, so all properties of that type will be converted automatically. Here’s how:
Session.DefaultSession.Dictionary.RegisterValueConverter(
new NullableValueConverter<int>(), typeof(int?));
Unknown types
The second interesting use of the value converter was something I stumbled upon a few days later. I had a field in a class that should have been persisted, but I really didn’t know what that field might contain later on. The information would be sent to a server via Remoting and the type depended on the client implementation, so I could only use object
as the type for the field. Now, how would I go about persisting this information? The solution I found was to create a value converter that uses serialization to persist the object into a string field. Of course, the object has to be Serializable for this to work, but that’s a requirement for the Remoting part anyway, so I didn’t care. With my SerializableObjectConverter
it’s possible to decorate a property like this:
private object data;
[ValueConverter(typeof(SerializableObjectConverter)), Size(SizeAttribute.Unlimited)]
public object Data {
get { return data; }
set { data = value; }
}
And here’s the code for the SerializableObjectConverter
:
public class SerializableObjectConverter : ValueConverter {
public override Type StorageType { get { return typeof(string); } }
public override object ConvertToStorageType(object value) {
Assert.Check(value.GetType().IsSerializable,
String.Format("The given object ({0}) is not serializable.", value));
string result = null;
MemoryStream stream = new MemoryStream();
try {
SoapFormatter formatter = new SoapFormatter( );
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
formatter.Serialize(stream, value);
result = Encoding.Default.GetString(stream.GetBuffer( ));
}
finally {
stream.Close();
}
return result;
}
public override object ConvertFromStorageType(object value) {
object result = null;
MemoryStream stream = new MemoryStream(
Encoding.Default.GetBytes((string) value));
try {
stream.Position = 0;
SoapFormatter formatter = new SoapFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
result = formatter.Deserialize(stream);
}
finally {
stream.Close();
}
return result;
}
}