private static async Task <T> ExecuteScalarImplAsync <T>(IDbConnection cnn, CommandDefinition command)
        {
            Action <IDbCommand, DynamicParameters> paramReader = null;
            object param = command.Parameters;

            if (param != null)
            {
                var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType());
                paramReader = GetCacheInfo(identity).ParamReader;
            }

            DbCommand cmd       = null;
            bool      needClose = cnn.State == ConnectionState.Closed;
            object    result;

            try
            {
                cmd = command.TrySetupAsyncCommand(cnn, paramReader);
                if (needClose)
                {
                    await cnn.TryOpenAsync(default(CancellationToken)).ConfigureAwait(false);
                }
                result = await cmd.ExecuteScalarAsync(default(CancellationToken)).ConfigureAwait(false);
            }
            finally
            {
                if (needClose)
                {
                    cnn.Close();
                }
                cmd?.Dispose();
            }
            return(Parse <T>(result));
        }
        /*
         * ExecuteImpl
         */
        private static async Task <int> ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, DynamicParameters param)
        {
            var  identity  = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType());
            var  info      = GetCacheInfo(identity);
            bool needClose = cnn.State == ConnectionState.Closed;

            using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
            {
                try
                {
                    if (needClose)
                    {
                        await cnn.TryOpenAsync(default(CancellationToken)).ConfigureAwait(false);
                    }
                    var result = await cmd.ExecuteNonQueryAsync(default(CancellationToken)).ConfigureAwait(false);

                    return(result);
                }
                finally
                {
                    if (needClose)
                    {
                        cnn.Close();
                    }
                }
            }
        }
        private static async Task <IEnumerable <T> > QueryAsync <T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
        {
            DynamicParameters param = command.Parameters;
            var  identity           = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
            var  info      = GetCacheInfo(identity);
            bool needClose = cnn.State == ConnectionState.Closed;

            using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
            {
                DbDataReader reader = null;
                try
                {
                    if (needClose)
                    {
                        await cnn.TryOpenAsync(default(CancellationToken)).ConfigureAwait(false);
                    }
                    reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, needClose, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, default(CancellationToken)).ConfigureAwait(false);

                    var tuple = info.Deserializer;
                    int hash  = GetColumnHash(reader);
                    if (tuple.Func == null || tuple.Hash != hash)
                    {
                        if (reader.FieldCount == 0)
                        {
                            return(Enumerable.Empty <T>());
                        }
                        info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
                        tuple             = info.Deserializer;
                    }

                    var func = tuple.Func;

                    if (command.Buffered)
                    {
                        var buffer        = new List <T>();
                        var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                        while (await reader.ReadAsync(default(CancellationToken)).ConfigureAwait(false))
                        {
                            object val = func(reader);
                            if (val == null || val is T)
                            {
                                buffer.Add((T)val);
                            }
                            else
                            {
                                buffer.Add((T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture));
                            }
                        }
                        while (await reader.NextResultAsync(default(CancellationToken)).ConfigureAwait(false))
                        { /* ignore subsequent result sets */
                        }
                        return(buffer);
                    }
                    else
                    {
                        // can't use ReadAsync / cancellation; but this will have to do
                        needClose = false; // don't close if handing back an open reader; rely on the command-behavior
                        var deferred = ExecuteReaderSync <T>(reader, func, command.Parameters);
                        reader = null;     // to prevent it being disposed before the caller gets to see it
                        return(deferred);
                    }
                }
                finally
                {
                    using (reader) { /* dispose if non-null */ }
                    if (needClose)
                    {
                        cnn.Close();
                    }
                }
            }
        }
        /******************************************************************************************/



        /// <summary>
        /// 查询单行
        /// </summary>
        private static async Task <T> QueryRowAsync <T>(this IDbConnection cnn, RowEnum row, Type effectiveType, CommandDefinition command)
        {
            DynamicParameters param = command.Parameters;
            var  identity           = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
            var  info      = GetCacheInfo(identity);
            bool needClose = cnn.State == ConnectionState.Closed;

            using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
            {
                DbDataReader reader = null;
                try
                {
                    if (needClose)
                    {
                        await cnn.TryOpenAsync(default(CancellationToken)).ConfigureAwait(false);
                    }
                    reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, needClose, (row & RowEnum.Single) != 0
                                                                       ?CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
                                                                       : CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow, default(CancellationToken)).ConfigureAwait(false);

                    T result = default(T);
                    if (await reader.ReadAsync(default(CancellationToken)).ConfigureAwait(false) && reader.FieldCount != 0)
                    {
                        var tuple = info.Deserializer;
                        int hash  = GetColumnHash(reader);
                        if (tuple.Func == null || tuple.Hash != hash)
                        {
                            tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
                        }

                        var func = tuple.Func;

                        object val = func(reader);
                        if (val == null || val is T)
                        {
                            result = (T)val;
                        }
                        else
                        {
                            var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                            result = (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
                        }
                        if ((row & RowEnum.Single) != 0 && await reader.ReadAsync(default(CancellationToken)).ConfigureAwait(false))
                        {
                            ThrowMultipleRows(row);
                        }
                        while (await reader.ReadAsync(default(CancellationToken)).ConfigureAwait(false))
                        { /* ignore rows after the first */
                        }
                    }
                    else if ((row & RowEnum.FirstOrDefault) == 0) // demanding a row, and don't have one
                    {
                        ThrowZeroRows(row);
                    }
                    while (await reader.NextResultAsync(default(CancellationToken)).ConfigureAwait(false))
                    { /* ignore result sets after the first */
                    }
                    return(result);
                }
                finally
                {
                    using (reader)
                    { /* dispose if non-null */ }
                    if (needClose)
                    {
                        cnn.Close();
                    }
                }
            }
        }