Database Upgrade - potential solution

Jun 10, 2011 at 9:42 AM
Edited Jun 10, 2011 at 11:58 AM

I've had a thought on the database upgrade problem, and have a Beta SerializationHelper class (SerializationHelper.cs) that passes all unit tests.

1. new code is in #region upgrade sections, the original code in #region wintellect

2. I write the actual type.AssemblyQualifiedName instead of using _typeResolver(type.AssemblyQualifiedName).

This was for debug ease, and should be changed back to using _typeResolver - may be some version checking to do...

3. When a property type is changed, an event will be surfaced so any data conversion can be performed before the value is set or ignored

This is not implemented at the moment, but search for 'type mismatch event' to see where it will be

Have tested this by saving a dummy class with lots of different properties.

Then commenting properties and reloading the original saved class

Created new properties and reloading the original saved class

Have a look and a play

Paul

SerializationHelper.cs

 

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Wintellect.Sterling.Database;
using Wintellect.Sterling.Exceptions;
using System.Collections.Generic;

namespace Wintellect.Sterling.Serialization
{
    /// <summary>
    ///     Wraps nodes for passing directly into the Save pass of the Serialization Helper
    ///     Basically just hosts another object so that the helper can recursively navigate properties
    ///     Useful in external serializers that want to re-enter the stream using the helper methods
    /// </summary>
    public class SerializationNode
    {
        public object Value { get; set; }

        public static SerializationNode WrapForSerialization(object obj)
        {
            return new SerializationNode {Value = obj};
        }

        public T UnwrapForDeserialization<T>()
        {
            return (T) Value;
        }
    }

    /// <summary>
    ///     This class assists with the serialization and de-serialization of objects
    /// </summary>
    /// <remarks>
    ///     This is where the heavy lifting is done, and likely where most of the tweaks make sense
    /// </remarks>
    public class SerializationHelper
    {
        // a few constants to serialize null values to the stream
        private const ushort NULL = 0;
        private const ushort NOTNULL = 1;
        private const string NULL_DISPLAY = "[NULL]";
        private const string NOTNULL_DISPLAY = "[NOT NULL]";
        
        /// <summary>
        ///     The import cache, stores what properties are available and how to access them
        /// </summary>
        private readonly
            Dictionary<Type, List<SerializationCache>>
            _propertyCache =
                new Dictionary
                    <Type, List<SerializationCache>>();

        private readonly Dictionary<string,Type> _typeRef = new Dictionary<string, Type>();

        private readonly ISterlingDatabaseInstance _database;
        private readonly ISterlingSerializer _serializer;
        private readonly LogManager _logManager;
        private readonly Func<string, int> _typeResolver = s => 1;
        private readonly Func<int, string> _typeIndexer = i => string.Empty;

