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;
  }
}