/// <summary>
        ///     Factory method used to create a precompiled type description.
        ///     This version of the method uses an external description (no need to attach attributes to the public properties)
        ///     In order to be cacheable, a type must be serializable and must have exactly one primary key
        ///     Optionally it can have multiple unique keys and index keys
        /// </summary>
        /// <param name="type"> </param>
        /// <param name="typeDescription"> </param>
        /// <returns> </returns>
        public static ClientSideTypeDescription RegisterType(Type type, TypeDescriptionConfig typeDescription)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (!type.IsSerializable)
            {
                throw new NotSupportedException(
                          $"the type {type} is not serializable so it can not be registered as cacheable type");
            }

            var result = new ClientSideTypeDescription
            {
                TypeName       = type.Name,
                FullTypeName   = type.FullName,
                UseCompression = typeDescription.UseCompression
            };

            var props = type.GetProperties();

            foreach (var info in props)
            {
                if (typeDescription.Keys.ContainsKey(info.Name))
                {
                    var propertyDescription = typeDescription.Keys[info.Name];

                    result._propertyDescriptionByName[info.Name] = propertyDescription;

                    var key = new ClientSideKeyInfo(info, propertyDescription);

                    if (key.KeyType == KeyType.None)
                    {
                        continue;
                    }

                    if (key.KeyType == KeyType.Primary)
                    {
                        result.PrimaryKeyField = key;
                    }
                    else if (key.KeyType == KeyType.Unique)
                    {
                        result._uniqueKeyFields.Add(key);
                    }
                    else if (key.KeyType == KeyType.ScalarIndex)
                    {
                        result._indexFields.Add(key);
                    }
                    else if (key.KeyType == KeyType.ListIndex)
                    {
                        result._listFields.Add(key);
                    }
                }
            }

            //check if the newly registered type is valid
            if (result.PrimaryKeyField == null)
            {
                throw new NotSupportedException($"no primary key defined for type {type}");
            }

            return(result);
        }
        /// <summary>
        ///     Factory method used to create a precompiled type description.
        ///     This version of the method uses a tagged type ( Attributes are attached to the public properties
        ///     which are indexed in the cache)
        ///     In order to be cacheable, a type must be serializable and must have exactly one primary key
        ///     Optionally it can have multiple unique keys and index keys
        /// </summary>
        /// <param name="type"> type to register (must be properly decorated) </param>
        /// <returns> not null type description if successful </returns>
        public static ClientSideTypeDescription RegisterType(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }


            var useCompression = false;
            var storage        = type.GetCustomAttributes(typeof(StorageAttribute), false).FirstOrDefault();

            if (storage != null)
            {
                var storageParams = (StorageAttribute)storage;
                useCompression = storageParams.UseCompression;
            }

            var result = new ClientSideTypeDescription
            {
                UseCompression = useCompression,
                TypeName       = type.Name,
                FullTypeName   = type.FullName
            };

            var props = type.GetProperties();

            foreach (var info in props)
            {
                var key = new ClientSideKeyInfo(info);

                if (key.KeyType == KeyType.None)
                {
                    continue;
                }

                if (key.KeyType == KeyType.Primary)
                {
                    result.PrimaryKeyField = key;
                }
                else if (key.KeyType == KeyType.Unique)
                {
                    result._uniqueKeyFields.Add(key);
                }
                else if (key.KeyType == KeyType.ScalarIndex)
                {
                    result._indexFields.Add(key);
                }
                else if (key.KeyType == KeyType.ListIndex)
                {
                    result._listFields.Add(key);
                }


                var propertyDescription = new PropertyDescription
                {
                    PropertyName = key.Name,
                    KeyDataType  = key.KeyDataType,
                    KeyType      = key.KeyType,
                    Ordered      = key.IsOrdered
                };

                result._propertyDescriptionByName[key.Name] = propertyDescription;
            }

            //check if the newly registered type is valid
            if (result.PrimaryKeyField == null)
            {
                throw new NotSupportedException($"No primary key defined for type {type}");
            }

            return(result);
        }