        /// <summary>
        ///     Cache the properties for a type so we don't reflect every time
        /// </summary>
        /// <param name="type">The type to manage</param>
        private void _CacheProperties(Type type)
        {
            lock (((ICollection)_propertyCache).SyncRoot)
            {
                // fast "out" if already exists
                if (_propertyCache.ContainsKey(type)) return;

                _propertyCache.Add(type,
                                   new List<SerializationCache>());

                var isList = typeof (IList).IsAssignableFrom(type);
                var isDictionary = typeof (IDictionary).IsAssignableFrom(type);
                var isArray = typeof (Array).IsAssignableFrom(type);

                var noDerived = isList || isDictionary || isArray; 

                // first fields
                var fields = from f in type.GetFields()
                             where                              
                             !f.IsStatic &&
                             !f.IsLiteral &&
                             !f.IsIgnored(_database.IgnoreAttribute) && !f.FieldType.IsIgnored(_database.IgnoreAttribute)
                             select new PropertyOrField(f);               
                
                var properties = from p in type.GetProperties()
                                 where          
                                 ((noDerived && p.DeclaringType.Equals(type) || !noDerived)) &&
                                 p.CanRead && p.CanWrite &&
                                 p.GetGetMethod() != null && p.GetSetMethod() != null
                                       && !p.IsIgnored(_database.IgnoreAttribute) && !p.PropertyType.IsIgnored(_database.IgnoreAttribute)
                                 select new PropertyOrField(p);

                var tmp = (properties.Concat(fields)).ToList();
                foreach (var p in tmp)
                {                    
                    var propType = p.PfType;   
                 
                    // eagerly add to the type master
                    _typeResolver(propType.AssemblyQualifiedName);

                    var p1 = p;

                    _propertyCache[type].Add(new SerializationCache(propType,p1.Name, (parent, property) => p1.Setter(parent,property), p1.GetValue));                    
                }                
            }
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="database">Database this is a helper for</param>
        /// <param name="serializer">The serializer</param>
        /// <param name="logManager">The logger</param>
        /// <param name="typeResolver"></param>
        /// <param name="typeIndexer"></param>
        public SerializationHelper(ISterlingDatabaseInstance database, ISterlingSerializer serializer,
                                   LogManager logManager, Func<string,int> typeResolver, Func<int,string> typeIndexer)
        {
            _database = database;
            _serializer = serializer;
            _logManager = logManager;
            _typeResolver = typeResolver;
            _typeIndexer = typeIndexer;
        }

        /// <summary>
        ///     External entry point for save, used by serializers
        ///     or other methods that simply want to intercept the
        ///     serialization stream. Wraps the object in a node and
        ///     then parses recursively
        /// </summary>
        /// <remarks>
        ///     See the custom serializer test for an example
        /// </remarks>
        /// <param name="obj">The instance to save</param>
        /// <param name="bw">The writer to inject to</param>
        public void Save(object obj, BinaryWriter bw)
        {
            var node = SerializationNode.WrapForSerialization(obj);
            Save(typeof(SerializationNode), node, bw, new CycleCache(),true);
        }

        /// <summary>
        ///     Recursive save operation
        /// </summary>
        /// <param name="type">The type to save (passed to support NULL)</param>
        /// <param name="instance">The instance to type</param>
        /// <param name="bw">The writer to save it to</param>
        /// <param name="cache">Cycle cache</param>
        /// <param name="saveTypeExplicit">False if the calling method has already stored the object type, otherwise true</param>
        public void Save(Type type, object instance, BinaryWriter bw, CycleCache cache, bool saveTypeExplicit)
        {
            _logManager.Log(SterlingLogLevel.Verbose, string.Format("Sterling is serializing type {0}", type.FullName),
                            null);
            
            // need to indicate to the stream whether or not this is null
            var nullFlag = instance == null;

            _SerializeNull(bw, nullFlag);

            if (nullFlag) return;

            // build the cache for reflection
            if (!_propertyCache.ContainsKey(type))
            {
                //_CacheProperties(type);
                _CacheProperties(type);
            }

            #region upgrade
            var props = new List<SerializationCache>(_propertyCache[type]);
            Debug.WriteLine(string.Format("Writing fields {0} for {1}", props.Count, type.FullName));
            bw.Write(props.Count);
            foreach (var prop in props)
            {
                bw.Write(prop.PropName);
                bw.Write(prop.PropType.AssemblyQualifiedName); //need this so the OnPropertyTypeMismatch can surface it
                Debug.WriteLine("Write " + prop.PropName + " " + prop.PropType.AssemblyQualifiedName);
            }
            #endregion

            if (typeof(Array).IsAssignableFrom(type))
            {
                _SaveArray(bw, cache, instance as Array);
            }
            else if (typeof(IList).IsAssignableFrom(type))
            {
                _SaveList(instance as IList, bw, cache);
            }
            else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                _SaveDictionary(instance as IDictionary, bw, cache);              
            }
            else if (saveTypeExplicit)
            {
                #region wintellect
//                bw.Write(_typeResolver(type.AssemblyQualifiedName));
                #endregion
                #region upgrade
                //bw.Write(type.ToString());
                bw.Write(type.AssemblyQualifiedName);
                Debug.WriteLine("saveTypeExplicit " + type.AssemblyQualifiedName);
                #endregion
            }

            // now iterate the serializable properties - create a copy to avoid multi-threaded conflicts
        #region upgrade

            foreach (var p in props)
            {
                var value = p.GetMethod(instance);
                _InnerSave(value == null ? p.PropType : value.GetType(), value, bw, cache);
            }
        #endregion

            #region wintellect
                //foreach (var p in new List<SerializationCache>(_propertyCache[type]))
                //{
                //    var value = p.GetMethod(instance);
                //    _InnerSave(value == null ? p.PropType : value.GetType(), value, bw, cache);
                //}
            #endregion
        }

