Say you have a complex object graph with circular references and you need to send it across the wire as XML. The good ol' XmlSerializer is not going to help you; you need a more powerful too like WCF's DataContractSerializer. By slapping [DataContract] onto the classes in your object graph and [DataMember] onto the non-transient properties and fields of these classes, you're golden. WCF serializes cyclical object graphs without complaint. Well, almost.
If you're using Hibernate's lazy-initialization feature, you'll see a problem: .NET will compain about unknown types (proxy wrappers) that need to be registered as well-known types. Unfortunately the types aren't known until run-time. Sure, you could traverse the object graph at runtime, figure out these proxy types, and pass them into a DataContractSerializer costructor, but that's far from elegant. Even if you make it over that hurdle, .NET will complain about Hibernate's persistent set implementations too; you'll have to add those types too. If you're using generics, that could quickly balloon to a lot of types.
Fortunately there's a straightforward solution: IDataContractSurrogate. WCF provides this interface specifically to get around sticky situations like this. You can use it to unwrap and initialize proxies, and to replace Hibernate's persistent collections with built-in well-known collections. Here's how it works:
public class HibernateDataContractSurrogate : IDataContractSurrogate
{
public HibernateDataContractSurrogate()
{
}
public Type GetDataContractType(Type type)
{
// Serialize proxies as the base type
if (typeof(INHibernateProxy).IsAssignableFrom(type))
{
type = type.GetType().BaseType;
}
// Serialize persistent collections as the collection interface type
if (typeof(IPersistentCollection).IsAssignableFrom(type))
{
foreach (Type collInterface in type.GetInterfaces())
{
if (collInterface.IsGenericType)
{
type = collInterface;
break;
}
else if (!collInterface.Equals(typeof(IPersistentCollection)))
{
type = collInterface;
}
}
}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// Serialize proxies as the base type
if (obj is INHibernateProxy)
{
// Getting the implementation of the proxy forces an initialization of the proxied object (if not yet initialized)
obj = NHibernateProxyHelper.GetLazyInitializer((INHibernateProxy)obj).GetImplementation();
}
// Serialize persistent collections as the collection interface type
if (obj is IPersistentCollection)
{
IPersistentCollection persistentCollection = (IPersistentCollection)obj;
persistentCollection.ForceInitialization();
obj = persistentCollection.Entries(); // This returns the "wrapped" collection
}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
return obj;
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
}
To use this surrogate, simply pass in an instance to the DataContractSerializer constructor:
DataContractSerializer serializer = new DataContractSerializer(typeof(rootObj.GetType()), null, int.MaxValue, false, true, new HibernateDataContractSurrogate());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, rootObj);
Notes:
-
With this technique, you avoid having to translate your Hibernate objects into DTOs before serialization.
-
There's an alternative: you can use
NetDataContractSerializer, however that would assume you've got NHibernate and Castyle.DynamicProxy at the other end of the wire. It also requires a bit more legwork, as described
here.
-
WCF doesn't know about Iesi sets, so you're still on the hook for adding attributes such as [WellKnown(typeof(HasedSet<type>))] to classes that use sets.