/// <summary>
        /// Delete all entities in the table related to the type T.
        /// </summary>
        /// <typeparam name="T">Type of entity</typeparam>
        /// <param name="connection">Open SqlConnection</param>
        /// <param name="transaction">The transaction to run under, null (the default) if none</param>
        /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
        /// <returns>true if deleted, false if none found</returns>
        public static bool DeleteAll <T>(this IDbConnection connection, IDbTransaction transaction = null, int?commandTimeout = null) where T : class
        {
            var connectionName = GetDatabaseType?.Invoke(connection).ToLower()
                                 ?? connection.GetType().Name.ToLower();

            var type = typeof(T);
            var name = GetTableName(type);

            if (connectionName == "hanaconnection")
            {
                var statement = $"delete from \"{name}\"";
                var deleted   = connection.Execute(statement, null, transaction, commandTimeout);
                return(deleted > 0);
            }
            else
            {
                var statement = $"delete from {name}";
                var deleted   = connection.Execute(statement, null, transaction, commandTimeout);
                return(deleted > 0);
            }
        }
        /// <summary>
        /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list.
        /// </summary>
        /// <typeparam name="T">The type to insert.</typeparam>
        /// <param name="connection">Open SqlConnection</param>
        /// <param name="entityToInsert">Entity to insert, can be list of entities</param>
        /// <param name="transaction">The transaction to run under, null (the default) if none</param>
        /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
        /// <returns>Identity of inserted entity, or number of inserted rows if inserting a list</returns>
        public static long Insert <T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int?commandTimeout = null) where T : class
        {
            var connectionName = GetDatabaseType?.Invoke(connection).ToLower()
                                 ?? connection.GetType().Name.ToLower();

            var isList = false;

            var type = typeof(T);

            if (type.IsArray)
            {
                isList = true;
                type   = type.GetElementType();
            }
            else if (type.IsGenericType)
            {
                var  typeInfo = type.GetTypeInfo();
                bool implementsGenericIEnumerableOrIsGenericIEnumerable =
                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable <>)) ||
                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable <>);

                if (implementsGenericIEnumerableOrIsGenericIEnumerable)
                {
                    isList = true;
                    type   = type.GetGenericArguments()[0];
                }
            }

            var name               = GetTableName(type);
            var sbColumnList       = new StringBuilder(null);
            var allProperties      = TypePropertiesCache(type);
            var keyProperties      = KeyPropertiesCache(type);
            var computedProperties = ComputedPropertiesCache(type);
            var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();

            var adapter = GetFormatter(connection);

            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
            {
                var property = allPropertiesExceptKeyAndComputed[i];
                adapter.AppendColumnName(sbColumnList, property.Name);  //fix for issue #336
                if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                {
                    sbColumnList.Append(", ");
                }
            }

            var sbParameterList = new StringBuilder(null);

            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
            {
                var property = allPropertiesExceptKeyAndComputed[i];
                if (connectionName == "hanaconnection")
                {
                    sbParameterList.AppendFormat(":{0}", property.Name);
                }
                else
                {
                    sbParameterList.AppendFormat("@{0}", property.Name);
                }
                if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                {
                    sbParameterList.Append(", ");
                }
            }

            int returnVal;
            var wasClosed = connection.State == ConnectionState.Closed;

            if (wasClosed)
            {
                connection.Open();
            }

            if (!isList)    //single entity
            {
                returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
                                           sbParameterList.ToString(), keyProperties, entityToInsert);
            }
            else
            {
                //insert list of entities
                if (connectionName == "hanaconnection")
                {
                    var cmd = $"insert into \"{name}\" ({sbColumnList}) values ({sbParameterList})";
                    returnVal = connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
                }
                else
                {
                    var cmd = $"insert into {name} ({sbColumnList}) values ({sbParameterList})";
                    returnVal = connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
                }
            }
            if (wasClosed)
            {
                connection.Close();
            }
            return(returnVal);
        }
        /// <summary>
        /// Delete entity in table "Ts".
        /// </summary>
        /// <typeparam name="T">Type of entity</typeparam>
        /// <param name="connection">Open SqlConnection</param>
        /// <param name="entityToDelete">Entity to delete</param>
        /// <param name="transaction">The transaction to run under, null (the default) if none</param>
        /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
        /// <returns>true if deleted, false if not found</returns>
        public static bool Delete <T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int?commandTimeout = null) where T : class
        {
            var connectionName = GetDatabaseType?.Invoke(connection).ToLower()
                                 ?? connection.GetType().Name.ToLower();

            if (entityToDelete == null)
            {
                throw new ArgumentException("Cannot Delete null Object", nameof(entityToDelete));
            }

            var type = typeof(T);

            if (type.IsArray)
            {
                type = type.GetElementType();
            }
            else if (type.IsGenericType)
            {
                var  typeInfo = type.GetTypeInfo();
                bool implementsGenericIEnumerableOrIsGenericIEnumerable =
                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable <>)) ||
                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable <>);

                if (implementsGenericIEnumerableOrIsGenericIEnumerable)
                {
                    type = type.GetGenericArguments()[0];
                }
            }

            var keyProperties         = KeyPropertiesCache(type).ToList(); //added ToList() due to issue #418, must work on a list copy
            var explicitKeyProperties = ExplicitKeyPropertiesCache(type);

            if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
            {
                throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");
            }

            var name = GetTableName(type);

            keyProperties.AddRange(explicitKeyProperties);

            var sb = new StringBuilder();

            if (connectionName == "hanaconnection")
            {
                sb.AppendFormat("delete from \"{0}\" where ", name);
            }
            else
            {
                sb.AppendFormat("delete from {0} where ", name);
            }

            var adapter = GetFormatter(connection);

            for (var i = 0; i < keyProperties.Count; i++)
            {
                var property = keyProperties[i];
                adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
                if (i < keyProperties.Count - 1)
                {
                    sb.Append(" and ");
                }
            }
            var deleted = connection.Execute(sb.ToString(), entityToDelete, transaction, commandTimeout);

            return(deleted > 0);
        }
        /// <summary>
        /// Returns a single entity by a single id from table "Ts".
        /// Id must be marked with [Key] attribute.
        /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension
        /// for optimal performance.
        /// </summary>
        /// <typeparam name="T">Interface or type to create and populate</typeparam>
        /// <param name="connection">Open SqlConnection</param>
        /// <param name="id">Id of the entity to get, must be marked with [Key] attribute</param>
        /// <param name="transaction">The transaction to run under, null (the default) if none</param>
        /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
        /// <returns>Entity of T</returns>
        public static T Get <T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int?commandTimeout = null) where T : class
        {
            var type = typeof(T);


            var connectionName = GetDatabaseType?.Invoke(connection).ToLower()
                                 ?? connection.GetType().Name.ToLower();

            if (!GetQueries.TryGetValue(type.TypeHandle, out string sql))
            {
                var key  = GetSingleKey <T>(nameof(Get));
                var name = GetTableName(type);

                if (connectionName == "hanaconnection")
                {
                    sql = $"select * from \"{name}\" where \"{key.Name}\" = :id";
                }
                else
                {
                    sql = $"select * from \"{name}\" where \"{key.Name}\" = :id";
                }

                GetQueries[type.TypeHandle] = sql;
            }

            var dynParms = new DynamicParameters();

            if (connectionName == "hanaconnection")
            {
                dynParms.Add(":id", id);
            }
            else
            {
                dynParms.Add("@id", id);
            }

            T obj;

            if (type.IsInterface)
            {
                var res = connection.Query(sql, dynParms).FirstOrDefault() as IDictionary <string, object>;

                if (res == null)
                {
                    return(null);
                }

                obj = ProxyGenerator.GetInterfaceProxy <T>();

                foreach (var property in TypePropertiesCache(type))
                {
                    var val = res[property.Name];
                    if (val == null)
                    {
                        continue;
                    }
                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                    {
                        var genericType = Nullable.GetUnderlyingType(property.PropertyType);
                        if (genericType != null)
                        {
                            property.SetValue(obj, Convert.ChangeType(val, genericType), null);
                        }
                    }
                    else
                    {
                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);
                    }
                }

                ((IProxy)obj).IsDirty = false;   //reset change tracking and return
            }
            else
            {
                obj = connection.Query <T>(sql, dynParms, transaction, commandTimeout: commandTimeout).FirstOrDefault();
            }
            return(obj);
        }