        private void _SaveList(IList list, BinaryWriter bw, CycleCache cache)
        {
            _SerializeNull(bw, list == null);

            if (list == null)
            {
                return;
            }

            bw.Write(list.Count);
            foreach(var item in list)
            {
                _InnerSave(item == null ? typeof(string) : item.GetType(), item, bw, cache);
            }
        }

        private void _SaveDictionary(IDictionary dictionary, BinaryWriter bw, CycleCache cache)
        {
            _SerializeNull(bw, dictionary == null);

            if (dictionary == null)
            {
                return;
            }

            bw.Write(dictionary.Count);
            foreach (var item in dictionary.Keys)
            {
                _InnerSave(item.GetType(), item, bw, cache);
                _InnerSave(dictionary[item] == null ? typeof(string) : dictionary[item].GetType(), dictionary[item], bw, cache);
            }
        }

        private void _SaveArray(BinaryWriter bw, CycleCache cache, Array array)
        {
            _SerializeNull(bw, array == null);

            if (array == null)
            {
                return;
            }

            bw.Write(array.Length);
            foreach (var item in array)
            {
                _InnerSave(item == null ? typeof(string) : item.GetType(), item, bw, cache);
            }
        }

        private void _InnerSave(Type type, object instance, BinaryWriter bw,  CycleCache cache)
        {                                    
            if (_database.IsRegistered(type))
            {
                // foreign table - write if it is null or not, and if not null, write the key
                // then serialize it separately
                _SerializeClass(type, instance, bw, cache);
                return;
            }
            
            if (_serializer.CanSerialize(type))
            {
                _SerializeProperty(type, instance, bw);
                return;
            }

            if (instance is Array)
            {
                #region wintellect
                //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
                #endregion
                #region upgrade
                //bw.Write(type.ToString());
                bw.Write(type.AssemblyQualifiedName);
                #endregion
                
                
                _SaveArray(bw, cache, instance as Array);
                return;
            }

            if (typeof(IList).IsAssignableFrom(type))
            {
                #region wintellect
                //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
                #endregion
                #region upgrade
                //bw.Write(type.ToString());
                bw.Write(type.AssemblyQualifiedName);
                #endregion
                _SaveList(instance as IList, bw, cache);                                
                return;
            }

            if (typeof(IDictionary).IsAssignableFrom(type))
            {
                #region wintellect
                //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
                #endregion
                #region upgrade
                //bw.Write(type.ToString());
                bw.Write(type.AssemblyQualifiedName);
                #endregion
                _SaveDictionary(instance as IDictionary, bw, cache);
                return;
            }

            #region wintellect
            //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
            #endregion
            #region upgrade
            //bw.Write(type.ToString());
            bw.Write(type.AssemblyQualifiedName);
            _logManager.Log(SterlingLogLevel.Verbose, string.Format("Sterling _InnerSave {0}", type.AssemblyQualifiedName), null);

            #endregion
            Save(type, instance, bw, cache, false);
        }

