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

Sorry, this blog does not support comments.

I used various blog hosting services since this blog was established in 2005, but unfortunately they turned out to be unreliable in the long term and comment threads were lost in unavoidable transitions. At this time I don't want to enable third-party services for comments since it has become obvious in recent years that these providers invariably monetize information about their visitors and users.

Please use the links in the page footer to get in touch with me. I'm available for conversations on Keybase, Matrix, Mastodon or Twitter, as well as via email.