.NET remoting support

LLBLGen Pro fully support the usage of the generated code and the LLBLGen Pro runtime framework in a distributed scenario with .NET remoting. .NET remoting is often preferred in situations where the client and service are both .NET based and have a tight connection. You've two options: using normal serialization based on the .NET BinaryFormatter logic or use LLBLGen Pro's own fast serialization logic which is simply called FastSerialization.

It's required that at both sides the same serialization option is chosen, so if you decide to use FastSerialization, be sure that both the client and the service are using FastSerialization.

LLBLGen Pro also supports XML serialization used in webservices and WCF scenarios. If you're looking for more information on XML serialization, please see the section Using the generated code - XML Webservices support

To enable normal serialization, you don't have to do anything, it's enabled by default. Simply send the entities over the wire using remoting and you're set. All classes which are usable in a distributed scenario are serializable over remoting, including the exceptions.

Enabling FastSerialization

To enable FastSerialization you have to set the static property SerializationHelper.Optimization to one of the values of the enum type SerializationOptimization, which is either None (default, use normal BinaryFormatter serialization) or Fast.

After setting this property, LLBLGen Pro will serialize all entities and other objects using FastSerialization. This means that the serialization and deserialization process is much faster and the data block to send over the wire is much smaller.

FastSerialization works for Entities, TypedLists, TypedViews and UnitOfWork2 objects. For other objects, normal serialization is used as these tend to be rather small anyway.

SerializationHelper has other optimization settings. To avoid having the serialization logic to emit for every entity a GUID (the ObjectID value of an entity) you can switch this off by setting SerializationHelper.PreserveObjectIDs to false. The default is true. It has the side effect that an entity will receive a new GUID when deserialized. This will make using a Context object with these entities useless as the entity isn't detectable as being the same instance.

You can also inject your own compressor object. The FastSerialization logic will create a large byte array which is placed under the key "_" in the SerializationContext of the BinaryFormatter. To further compress this byte array, for example with a zip library, you can set the SerializationHelper.Compressor property to an instance of the IByteCompressor interface, which is a simple interface to compress and decompress blocks of bytes. LLBLGen Pro doesn't provide a basic implementation of this interface.

Serializing / Deserializing custom entity data

If you've added additional members to an entity class and you want to serialize the data in these members into the binary output as well as deserialize them at the other side, you have to add some code to make that happen. Below are the places described where to place which code to serialize / deserialize your own data with LLBLGen Pro's remoting logic. As an example, the value of a custom string member added to the entity class OrderEntity, _orderTotal (decimal), is serialized and deserialized.

Normal serialization / deserialization

To serialize the custom member _orderTotal in the OrderEntity class, and also to deserialize it properly, create a partial class of OrderEntity and add the following code to that partial class. (You can also add the code to the user code region at the bottom of OrderEntity if you want to). You don't need to add code for entity fields and entity collections inside entities, these are serialized automatically.

protected override void OnGetObjectData(SerializationInfo info, StreamingContext context)
{
    info.Add("_orderTotal", _orderTotal);
}

To deserialize the _orderTotal value again, add the following code to the partial class.

protected override void OnDeserialized(SerializationInfo info, StreamingContext context)
{
    _orderTotal = info.GetDecimal("_orderTotal");
}

FastSerialization

FastSerialization doesn't use the BinaryFormatter routines of ISerializable, it uses its own graph traversal routines and therefore to be able to serialize / deserialize your own custom member variables, you can't use the OnGetObjectData and OnDeserialized methods. Instead you have to use the following two routines, following the same example as above and these also should be added to a partial class of OrderEntity.

Important!

Make sure that the serialization order and deserialization order are the same.

To serialize the _orderTotal value into the stream, use the following code:

protected override void SerializeOwnedData(SerializationWriter writer, object context)
{
    base.SerializeOwnedData(writer, context);
    writer.WriteOptimized(_orderTotal);
}

To deserialize the serialized value, use the following code:

protected override void DeserializeOwnedData(SerializationReader reader, object context)
{
    base.DeserializeOwnedData(reader, context);
    _orderDecimal = reader.ReadOptimizedDecimal();
}

Serialize / Deserialize RemovedEntitiesTracker with FastSerialization

