using System; using System.Data; using System.Reflection; using System.Reflection.Emit; using System.Threading; namespace Dapper { /// /// Represents the key aspects of a sql operation /// public struct CommandDefinition { internal static CommandDefinition ForCallback(object parameters) { if (parameters is DynamicParameters) { return new CommandDefinition(parameters); } else { return default(CommandDefinition); } } internal void OnCompleted() { (Parameters as SqlMapper.IParameterCallbacks)?.OnCompleted(); } /// /// The command (sql or a stored-procedure name) to execute /// public string CommandText { get; } /// /// The parameters associated with the command /// public object Parameters { get; } /// /// The active transaction for the command /// public IDbTransaction Transaction { get; } /// /// The effective timeout for the command /// public int? CommandTimeout { get; } /// /// The type of command that the command-text represents /// public CommandType? CommandType { get; } /// /// Should data be buffered before returning? /// public bool Buffered => (Flags & CommandFlags.Buffered) != 0; /// /// Should the plan for this query be cached? /// internal bool AddToCache => (Flags & CommandFlags.NoCache) == 0; /// /// Additional state flags against this command /// public CommandFlags Flags { get; } /// /// Can async queries be pipelined? /// public bool Pipelined => (Flags & CommandFlags.Pipelined) != 0; /// /// Initialize the command definition /// public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered #if ASYNC , CancellationToken cancellationToken = default(CancellationToken) #endif ) { CommandText = commandText; Parameters = parameters; Transaction = transaction; CommandTimeout = commandTimeout; CommandType = commandType; Flags = flags; #if ASYNC CancellationToken = cancellationToken; #endif } private CommandDefinition(object parameters) : this() { Parameters = parameters; } #if ASYNC /// /// For asynchronous operations, the cancellation-token /// public CancellationToken CancellationToken { get; } #endif internal IDbCommand SetupCommand(IDbConnection cnn, Action paramReader) { var cmd = cnn.CreateCommand(); var init = GetInit(cmd.GetType()); init?.Invoke(cmd); if (Transaction != null) cmd.Transaction = Transaction; cmd.CommandText = CommandText; if (CommandTimeout.HasValue) { cmd.CommandTimeout = CommandTimeout.Value; } else if (SqlMapper.Settings.CommandTimeout.HasValue) { cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value; } if (CommandType.HasValue) cmd.CommandType = CommandType.Value; paramReader?.Invoke(cmd, Parameters); return cmd; } private static SqlMapper.Link> commandInitCache; private static Action GetInit(Type commandType) { if (commandType == null) return null; // GIGO Action action; if (SqlMapper.Link>.TryGet(commandInitCache, commandType, out action)) { return action; } var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool)); var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int)); action = null; if (bindByName != null || initialLongFetchSize != null) { var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) }); var il = method.GetILGenerator(); if (bindByName != null) { // .BindByName = true il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, commandType); il.Emit(OpCodes.Ldc_I4_1); il.EmitCall(OpCodes.Callvirt, bindByName, null); } if (initialLongFetchSize != null) { // .InitialLONGFetchSize = -1 il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, commandType); il.Emit(OpCodes.Ldc_I4_M1); il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null); } il.Emit(OpCodes.Ret); action = (Action)method.CreateDelegate(typeof(Action)); } // cache it SqlMapper.Link>.TryAdd(ref commandInitCache, commandType, ref action); return action; } private static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType) { var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); if (prop != null && prop.CanWrite && prop.PropertyType == expectedType && prop.GetIndexParameters().Length == 0) { return prop.GetSetMethod(); } return null; } } }