You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
6.3 KiB
187 lines
6.3 KiB
using System;
|
|
using System.Data;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Threading;
|
|
|
|
namespace Dapper
|
|
{
|
|
/// <summary>
|
|
/// Represents the key aspects of a sql operation
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The command (sql or a stored-procedure name) to execute
|
|
/// </summary>
|
|
public string CommandText { get; }
|
|
|
|
/// <summary>
|
|
/// The parameters associated with the command
|
|
/// </summary>
|
|
public object Parameters { get; }
|
|
|
|
/// <summary>
|
|
/// The active transaction for the command
|
|
/// </summary>
|
|
public IDbTransaction Transaction { get; }
|
|
|
|
/// <summary>
|
|
/// The effective timeout for the command
|
|
/// </summary>
|
|
public int? CommandTimeout { get; }
|
|
|
|
/// <summary>
|
|
/// The type of command that the command-text represents
|
|
/// </summary>
|
|
public CommandType? CommandType { get; }
|
|
|
|
/// <summary>
|
|
/// Should data be buffered before returning?
|
|
/// </summary>
|
|
public bool Buffered => (Flags & CommandFlags.Buffered) != 0;
|
|
|
|
/// <summary>
|
|
/// Should the plan for this query be cached?
|
|
/// </summary>
|
|
internal bool AddToCache => (Flags & CommandFlags.NoCache) == 0;
|
|
|
|
/// <summary>
|
|
/// Additional state flags against this command
|
|
/// </summary>
|
|
public CommandFlags Flags { get; }
|
|
|
|
/// <summary>
|
|
/// Can async queries be pipelined?
|
|
/// </summary>
|
|
public bool Pipelined => (Flags & CommandFlags.Pipelined) != 0;
|
|
|
|
/// <summary>
|
|
/// Initialize the command definition
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// For asynchronous operations, the cancellation-token
|
|
/// </summary>
|
|
public CancellationToken CancellationToken { get; }
|
|
#endif
|
|
|
|
internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> 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<Type, Action<IDbCommand>> commandInitCache;
|
|
|
|
private static Action<IDbCommand> GetInit(Type commandType)
|
|
{
|
|
if (commandType == null)
|
|
return null; // GIGO
|
|
Action<IDbCommand> action;
|
|
if (SqlMapper.Link<Type, Action<IDbCommand>>.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<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
|
|
}
|
|
// cache it
|
|
SqlMapper.Link<Type, Action<IDbCommand>>.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;
|
|
}
|
|
}
|
|
|
|
}
|
|
|