A RemovedEntitiesTracker collection inside an entity collection isn't serialized into the remoting stream by default. This is because it's recommended to use a UnitOfWork2 instance and if an entity collection with a RemovedEntitiesTracker is added to the UnitOfWork2 instance, as well as the RemovedEntitiesTracker collection, it would be redundant to serialize the same collection twice.

To serialize the RemovedEntitiesTracker inside an entity collection without a UnitOfWork2 class, you've to add some lines of code to make this work. First you've to create a partial class of EntityCollection<T> in the generated code. In your partial class of EntityCollection<T>, add the code as illustrated below. After that, you can serialize over FastSerialization enabled remoting an entity collection with embedded inside itself the RemovedEntitiesTracker collection

public partial class EntityCollection<TEntity> : EntityCollectionBase2<TEntity>
    where TEntity : EntityBase2, IEntity2
{
    /// <summary>
    /// Method which restores owned data - i.e. considered private to this collection
    /// and not shared with any external object
    /// </summary>
    /// <param name="writer">SerializationWriter</param>
    /// <param name="context">The serialization flags (previously constructed)</param>
    protected override void SerializeOwnedData(SerializationWriter writer, object context)
    {
        base.SerializeOwnedData(writer, context);
        var trackerData = new byte[0];
        if((this.RemovedEntitiesTracker != null) && (this.RemovedEntitiesTracker.Count>0))
        {
            // serialize tracker
            var serializer = new FastSerializer();
            trackerData = serializer.Serialize(this.RemovedEntitiesTracker).ToArray();
        }
        writer.Write(trackerData);
    }

    /// <summary>
    /// Method which restores owned data - i.e. considered private to this entity
    /// and not shared with any external object
    /// </summary>
    /// <param name="reader">The SerializationReader containing the serialized data</param>
    /// <param name="context">The serialization flags (previously read)</param>
    protected override void DeserializeOwnedData(SerializationReader reader, object context)
    {
        base.DeserializeOwnedData(reader, context);
        var trackerData = reader.ReadByteArray();
        if(trackerData.Length > 0)
        {
            // tracker data read, deserialize it to a real tracker collection
            var trackerCollection = new EntityCollection<TEntity>();
            var deserializer = new FastDeserializer();
            deserializer.Deserialize(trackerData, trackerCollection);
            this.RemovedEntitiesTracker = trackerCollection;
        }
    }
}
Public Partial Class EntityCollection(Of TEntity As {EntityBase2, IEntity2}) 
    Inherits EntityCollectionBase2(Of TEntity)

    ''' <summary>
    ''' Method which restores owned data - i.e. considered private to this collection
    ''' and not shared with any external object
    ''' </summary>
    ''' <param name="writer">SerializationWriter</param>
    ''' <param name="context">The serialization flags (previously constructed)</param>
    Protected Overrides Sub SerializeOwnedData(writer As SerializationWriter, context As Object)
        MyBase.SerializeOwnedData(writer, context)
        Dim trackerData() As Byte
        If (Not (this.RemovedEntitiesTracker Is Nothing) AndAlso (Me.RemovedEntitiesTracker.Count>0)) Then
            ' serialize tracker
            Dim serializer As New FastSerializer()
            trackerData = serializer.Serialize(Me.RemovedEntitiesTracker).ToArray()
        End If
        writer.Write(trackerData)
    End Sub

    ''' <summary>
    ''' Method which restores owned data - i.e. considered private to this entity
    ''' and not shared with any external object
    ''' </summary>
    ''' <param name="reader">The SerializationReader containing the serialized data</param>
    ''' <param name="context">The serialization flags (previously read)</param>
    Protected Overrides Sub DeserializeOwnedData(reader As SerializationReader, context As object)
        MyBase.DeserializeOwnedData(reader, context)
        Dim trackerData() As Byte = reader.ReadByteArray()
        If trackerData.Length > 0 Then
            ' tracker data read, deserialize it to a real tracker collection
            Dim trackerCollection As New EntityCollection(Of TEntity)()
            Dim deserializer As New FastDeserializer()
            deserializer.Deserialize(trackerData, trackerCollection)
            Me.RemovedEntitiesTracker = trackerCollection
        End If
    End Sub
End Class