        /// <summary>
        ///     Serializes a property
        /// </summary>
        /// <param name="type">The parent type</param>
        /// <param name="propertyValue">The property value</param>
        /// <param name="bw">The writer</param>
        private void _SerializeProperty(Type type, object propertyValue, BinaryWriter bw)
        {
            #region wintellect
            //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
            #endregion
            #region upgrade
            //bw.Write(type.ToString());
            bw.Write(type.AssemblyQualifiedName);
            #endregion

            var isNull = propertyValue == null;
            _SerializeNull(bw, isNull);

            if (isNull)
            {
                return;
            }

            _serializer.Serialize(propertyValue, bw);
        }

        /// <summary>
        ///     Serialize a class
        /// </summary>
        /// <param name="type">The type</param>
        /// <param name="foreignTable">The referenced type</param>
        /// <param name="bw">The writer</param>
        /// <param name="cache">Cycle cache</param>
        private void _SerializeClass(Type type, object foreignTable, BinaryWriter bw, CycleCache cache)
        {
            #region wintellect
            //                bw.Write(_typeResolver(type.AssemblyQualifiedName));
            #endregion
            #region upgrade
            //bw.Write(type.ToString());
            bw.Write(type.AssemblyQualifiedName);
            #endregion

            // serialize to the stream if the foreign key is nulled
            _SerializeNull(bw, foreignTable == null);

            if (foreignTable == null) return;

            var foreignKey = _database.Save(foreignTable.GetType(), foreignTable.GetType(),foreignTable, cache);
            
            // need to be able to serialize the key 
            if (!_serializer.CanSerialize(foreignKey.GetType()))
            {
                var exception = new SterlingSerializerException(_serializer, foreignKey.GetType());
                _logManager.Log(SterlingLogLevel.Error, exception.Message, exception);
                throw exception;
            }

            _logManager.Log(SterlingLogLevel.Verbose,
                            string.Format(
                                "Sterling is saving foreign key of type {0} with value {1} for parent {2}",
                                foreignKey.GetType().FullName, foreignKey, type.FullName), null);

            _serializer.Serialize(foreignKey, bw);            
        }

        /// <summary>
        ///     Helper load for serializers - this is not part of the internal recursion
        ///     Basically allows a node to be saved in a wrapper, and this is the entry
        ///     to unwrap it
        /// </summary>
        /// <typeparam name="T">Type of the object to laod</typeparam>
        /// <param name="br">The reader stream being accessed</param>
        /// <returns>The unwrapped object instance</returns>
        public T Load<T>(BinaryReader br)
        {
            var node = Load(typeof (SerializationNode), null, br, new CycleCache()) as SerializationNode;
            if (node != null)
            {
                return node.UnwrapForDeserialization<T>();
            }
            return default(T);
        }

        /// <summary>
        ///     Recursive load operation
        /// </summary>
        /// <param name="type">The type to save (passed to support NULL)</param>
        /// <param name="key">The associated key (for cycle detection)</param>
        /// <param name="br">The reader</param>
        /// <param name="cache">Cycle cache</param>
        public object Load(Type type, object key, BinaryReader br, CycleCache cache)
        {
            _logManager.Log(SterlingLogLevel.Verbose,
                            string.Format("Sterling is de-serializing type {0}", type.FullName), null);

            if (_DeserializeNull(br))
            {
                return null;
            }
            
            // make a template
            var instance = Activator.CreateInstance(type);

            // build the reflection cache);
            if (!_propertyCache.ContainsKey(type))
            {
                //_CacheProperties(type);
                _CacheProperties(type);
            }

            #region upgrade
            //read field count
            int fieldCount = br.ReadInt32();

