| « Skype in your mobile phone? | The Truth About Real Programmers - John Topley's Weblog » |
Persisting of unknown (or new) types with XPO
10/02/05
Persisting of unknown (or new) types with XPO
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;
}
}
3 comments, 1 pingback
AFAIK it should take any object, serializeable or not.
Of course, using the binary format has its advantages because the stored data will be more compact and I guess the serialization process will probably be faster, too. But I usually prefer to use text (xml) based formats these days because memory and processing power aren't the scarce resources they used to be. Plus, I can always look into the database with any tool and - more or less - read what's in the field if it's in SOAP format.
objects back in time when .net was initially released. It wasn't possible without using the
binary formatter. Maybe it had something to do with the object graph. Anyway, I haven't had any
use for it since, so until now I just believed that the binary formatter was capable of
serializing any objects. Maybe I also confused it with the ISerializable interface.


