csharp/AlenToma/EntityWorker.Core/Source/EntityWorker.Core/Helper/Extension.cs

Extension.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using EnsatyWorker.Core.Attributes;
using EnsatyWorker.Core.Interface;
using EnsatyWorker.Core.Object.Library;
using System.Text.RegularExpressions;
using EnsatyWorker.Core.Object.Library.JSON;
using EnsatyWorker.Core.InterFace;
using EnsatyWorker.Core.SqlQuerys;
using EnsatyWorker.Core.Object.Library.XML;
using FastDeepCloner;

namespace EnsatyWorker.Core.Helper
{
    /// 
    /// UseFull Extension, that work with EnsatyWorker.Core
    /// 
    public static clast Extension
    {
        private static readonly SafeValueType CachedPropertyNames = new SafeValueType();
        private static readonly SafeValueType CachedPrimaryKeys = new SafeValueType();
        internal static readonly SafeValueType CachedTableNames = new SafeValueType();

        private static readonly SafeValueType DbMsSqlMapper = new SafeValueType()
        {
            {typeof(int), new List(){ "BIGINT" , "int", "single", "smallint", "tinyint" } },
            {typeof(long), new List(){ "BIGINT" } },
            {typeof(string), new List(){ "NVARCHAR(MAX)" , "varchar", "xml" } },
            {typeof(bool), new List(){ "BIT"} },
            {typeof(DateTime), new List(){ "DATETIME" , "date", "datetime2", "datetimeoffset", "smalldatetime" } },
            {typeof(TimeSpan), new List(){ "DATETIME" , "time" } },
            {typeof(float), new List(){ "FLOAT"} },
            {typeof(decimal), new List(){ "DECIMAL(18,5)", "money" , "numeric", "smallmoney" } },
            {typeof(Guid), new List(){ "UNIQUEIDENTIFIER"} },
            {typeof(byte[]), new List(){ "varbinary(MAX)" , "image" , "rowversion", "timestamp" } },
            {typeof(char), new List(){ "NVARCHAR(10)", "char" , "nchar" , "ntext" } },
            {typeof(double), new List(){ "DECIMAL(18,5)", "money" , "numeric", "smallmoney" } },
        };

        private static readonly SafeValueType DbSQLiteMapper = new SafeValueType()
        {
            {typeof(int), new List(){ "BIGINT" , "SMALLINT", "TINYINT", "MEDIUMINT", "UNSIGNED BIG INT", "INT2", "INT8" } },
            {typeof(long), new List(){ "BIGINT", "INT", "INTEGER"}},
            {typeof(string),  new List(){"NVARCHAR(4000)", "CHARACTER","VARYING CHARACTER", "NCHAR", "NATIVE CHARACTER" , "CLOB", "TEXT" }},
            {typeof(bool), new List(){ "BIT"}},
            {typeof(DateTime), new List(){ "DATETIME", "date"}},
            {typeof(TimeSpan), new List(){ "DATETIME"}},
            {typeof(float),  new List(){"FLOAT"}},
            {typeof(decimal), new List(){ "DECIMAL(18,5)", "real" , "NUMERIC", "DOUBLE PRECISION"}},
            {typeof(Guid), new List(){ "UNIQUEIDENTIFIER"}},
            {typeof(byte[]), new List(){ "BLOB"}},
            {typeof(char), new List(){ "NVARCHAR(10)"}},
            {typeof(double), new List(){ "DECIMAL(18,5)", "real" , "NUMERIC", "DOUBLE PRECISION"}},
        };

        private static readonly SafeValueType DbPostGresqlMapper = new SafeValueType()
        {
            {typeof(int), new List(){ "BIGINT", "smallint", "integer", "smallserial", "serial"}},
            {typeof(long), new List(){ "BIGINT", "bigserial" } },
            {typeof(string), new List(){ "TEXT", "varchar" , "character varying", "character", "json", "enum" } },
            {typeof(bool), new List { "BOOLEAN" } },
            {typeof(DateTime), new List(){ "TIMESTAMP" , "timestamp", "date", "interval" } },
            {typeof(TimeSpan), new List(){ "TIME" } },
            {typeof(float),new List(){ "FLOAT"} },
            {typeof(decimal), new List(){ "DECIMAL(18,5)", "numeric" , "real" , "money"  } },
            {typeof(Guid), new List(){ "uuid"} },
            {typeof(byte[]), new List(){ "bytea"} },
            {typeof(char), new List(){ "VARCHAR(10)", "char"} },
            {typeof(double), new List(){ "DECIMAL(18,5)", "numeric" , "real" , "money"  } },
        };


        internal static string GetValidSqlName(this DataBaseTypes dbtype, string col)
        {
            return dbtype == DataBaseTypes.PostgreSql ? col : "[" + col + "]";
        }

        internal static string CleanValidSqlName(this string col, DataBaseTypes dbtype)
        {
            if (dbtype == DataBaseTypes.PostgreSql)
                return col.Replace("[", "").Replace("]", "");
            return col;
        }

        internal static string CleanName(this string name)
        {
            return new Regex("[^a-zA-Z0-9,_]").Replace(name, "_");
        }

        /// 
        /// Convert To Json
        /// 
        /// 
        /// The Default is GlobalConfigration.JSONParameters
        /// 
        public static string ToJson(this object o, JSONParameters param = null)
        {
            return JSON.ToNiceJSON(o, param ?? GlobalConfiguration.JSONParameters);
        }


        /// 
        /// Convert Json to dynamic object
        /// 
        /// 
        /// 
        /// 
        public static dynamic FromJsonToDynamic(this string json)
        {
            return JSON.ToDynamic(json);
        }

        /// 
        /// generic Json to object
        /// 
        /// 
        /// 
        /// The Default is GlobalConfigration.JSONParameters
        /// 
        public static T FromJson(this string json, JSONParameters param = null)
        {
            return JSON.ToObject(json, param ?? GlobalConfiguration.JSONParameters);
        }

        /// 
        /// generic Json to object
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        internal static T FromJson(this string json, IRepository repository, JSONParameters param = null)
        {

            var o = JSON.ToObject(json, param ?? GlobalConfiguration.JSONParameters);

            void LoadJsonIgnoreProperties(object item)
            {
                if (item is IList)
                {
                    foreach (var t in (IList)item)
                        LoadJsonIgnoreProperties(t);
                    return;
                }

                var type = item?.GetType().GetActualType();
                if (type == null)
                    return;
                if (!(item?.GetPrimaryKeyValue().ObjectIsNew() ?? true))
                {
                    var primaryId = item.GetPrimaryKeyValue();
                    foreach (var prop in DeepCloner.GetFastDeepClonerProperties(item.GetType()).Where(x => (x.ContainAttribute() || !x.IsInternalType) && !x.ContainAttribute() && x.CanRead))
                    {
                        var value = prop.GetValue(item);
                        if (prop.PropertyType == typeof(string) && string.IsNullOrEmpty(value?.ToString()))
                            value = string.Empty;
                        if (prop.IsInternalType && value == LightDataTableShared.ValueByType(prop.PropertyType)) // Value is default
                        {
                            var cmd = repository.GetSqlCommand($"SELECT [{prop.GetPropertyName()}] FROM {type.TableName().GetName(repository.DataBaseTypes)} WHERE [{item.GetPrimaryKey().GetPropertyName()}] = {Querys.GetValueByType(item.GetPrimaryKeyValue(), repository.DataBaseTypes)}");
                            var data = repository.ExecuteScalar(cmd);
                            if (data == null)
                                continue;
                            if (prop.ContainAttribute())
                                data = new DataCipher(prop.GetCustomAttribute().Key, prop.GetCustomAttribute().KeySize).Decrypt(data.ToString());
                            else if (prop.ContainAttribute() && data.ToString().IsBase64String())
                                data = MethodHelper.DecodeStringFromBase64(data.ToString());
                            else if (prop.ContainAttribute())
                                data = data?.ToString().FromJson(prop.PropertyType);
                            else if (prop.ContainAttribute())
                                data = data?.ToString().FromXml();
                            prop.SetValue(item, data.ConvertValue(prop.PropertyType));
                        }
                        else if (value != null) LoadJsonIgnoreProperties(value);
                    }

                }
            }

            LoadJsonIgnoreProperties(o);
            return o;
        }

        /// 
        /// Json to object
        /// 
        /// 
        /// 
        /// 
        /// The Default is GlobalConfigration.JSONParameters
        /// 
        public static object FromJson(this string json, Type type, JSONParameters param = null)
        {
            return JSON.ToObject(json, type, param ?? GlobalConfiguration.JSONParameters);
        }

        /// 
        /// Json to object
        /// json must containe the $type
        /// 
        /// 
        /// 
        /// The Default is GlobalConfigration.JSONParameters
        /// 
        public static object FromJson(this string json, JSONParameters param)
        {
            return JSON.ToObject(json, param ?? GlobalConfiguration.JSONParameters);
        }

        /// 
        /// Xml to object
        /// 
        /// 
        /// 
        ///  astign repository to load XmlIgnored properties
        /// 
        public static T FromXml(this string xml, IRepository repository = null) where T : clast
        {
            return XmlUtility.FromXml(xml, repository);
        }

        /// 
        /// Xml to object
        /// 
        /// 
        /// 
        ///  astign repository to load XmlIgnored properties
        /// 
        public static object FromXml(this string xml, IRepository repository = null)
        {
            return XmlUtility.FromXml(xml, repository);
        }

        /// 
        /// Object to xml
        /// 
        /// 
        /// 
        public static string ToXml(this object o)
        {
            return XmlUtility.ToXml(o);
        }

        /// 
        /// Get PropertyName of the expression
        /// 
        /// 
        /// 
        /// 
        /// 
        public static string GetMemberName(this Expression action)
        {
            var member = action.Body is UnaryExpression ? ((MemberExpression)((UnaryExpression)action.Body).Operand) : (action.Body is MethodCallExpression ? ((MemberExpression)((MethodCallExpression)action.Body).Object) : (MemberExpression)action.Body);
            return member?.Member.Name;
        }

        /// 
        /// Clear all ids
        /// 
        /// 
        /// 
        ///  Clear all ids of object that contains IndependedData attributes
        public static T ClearAllIdsHierarchy(this T item, bool includeIndependedData = false)
        {
            return (T)ClearAllIdsHierarchy(item as object, includeIndependedData);
        }

        /// 
        /// Clear all ids
        /// 
        /// 
        /// 
        ///  Clear all ids of object that contains IndependedData attributes
        public static List ClearAllIdsHierarchy(List item, bool includeIndependedData = false)
        {
            return (List)ClearAllIdsHierarchy(item as object, includeIndependedData);
        }

        /// 
        /// Clear all PrimaryId and ForeignKey
        /// 
        /// 
        /// 
        ///  Clear all ids of object that contains IndependedData attributes
        public static object ClearAllIdsHierarchy(object item, bool includeIndependedData = false)
        {
            if (item == null)
                return item;
            if (item is IList)
            {
                foreach (var tm in item as IList)
                {
                    if (tm != null)
                        ClearAllIdsHierarchy(tm, includeIndependedData);
                }
            }
            else
            {
                if (item == null)
                    return item;

                var props = DeepCloner.GetFastDeepClonerProperties(item.GetType());
                foreach (var p in props)
                {
                    if (p.ContainAttribute() || (p.ContainAttribute() && !includeIndependedData))
                        continue;
                    if (!p.IsInternalType)
                    {
                        ClearAllIdsHierarchy(p.GetValue(item), includeIndependedData);
                        continue;
                    }
                    else if (p.ContainAttribute() || p.ContainAttribute())
                        p.SetValue(item, MethodHelper.ConvertValue(null, p.PropertyType)); // Reset to default

                }
            }

            return item;
        }

        /// 
        /// TrimEnd with string
        /// 
        /// 
        /// 
        /// 
        public static string TrimEnd(this string source, string value)
        {
            return !source.EndsWith(value) ? source : source.Remove(source.LastIndexOf(value, StringComparison.Ordinal));
        }

        /// 
        /// Try and insert Last
        /// 
        /// Source string
        ///  string to insert
        /// after last identified string 
        /// insert after a specific string, count is from last, even if the identifier text not found
        /// 
        public static string InsertAfter(this string str, string text, string identifier, bool insertLastIfNotFound = true)
        {
            var txt = "";
            var found = false;
            for (var j = str.Length - 1; j >= 0; j--)
            {
                txt = str[j] + txt;
                if (txt == identifier)
                {
                    str = str.Insert(j, text);
                    found = true;
                    break;
                }
                else if (txt.Length >= identifier.Length || txt.Last() != identifier.Last() || (txt.Length >= 2 && identifier.Length >= 2 && txt[txt.Length - 2] != identifier[identifier.Length - 2]))
                    txt = "";
            }

            if (!found && insertLastIfNotFound)
                str += text;
            return str;
        }

        /// 
        /// Try to insert 
        /// 
        /// Source
        /// string to insert
        /// 
        /// 
        /// 
        public static string InsertBefore(this string str, string text, string identifier, bool insertLastIfNotFound = true)
        {
            var txt = "";
            var found = false;
            for (var j = str.Length - 1; j >= 0; j--)
            {
                txt = str[j] + txt;
                if (txt == identifier)
                {
                    str = str.Insert(j, text);
                    found = true;
                    break;
                }
                else if (txt.Length >= identifier.Length || txt.Last() != identifier.Last() || (txt.Length >= 2 && identifier.Length >= 2 && txt[txt.Length - 2] != identifier[identifier.Length - 2]))
                    txt = "";
            }
            if (!found && insertLastIfNotFound)
                str += text;

            return str;
        }


        /// 
        /// Generate an ensatyKey. Primary Id cant be null or empty
        /// 
        /// 
        /// 
        public static string EnsatyKey(this object ensaty) => ensaty.GetType().GetActualType().FullName + ensaty.GetPrimaryKeyValue()?.ToString();

        /// 
        /// Generate an ensatyKey Primary Id cant be null or empty
        /// 
        /// ensatytyp
        /// 
        /// 
        public static string EnsatyKey(this Type type, object id) => type.GetActualType().FullName + id.ToString();

        /// 
        /// Search and insert before identifier
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static StringBuilder InsertBefore(this StringBuilder str, string text, string identifier, bool insertLastIfNotFound = true)
        {
            str = new StringBuilder(str.ToString().InsertBefore(text, identifier, insertLastIfNotFound));
            return str;
        }

        /// 
        /// Convert System Type to SqlType
        /// 
        /// 
        /// 
        /// 
        public static string GetDbTypeByType(this Type type, DataBaseTypes dbType)
        {
            if (type.GetTypeInfo().IsEnum)
                type = typeof(long);


            if (Nullable.GetUnderlyingType(type) != null)
                type = Nullable.GetUnderlyingType(type);
            if (dbType == DataBaseTypes.Mssql)
                return DbMsSqlMapper.ContainsKey(type) ? DbMsSqlMapper[type].First() : null;
            else if (dbType == DataBaseTypes.Sqllight)
                return DbSQLiteMapper.ContainsKey(type) ? DbSQLiteMapper[type].First() : null;
            else return DbPostGresqlMapper.ContainsKey(type) ? DbPostGresqlMapper[type].First() : null;
        }

        /// 
        /// Convert System Type to SqlType
        /// 
        /// 
        /// 
        /// 
        public static string GetDbTypeByType(this IFastDeepClonerProperty prop, DataBaseTypes dbType)
        {
            var type = prop.PropertyType;

            if (prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute())
                return typeof(string).GetDbTypeByType(dbType);

            if (prop.ContainAttribute() && prop.Attributes.Any(x => x is ColumnType && !string.IsNullOrEmpty((x as ColumnType).DataType) && ((x as ColumnType).DataBaseTypes == dbType) || !(x as ColumnType).DataBaseTypes.HasValue))
                return prop.Attributes.Where(x => x is ColumnType && !string.IsNullOrEmpty((x as ColumnType).DataType) && ((x as ColumnType).DataBaseTypes == dbType) || !(x as ColumnType).DataBaseTypes.HasValue).Select(x => (x as ColumnType).DataType).First();

            if (type.GetTypeInfo().IsEnum)
                type = typeof(long);

            if (Nullable.GetUnderlyingType(type) != null)
                type = Nullable.GetUnderlyingType(type);
            if (dbType == DataBaseTypes.Mssql)
                return DbMsSqlMapper.ContainsKey(type) ? DbMsSqlMapper[type].First() : null;
            else if (dbType == DataBaseTypes.Sqllight)
                return DbSQLiteMapper.ContainsKey(type) ? DbSQLiteMapper[type].First() : null;
            else return DbPostGresqlMapper.ContainsKey(type) ? DbPostGresqlMapper[type].First() : null;
        }


        /// 
        /// Convert System Type to SqlType
        /// 
        /// 
        /// 
        /// 
        public static List GetDbTypeListByType(this IFastDeepClonerProperty prop, DataBaseTypes dbType)
        {
            var type = prop.PropertyType;

            if (prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute() ||
                prop.ContainAttribute())
                return new List() { typeof(string).GetDbTypeByType(dbType) };

            if (prop.ContainAttribute() && prop.Attributes.Any(x => x is ColumnType && !string.IsNullOrEmpty((x as ColumnType).DataType) && ((x as ColumnType).DataBaseTypes == dbType) || !(x as ColumnType).DataBaseTypes.HasValue))
                return prop.Attributes.Where(x => x is ColumnType && !string.IsNullOrEmpty((x as ColumnType).DataType) && ((x as ColumnType).DataBaseTypes == dbType) || !(x as ColumnType).DataBaseTypes.HasValue).Select(x => (x as ColumnType).DataType).ToList();

            if (type.GetTypeInfo().IsEnum)
                type = typeof(long);

            if (Nullable.GetUnderlyingType(type) != null)
                type = Nullable.GetUnderlyingType(type);
            if (dbType == DataBaseTypes.Mssql)
                return DbMsSqlMapper.ContainsKey(type) ? DbMsSqlMapper[type] : null;
            else if (dbType == DataBaseTypes.Sqllight)
                return DbSQLiteMapper.ContainsKey(type) ? DbSQLiteMapper[type] : null;
            else return DbPostGresqlMapper.ContainsKey(type) ? DbPostGresqlMapper[type] : null;
        }

        /// 
        /// if date is between two dates
        /// 
        /// 
        /// 
        /// 
        /// 
        public static bool Between(DateTime input, DateTime date1, DateTime date2)
        {
            return (input > date1 && input < date2);
        }

        /// 
        /// Get PropertyName from the cashed Properties
        /// 
        /// 
        /// 
        public static string GetPropertyName(this IFastDeepClonerProperty prop)
        {
            if (CachedPropertyNames.ContainsKey(prop))
                return CachedPropertyNames[prop];

            return CachedPropertyNames.GetOrAdd(prop, (prop.GetCustomAttribute()?.Name ?? prop.Name).CleanName());
        }

        /// 
        /// Type is numeric eg long, decimal or float
        /// 
        /// 
        /// 
        public static bool IsNumeric(this Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable))
                type = type.GetGenericArguments().FirstOrDefault() ?? type;
            return type.IsPrimitive && type != typeof(char) && type != typeof(bool);
        }

        /// 
        /// Get the Primary key from type
        /// 
        /// 
        /// 
        public static IFastDeepClonerProperty GetPrimaryKey(this Type type)
        {
            type = type.GetActualType();
            if (CachedPrimaryKeys.ContainsKey(type))
                return CachedPrimaryKeys[type];
            return CachedPrimaryKeys.GetOrAdd(type, DeepCloner.GetFastDeepClonerProperties(type).FirstOrDefault(x => x.ContainAttribute()));
        }

        /// 
        /// The value of attribute Table
        /// 
        /// 
        /// 
        public static Table TableName()
        {
            return typeof(T).TableName();
        }

        /// 
        /// The value of attribute Table
        /// 
        /// 
        /// 
        public static Table TableName(this Type type)
        {
            if (CachedTableNames.ContainsKey(type))
                return CachedTableNames[type];
            return CachedTableNames.GetOrAdd(type, (type.GetCustomAttribute() ?? new Table(type.Name)));
        }

        /// 
        /// Get the Primary key from type
        /// 
        /// 
        /// 
        /// 
        public static IFastDeepClonerProperty GetPrimaryKey(this object item)
        {
            return item.GetType().GetPrimaryKey();
        }

        /// 
        /// Get the Primary key Value
        /// 
        /// 
        /// 
        public static object GetPrimaryKeyValue(this object item)
        {
            return item?.GetPrimaryKey()?.GetValue(item);
        }

        /// 
        /// Validate string and guid and long 
        /// 
        /// 
        /// 

        public static bool ObjectIsNew(this object value)
        {
            if (value == null || (value.GetType().IsNumeric() && value.ConvertValue()  ((IList)x.ToObject())[0]).ToList());
            else return new LightDataTable(o).Rows.First().ToObject();
        }

        /// 
        /// Clone Object, se FastDeepCloner fo more information
        /// 
        /// 
        /// 
        /// 
        /// 
        public static List Clone(this List items, FieldType fieldType = FieldType.PropertyInfo)
        {
            return DeepCloner.Clone(items, new FastDeepClonerSettings()
            {
                FieldType = fieldType,
                OnCreateInstance = new Extensions.CreateInstance(FormatterServices.GetUninitializedObject)
            });
        }


        internal static List ConvertExpressionToIncludeList(this Expression[] actions, bool onlyLast = false)
        {
            var result = new List();
            if (actions == null) return result;
            foreach (var exp in actions)
            {
                var tempList = new List();
                var expString = exp.ToString().Split('.');
                var propTree = "";
                foreach (var item in expString)
                {
                    var x = item.Trim().Replace("(", "").Replace(")", "").Replace("&&", "").Replace("||", "").Trim();
                    if (x.Any(char.IsWhiteSpace) || x.Contains("="))
                        continue;
                    propTree += ("." + x);
                    if (propTree.Split('.').Length == 4)
                    {
                        propTree = string.Join(".", propTree.Split('.').ToList().Skip(2).Cast().ToArray());

                    }
                    tempList.Add(propTree.TrimStart('.'));
                }

                if (!onlyLast)
                    result.AddRange(tempList);
                else if (tempList.Any())
                {
                    var str = tempList.Last();
                    str = str?.Split('.').Length >= 2 ? string.Join(".", str.Split('.').Reverse().Take(2).Cast().Reverse()) : str;
                    result.Add(str);
                }
            }
            return result;
        }



        internal static Type TypeByTypeAndDbIsNull(this Type propertyType, bool allowDbNull)
        {
            if (propertyType == typeof(int))
                return allowDbNull ? typeof(int?) : typeof(int);

            if (propertyType == typeof(float))
                return allowDbNull ? typeof(int?) : typeof(int);

            if (propertyType == typeof(byte))
                return allowDbNull ? typeof(byte?) : typeof(byte);

            if (propertyType == typeof(decimal))
                return allowDbNull ? typeof(decimal?) : typeof(decimal);

            if (propertyType == typeof(double))
                return allowDbNull ? typeof(double?) : typeof(double);

            if (propertyType == typeof(float))
                return allowDbNull ? typeof(float?) : typeof(float);

            if (propertyType == typeof(DateTime))
                return allowDbNull ? typeof(DateTime?) : typeof(DateTime);

            if (propertyType == typeof(long))
                return allowDbNull ? typeof(long?) : typeof(long);

            if (propertyType == typeof(TimeSpan))
                return allowDbNull ? typeof(TimeSpan?) : typeof(TimeSpan);

            if (propertyType == typeof(bool))
                return allowDbNull ? typeof(bool?) : typeof(bool);

            if (propertyType == typeof(Guid))
                return allowDbNull ? typeof(Guid?) : typeof(Guid);

            if (propertyType == typeof(byte))
                return allowDbNull ? typeof(byte?) : typeof(byte);

            return propertyType == typeof(byte[]) ? typeof(byte[]) : typeof(string);
        }


        internal static List DataReaderConverter(Transaction.Transaction repository, IDataReader reader, ISqlCommand command)
        {
            return ((List)DataReaderConverter(repository, reader, command, typeof(T)));
        }

        internal static IList DataReaderConverter(Transaction.Transaction repository, IDataReader reader, ISqlCommand command, Type type)
        {
            var tType = type.GetActualType();
            var attachable = tType.GetPrimaryKey() != null;
            var baseListType = typeof(List);
            var listType = baseListType.MakeGenericType(tType);
            var iList = DeepCloner.CreateInstance(listType) as IList;
            var props = DeepCloner.GetFastDeepClonerProperties(tType);
            try
            {
                var colNames = new SafeValueType();
                var pp = new SafeValueType();
                while (reader.Read())
                {
                    object item = null;
                    object replacedem = null;

                    item = DeepCloner.CreateInstance(tType);
                    replacedem = attachable ? DeepCloner.CreateInstance(tType) : null;
                    var col = 0;

                    while (col < reader.FieldCount)
                    {
                        string columnName;
                        if (colNames.ContainsKey(col))
                            columnName = colNames[col];
                        else
                        {
                            columnName = reader.GetName(col);
                            colNames.TryAdd(col, columnName);
                        }

                        var value = reader[columnName];

                        IFastDeepClonerProperty prop;
                        if (!pp.ContainsKey(col))
                        {
                            prop = DeepCloner.GetProperty(tType, columnName);
                            if (prop == null)
                                prop = props.FirstOrDefault(x => string.Equals(x.GetPropertyName(), columnName, StringComparison.CurrentCultureIgnoreCase) || x.GetPropertyName().ToLower() == columnName);
                            pp.TryAdd(col, prop);
                        }
                        else prop = pp[col];
                        if (prop != null && value != DBNull.Value && value != null && prop.CanRead)
                        {
                            if (value as byte[] != null && prop.PropertyType.FullName.Contains("Guid"))
                                value = new Guid(value as byte[]);

                            var dataEncode = prop.GetCustomAttribute();
                            if (prop.ContainAttribute())
                            {
                                if (value.ConvertValue().IsBase64String())
                                    value = MethodHelper.DecodeStringFromBase64(value.ConvertValue());
                                else value = MethodHelper.ConvertValue(value, prop.PropertyType);
                            }
                            else if (dataEncode != null)
                                value = new DataCipher(dataEncode.Key, dataEncode.KeySize).Decrypt(value.ConvertValue());
                            else if (prop.ContainAttribute())
                                value = value?.ToString().FromJson(prop.PropertyType);
                            else if (prop.ContainAttribute())
                                value = value?.ToString().FromXml();
                            else value = MethodHelper.ConvertValue(value, prop.PropertyType);

                            prop.SetValue(item, value);
                            if (attachable)
                                prop.SetValue(replacedem, value);
                        }
                        col++;
                    }

                    if (replacedem != null && !(repository?.IsAttached(replacedem) ?? true))
                        repository?.AttachNew(replacedem);
                    iList.Add(item);

                }
            }
            catch (Exception e)
            {
                throw new EnsatyException(e.Message);
            }
            finally
            {
                reader.Close();
                reader.Dispose();
                if (repository.OpenedDataReaders.ContainsKey(reader))
                    repository.OpenedDataReaders.Remove(reader);
            }

            return iList;
        }

        private static readonly Dictionary CachedSqlException = new Dictionary();
        private static readonly Dictionary CachedGetSchemaTable = new Dictionary();
        internal static ILightDataTable ReadData(this ILightDataTable data, DataBaseTypes dbType, IDataReader reader, ISqlCommand command, string primaryKey = null, bool closeReader = true)
        {
            var i = 0;
            data.TablePrimaryKey = primaryKey;
            if (reader.FieldCount