            //read fields
            List<Tuple<string, Type, SerializationCache>> fields = new List<Tuple<string, Type, SerializationCache>>();
            //get list of properties to avoide multi-threaded issues
            var props = new List<SerializationCache>(_propertyCache[type]);
            Debug.WriteLine(string.Format("Load<{0}> - reading {1} field definitions",type.GetType(), props.Count));
            for (int i = 0; i < fieldCount; i++)
            {
                string fieldName = br.ReadString();
                string fieldType = br.ReadString();
                var serialization = (from prop in props where prop.PropName == fieldName select prop).FirstOrDefault();
                fields.Add(new Tuple<string, Type, SerializationCache>(fieldName,
                                                                       Type.GetType(fieldType),
                                                                       serialization));
                if (serialization != null)
                {
                    Debug.WriteLine(string.Format("Reading defintion {0} {1} {2} SerializationCache set",i+1,fieldName,fieldType));
                }
                else
                    Debug.WriteLine(string.Format("Reading defintion {0} {1} {2} NO SerializationCache set", i + 1, fieldName, fieldType));
            }
            #endregion

            if (instance is Array)
            {
                // push to the stack
                cache.Add(type, instance, key);
                var isNull = _DeserializeNull(br);

                if (!isNull)
                {
                    var count = br.ReadInt32();
                    for (var x = 0; x < count; x++)
                    {
                        ((Array) instance).SetValue(_Deserialize(br, cache), x);
                    }
                }
            }
            else if (instance is IList)
            {
                // push to the stack
                cache.Add(type, instance, key);
                var isNull = _DeserializeNull(br);
                if (!isNull)
                {
                    _LoadList(br, cache, instance as IList);
                }
            }

            else if (instance is IDictionary)
            {
                // push to the stack
                cache.Add(type, instance, key);
                var isNull = _DeserializeNull(br);
                if (!isNull)
                {
                    _LoadDictionary(br, cache, instance as IDictionary);
                }
            }
            else
            {
                #region wintellect
//                type = Type.GetType(_typeIndexer(br.ReadInt32()));
                #endregion
                #region upgrade

                string s = br.ReadString();
                type = Type.GetType(s);
                #endregion
                if (instance.GetType() != type)
                {
                    Debug.WriteLine(string.Format("Load<{0}> ({1}) -  re-linking field definitions", type.GetType(), instance.GetType()));
                    instance = Activator.CreateInstance(type);
                    #region upgrade
                    if (!_propertyCache.ContainsKey(instance.GetType()))
                    {
                        //_CacheProperties(type);
                        _CacheProperties(instance.GetType());
                    }
                    List<Tuple<string, Type, SerializationCache>> newFields = new List<Tuple<string, Type, SerializationCache>>();
                    var newProps = new List<SerializationCache>(_propertyCache[instance.GetType()]);
                    foreach (Tuple<string, Type, SerializationCache> field in fields)
                    {
                        var serialization = (from prop in newProps where prop.PropName == field.Item1 select prop).FirstOrDefault();
                        newFields.Add(new Tuple<string, Type, SerializationCache>(field.Item1,field.Item2,serialization));
                        if (serialization != null)
                        {
                            Debug.WriteLine(string.Format("Re-linking defintion {0} {1} SerializationCache set", field.Item1, field.Item2));
                        }
                        else
                            Debug.WriteLine(string.Format("Re-linking defintion {0} {1} NO SerializationCache set", field.Item1, field.Item2));
                    }
                    fields.Clear();
                    fields.AddRange(newFields);
                    #endregion

                }

                // push to the stack
                cache.Add(type, instance, key);

                // build the reflection cache);
                if (!_propertyCache.ContainsKey(type))
                {
                    //_CacheProperties(type);
                    _CacheProperties(type);
                }
            }

