For a custom typehandler example we will try to write a very simple typehandler for the StringBuilder class. We want to handle a StringBuilder as a value type, therefore we implement the ValueTypeHandler interface. Not that there's a whole collection of interfaces for typehandlers. Take a look at the TypeHandler4 type hierarchy.
To keep it simple we will skip information required for indexing - please look at IndexableTypeHandler in db4o sources to get more information on how to handle indexes.
The first thing should be the write method, which determines how the object is persisted:
public void Write(IWriteContext writeContext, object o) { StringBuilder builder = (StringBuilder) o; string str = builder.ToString(); byte[] bytes = Encoding.UTF8.GetBytes(str); writeContext.WriteInt(bytes.Length); writeContext.WriteBytes(bytes); }
Public Sub Write(ByVal writeContext As IWriteContext, ByVal o As Object) _ Implements IValueTypeHandler.Write Dim builder As StringBuilder = DirectCast(o, StringBuilder) Dim str As String = builder.ToString() Dim bytes As Byte() = Encoding.UTF8.GetBytes(str) writeContext.WriteInt(bytes.Length) writeContext.WriteBytes(bytes) End Sub
As you can see from the code above, there are 3 steps:
Next step is to read the stored object. It is just opposite to the write method:
public Object Read(IReadContext readContext) { int length = readContext.ReadInt(); byte[] data = new byte[length]; readContext.ReadBytes(data); return new StringBuilder(Encoding.UTF8.GetString(data)); } }
Public Function Read(ByVal readContext As IReadContext) As Object _ Implements IValueTypeHandler.Read Dim length As Integer = readContext.ReadInt() Dim data As Byte() = New Byte(length - 1) {} readContext.ReadBytes(data) Return New StringBuilder(Encoding.UTF8.GetString(data)) End Function
Delete is simple - we just reposition the buffer offset to the end of the slot:
public void Delete(IDeleteContext deleteContext) { SkipData(deleteContext); } private static void SkipData(IReadBuffer deleteContext) { int numBytes = deleteContext.ReadInt(); deleteContext.Seek(deleteContext.Offset() + numBytes); }
Public Sub Delete(ByVal deleteContext As IDeleteContext) _ Implements IValueTypeHandler.Delete SkipData(deleteContext) End Sub Private Shared Sub SkipData(ByVal deleteContext As IReadBuffer) Dim numBytes As Integer = deleteContext.ReadInt() deleteContext.Seek(deleteContext.Offset() + numBytes) End Sub
The last method left: #defragment. This one only moves the offset to the beginning of the object data, i.e. skips Id and size information (to be compatible to older versions):
public void Defragment(IDefragmentContext defragmentContext) { SkipData(defragmentContext); }
Public Sub Defragment(ByVal defragmentContext As IDefragmentContext) _ Implements IValueTypeHandler.Defragment SkipData(defragmentContext) End Sub
Now to use this type handler we need to configure db4o. To register a typehandler you have to provide a predicate which decides if a type is handled by the typehandler and the typehandler itself.
IEmbeddedConfiguration configuration = Db4oEmbedded.NewConfiguration(); configuration.Common.RegisterTypeHandler( new SingleClassTypeHandlerPredicate(typeof(StringBuilder)), new StringBuilderHandler());
Dim configuration As IEmbeddedConfiguration = Db4oEmbedded.NewConfiguration() configuration.Common.RegisterTypeHandler( New SingleClassTypeHandlerPredicate(GetType(StringBuilder)), New StringBuilderHandler())
After that all string builders are handled by you're type handler.