Custom PersistentProperty Module (Binary Serializer)
We recommend that you read the previous sections about Persistent Properties
before reading this section.
In the previous sample we wrote a custom module for the PersistentProperty
that stored the persistent properties to a Azure Storage Tables
. Now we will show a sample using a Binary Serializer
.
Considerations
Both the Azure sample and this sample will store property values and repopulate even if the server stops and then restarts. The big difference between the sample with Azure Storage Tables
and binary serialization is that writing values to disk will be an issue if you ScaleOut
servers. If using ScaleOut
and Custom Property Storage
you will either need a central storage (that all servers can access) or you will need sticky sessions.
Implement the PersistentPropertyStorage
There is a template for CustomPersistentPropertyStorage
. Choose Add->NewItem and go to XSockets.NET 5
This will give us something like
using System;
using System.Threading.Tasks;
using XSockets.Core.XSocket;
public class AzurePersistentPropertyStorage : PersistentPropertyStorage
{
public override async Task ReadFromPropertyStorage<T>(T controller)
{
throw new NotImplementedException("Implement custom storage for property values");
}
public override async Task WriteToPropertyStorage<T>(T controller)
{
throw new NotImplementedException("Implement custom storage for property values");
}
}
Reading/Writing to disk
The complete sample module looks like this.
using System;
using System.Threading.Tasks;
using XSockets.Core.XSocket;
public class FilePersistentPropertyStorage : PersistentPropertyStorage
{
public override async Task ReadFromPropertyStorage<T>(T controller)
{
foreach (var pi in this.GetPersistentProperties(controller.GetType()))
{
pi.ReadProperty(controller);
}
}
public override async Task WriteToPropertyStorage<T>(T controller)
{
foreach (var pi in this.GetPersistentProperties(controller.GetType()))
{
var v = pi.GetValue(controller, null);
if (v == GetDefault(pi.PropertyType)) continue;
pi.WriteProperty(v, controller);
}
}
public static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
Helpers
As you can see above that we call ReadProperty
and WriteProperty
for saving and retrieving entities (or persisted properties).
The helper class is pasted in below. Basically the helpers just serialize and deserialize properties to/from disk.
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using XSockets.Core.Common.Socket;
using XSockets.Core.Common.Utility.Logging;
using XSockets.Plugin.Framework;
public static class StorageHelpers
{
public const string storageFolder = "PropertyValues";
private const string storagePath = storageFolder + "\\{0}.{1}.{2}.bin";
static StorageHelpers()
{
if (!Directory.Exists(storageFolder))
Directory.CreateDirectory(storageFolder);
}
public static void ReadProperty(this PropertyInfo pi, IXSocketController controller)
{
var uniqueName = string.Format(storagePath, controller.Alias, controller.PersistentId, pi.Name);
if (!File.Exists(uniqueName)) return;
using (var fs = new FileStream(uniqueName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
try
{
var formatter = new BinaryFormatter();
pi.SetValue(controller, formatter.Deserialize(fs), null);
}
catch (SerializationException e)
{
Composable.GetExport<IXLogger>().Error(e, "Failed to deserialize. Reason: {e}" + e.Message);
}
}
}
public static void WriteProperty(this PropertyInfo pi, object o, IXSocketController controller)
{
var uniqueName = string.Format(storagePath, controller.Alias, controller.PersistentId, pi.Name);
using (var fs = new FileStream(uniqueName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
try
{
var formatter = new BinaryFormatter();
formatter.Serialize(fs, o);
}
catch (SerializationException e)
{
Composable.GetExport<IXLogger>().Error(e, "Failed to deserialize. Reason: {e}" + e.Message);
}
}
}
}
Test
So, if we repeat the Putty stuff from the Persistent Property Sample
section you will see that values are written to disk at the specified location when the connection is closed. You will also get back the value from the Custom Storage
when you reconnect
Note: You can also see that subscriptions are persisted. So the server will remember the clients subscriptions between connections.