            #region upgrade
            foreach (Tuple<string, Type, SerializationCache> field in fields)
            {
                if (field.Item3 == null)
                {
                    //need to read a now unused field
                    _Deserialize(br, cache); 
                    continue;
                }
                bool ignore = false;
                object value = _Deserialize(br, cache);
                if (value != null)
                    if (value.GetType() != field.Item2)
                    {
                        //fire an on property type mismatch event with Ignore flag
                        //will need surfacing into BaseDataBaseInstance, but not doing so at the moment so only one file is changed
                        //allows the developer to do a conversion or ignore
                        //if (OnPropertyTypeMismatch != null)
                        // OnPropertyTypeMismatch(field.Item1,Field.Item2, value.GetType, value, var ignore)
                    }
                if (ignore)
                    continue;
                field.Item3.SetMethod(instance, value);
            }
            #endregion

            
            // now iterate
            #region sterling
            //foreach (var p in new List<SerializationCache>(_propertyCache[type]))
            //{
            //    p.SetMethod(instance, _Deserialize(br, cache));
            //}
            #endregion

            return instance;
        }

        private object _Deserialize(BinaryReader br, CycleCache cache)
        {
            #region wintellect
            //var typeName = _typeIndexer(br.ReadInt32());
            #endregion
            #region upgrade
            var typeName = br.ReadString();
            Debug.WriteLine(string.Format("Sterling _Deserialize {0}", typeName));
            #endregion

            if (_DeserializeNull(br))
            {
                return null;
            }

            Type typeResolved = null;


            #region sterling

            if (!_typeRef.TryGetValue(typeName, out typeResolved))
            {
                typeResolved = Type.GetType(typeName);

                lock (((ICollection)_typeRef).SyncRoot)
                {
                    if (!_typeRef.ContainsKey(typeName))
                    {
                        _typeRef.Add(typeName, typeResolved);
                    }
                }
            }
            #endregion

            if (_database.IsRegistered(typeResolved))
            {
                var keyType = _database.GetKeyType(typeResolved);
                var key = _serializer.Deserialize(keyType, br);

                var cached = cache.CheckKey(keyType, key);
                if (cached != null)
                {
                    return cached;
                }

                cached = _database.Load(typeResolved, key, cache);
                cache.Add(typeResolved, cached, key);
                return cached;
            }

            if (_serializer.CanSerialize(typeResolved))
            {
                return _serializer.Deserialize(typeResolved, br);
            }

            
            if (typeof(Array).IsAssignableFrom(typeResolved))
            {                
                var count = br.ReadInt32();
                var array = Array.CreateInstance(typeResolved.GetElementType(), count);
                for (var x = 0; x < count; x++)
                {
                    array.SetValue(_Deserialize(br, cache), x);
                }

                return array;
            }

            if (typeof (IList).IsAssignableFrom(typeResolved))
            {
                var list = Activator.CreateInstance(typeResolved) as IList;
                return _LoadList(br, cache, list);               
            }

            if (typeof (IDictionary).IsAssignableFrom(typeResolved))
            {
                var dictionary = Activator.CreateInstance(typeResolved) as IDictionary;
                return _LoadDictionary(br, cache, dictionary);
            }            

            var instance = Activator.CreateInstance(typeResolved);

            // build the reflection cache);
            if (!_propertyCache.ContainsKey(typeResolved))
            {
                //_CacheProperties(type);
                _CacheProperties(typeResolved);
            }

            #region upgrade
            //read field count
            int fieldCount = br.ReadInt32();

            //read fields
            List<Tuple<string, Type, SerializationCache>> fields = new List<Tuple<string, Type, SerializationCache>>();
            //get list of properties to avoide multi-threaded issues
            var props = new List<SerializationCache>(_propertyCache[typeResolved]);
            Debug.WriteLine(string.Format("Load - reading {0} fields", props.Count));
            for (int i = 0; i < fieldCount; i++)
            {
                string fieldName = br.ReadString();
                string fieldType = br.ReadString();
                var serialization = (from prop in props where prop.PropName == fieldName select prop).FirstOrDefault();
                fields.Add(new Tuple<string, Type, SerializationCache>(fieldName,
                                                                       Type.GetType(fieldType),
                                                                       serialization));
                if (serialization != null)
                {
                    Debug.WriteLine(string.Format("Loading field {0} {1} SerializationCache set", fieldName, fieldType));
                }
                else
                    Debug.WriteLine(string.Format("Loading field {0} {1} NO SerializationCache set", fieldName, fieldType));
            }
            #endregion


            #region upgrade
            foreach (Tuple<string, Type, SerializationCache> field in fields)
            {
                if (field.Item3 == null)
                {
                    //need to read a now used field
                    _Deserialize(br, cache);
                    continue;
                } 
                bool ignore = false;
                object value = _Deserialize(br, cache);
                if (value != null)
                    if (value.GetType() != field.Item2)
                    {
                        //fire an on property type mismatch event with Ignore flag
                        //will need surfacing into BaseDataBaseInstance, but not doing so at the moment so only one file is changed
                        //allows the developer to do a conversion or ignore
                        //if (OnPropertyTypeMismatch != null)
                        // OnPropertyTypeMismatch(field.Item1,Field.Item2, value.GetType, value, var ignore)
                    }
                if (ignore)
                    continue;
                field.Item3.SetMethod(instance, value);
            }
            #endregion


            // now iterate
            #region sterling
            //foreach (var p in new List<SerializationCache>(_propertyCache[type]))
            //{
            //    p.SetMethod(instance, _Deserialize(br, cache));
            //}
            #endregion

            return instance;
        }
        
        private IDictionary _LoadDictionary(BinaryReader br, CycleCache cache, IDictionary dictionary)
        {            
            var count = br.ReadInt32();
            for (var x = 0; x < count; x++)
            {
                dictionary.Add(_Deserialize(br, cache), _Deserialize(br, cache));
            }
            return dictionary;
        }

        private IList _LoadList(BinaryReader br, CycleCache cache, IList list)
        {
            var count = br.ReadInt32();
            for (var x = 0; x < count; x++)
            {
                list.Add(_Deserialize(br, cache));
            }
            return list;
        }

        private void _SerializeNull(BinaryWriter bw, bool isNull)
        {
            bw.Write(isNull ? NULL : NOTNULL);
            _logManager.Log(SterlingLogLevel.Verbose, string.Format("{0}", isNull ? NULL_DISPLAY : NOTNULL_DISPLAY), null);
        }    

        private bool _DeserializeNull(BinaryReader br)
        {
            var nullFlag = br.ReadUInt16();
            var isNull = nullFlag == NULL;
            _logManager.Log(SterlingLogLevel.Verbose, string.Format("{0}", isNull ? NULL_DISPLAY : NOTNULL_DISPLAY),null);
            return isNull;
        }
    }
}

 

 

 

Paul

 

 

 

 

 

 

Coordinator
Jun 11, 2011 at 3:41 PM

This is similar to the approach the team is discussion to resolve. The issue is that writing the properties every save is a lot of overhead - it will not only slow the process but also increase the size on disk, and in some cases make the property signature bigger than the actual records being saved. What we're looking at is a versioned approach where the properties are saved with a version number, and only the version number needs to be written in the stream. If the version number doesn't match, the new set of properties can be inspected. When the property cache is being built, the current version is inspected and if there is a mismatch, a new version written out and all saves are flagged with that. Furthermore, the save would provide an optional overload delegate for conversion from a previous version to the current version - the delegate would take a property and value (null if the property is new) and then your code could convert to the new property or return a default if it's a new property. Old properties would get dropped on the floor.

Jun 13, 2011 at 9:15 AM

I agree completely with all properties being written for each object being an overhead in speed and size, I only prototyped this way as a proof of concept.

It should def change back to using _typeResolver (using the assembly version number of the class saved) as per point 2.
similair to kallocain suggested code of TableTypeResolver http://sterling.codeplex.com/discussions/239428 perhaps?

Paul