/*
License: http://www.apache.org/licenses/LICENSE-2.0
Home page: http://code.google.com/p/dapper-dot-net/
Note: to build on C# 3.0 + .NET 3.5, include the CSHARP30 compiler symbol (and yes,
I know the difference between language and runtime versions; this is a compromise).
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Globalization;
namespace Dapper
{
[AssemblyNeutral, AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
internal sealed class AssemblyNeutralAttribute : Attribute { }
///
/// Additional state flags that control command behaviour
///
[Flags]
public enum CommandFlags
{
///
/// No additonal flags
///
None = 0,
///
/// Should data be buffered before returning?
///
Buffered = 1,
///
/// Can async queries be pipelined?
///
Pipelined = 2,
}
///
/// Represents the key aspects of a sql operation
///
public struct CommandDefinition
{
private readonly string commandText;
private readonly object parameters;
private readonly IDbTransaction transaction;
private readonly int? commandTimeout;
private readonly CommandType? commandType;
private readonly CommandFlags flags;
///
/// The command (sql or a stored-procedure name) to execute
///
public string CommandText { get { return commandText; } }
///
/// The parameters associated with the command
///
public object Parameters { get { return parameters; } }
///
/// The active transaction for the command
///
public IDbTransaction Transaction { get { return transaction; } }
///
/// The effective timeout for the command
///
public int? CommandTimeout { get { return commandTimeout; } }
///
/// The type of command that the command-text represents
///
public CommandType? CommandType { get { return commandType; } }
///
/// Should data be buffered before returning?
///
public bool Buffered { get { return (flags & CommandFlags.Buffered) != 0; } }
///
/// Additional state flags against this command
///
public CommandFlags Flags { get { return flags; } }
///
/// Can async queries be pipelined?
///
public bool Pipelined { get { return (flags & CommandFlags.Pipelined) != 0; } }
///
/// Initialize the command definition
///
#if CSHARP30
public CommandDefinition(string commandText, object parameters, IDbTransaction transaction, int? commandTimeout,
CommandType? commandType, CommandFlags flags)
#else
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
)
#endif
{
this.commandText = commandText;
this.parameters = parameters;
this.transaction = transaction;
this.commandTimeout = commandTimeout;
this.commandType = commandType;
this.flags = flags;
#if ASYNC
this.cancellationToken = cancellationToken;
#endif
}
#if ASYNC
private readonly CancellationToken cancellationToken;
///
/// For asynchronous operations, the cancellation-token
///
public CancellationToken CancellationToken { get { return cancellationToken; } }
#endif
internal IDbCommand SetupCommand(IDbConnection cnn, Action paramReader)
{
var cmd = cnn.CreateCommand();
var bindByName = GetBindByName(cmd.GetType());
if (bindByName != null) bindByName(cmd, true);
if (transaction != null)
cmd.Transaction = transaction;
cmd.CommandText = commandText;
if (commandTimeout.HasValue)
cmd.CommandTimeout = commandTimeout.Value;
if (commandType.HasValue)
cmd.CommandType = commandType.Value;
if (paramReader != null)
{
paramReader(cmd, parameters);
}
return cmd;
}
static SqlMapper.Link> bindByNameCache;
static Action GetBindByName(Type commandType)
{
if (commandType == null) return null; // GIGO
Action action;
if (SqlMapper.Link>.TryGet(bindByNameCache, commandType, out action))
{
return action;
}
var prop = commandType.GetProperty("BindByName", BindingFlags.Public | BindingFlags.Instance);
action = null;
ParameterInfo[] indexers;
MethodInfo setter;
if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)
&& ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)
&& (setter = prop.GetSetMethod()) != null
)
{
var method = new DynamicMethod(commandType.Name + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, commandType);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, setter, null);
il.Emit(OpCodes.Ret);
action = (Action)method.CreateDelegate(typeof(Action));
}
// cache it
SqlMapper.Link>.TryAdd(ref bindByNameCache, commandType, ref action);
return action;
}
}
///
/// Dapper, a light weight object mapper for ADO.NET
///
static partial class SqlMapper
{
///
/// Implement this interface to pass an arbitrary db specific set of parameters to Dapper
///
public partial interface IDynamicParameters
{
///
/// Add all the parameters needed to the command just before it executes
///
/// The raw command prior to execution
/// Information about the query
void AddParameters(IDbCommand command, Identity identity);
}
///
/// Extends IDynamicParameters providing by-name lookup of parameter values
///
public interface IParameterLookup : IDynamicParameters
{
///
/// Get the value of the specified parameter (return null if not found)
///
object this[string name] { get; }
}
///
/// Implement this interface to pass an arbitrary db specific parameter to Dapper
///
[AssemblyNeutral]
public interface ICustomQueryParameter
{
///
/// Add the parameter needed to the command before it executes
///
/// The raw command prior to execution
/// Parameter name
void AddParameter(IDbCommand command, string name);
}
///
/// Implement this interface to perform custom type-based parameter handling and value parsing
///
[AssemblyNeutral]
public interface ITypeHandler
{
///
/// Assign the value of a parameter before a command executes
///
/// The parameter to configure
/// Parameter value
void SetValue(IDbDataParameter parameter, object value);
///
/// Parse a database value back to a typed value
///
/// The value from the database
/// The type to parse to
/// The typed value
object Parse(Type destinationType, object value);
}
///
/// Base-class for simple type-handlers
///
public abstract class TypeHandler : ITypeHandler
{
///
/// Assign the value of a parameter before a command executes
///
/// The parameter to configure
/// Parameter value
public abstract void SetValue(IDbDataParameter parameter, T value);
///
/// Parse a database value back to a typed value
///
/// The value from the database
/// The typed value
public abstract T Parse(object value);
void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
{
if (value is DBNull)
{
parameter.Value = value;
}
else
{
SetValue(parameter, (T)value);
}
}
object ITypeHandler.Parse(Type destinationType, object value)
{
return Parse(value);
}
}
///
/// Implement this interface to change default mapping of reader columns to type memebers
///
public interface ITypeMap
{
///
/// Finds best constructor
///
/// DataReader column names
/// DataReader column types
/// Matching constructor or default one
ConstructorInfo FindConstructor(string[] names, Type[] types);
///
/// Gets mapping for constructor parameter
///
/// Constructor to resolve
/// DataReader column name
/// Mapping implementation
IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName);
///
/// Gets member mapping for column
///
/// DataReader column name
/// Mapping implementation
IMemberMap GetMember(string columnName);
}
///
/// Implements this interface to provide custom member mapping
///
public interface IMemberMap
{
///
/// Source DataReader column name
///
string ColumnName { get; }
///
/// Target member type
///
Type MemberType { get; }
///
/// Target property
///
PropertyInfo Property { get; }
///
/// Target field
///
FieldInfo Field { get; }
///
/// Target constructor parameter
///
ParameterInfo Parameter { get; }
}
///
/// This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),
/// and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**
/// equality. The type is fully thread-safe.
///
internal partial class Link where TKey : class
{
public static bool TryGet(Link link, TKey key, out TValue value)
{
while (link != null)
{
if ((object)key == (object)link.Key)
{
value = link.Value;
return true;
}
link = link.Tail;
}
value = default(TValue);
return false;
}
public static bool TryAdd(ref Link head, TKey key, ref TValue value)
{
bool tryAgain;
do
{
var snapshot = Interlocked.CompareExchange(ref head, null, null);
TValue found;
if (TryGet(snapshot, key, out found))
{ // existing match; report the existing value instead
value = found;
return false;
}
var newNode = new Link(key, value, snapshot);
// did somebody move our cheese?
tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot;
} while (tryAgain);
return true;
}
private Link(TKey key, TValue value, Link tail)
{
Key = key;
Value = value;
Tail = tail;
}
public TKey Key { get; private set; }
public TValue Value { get; private set; }
public Link Tail { get; private set; }
}
partial class CacheInfo
{
public DeserializerState Deserializer { get; set; }
public Func[] OtherDeserializers { get; set; }
public Action ParamReader { get; set; }
private int hitCount;
public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
public void RecordHit() { Interlocked.Increment(ref hitCount); }
}
static int GetColumnHash(IDataReader reader)
{
unchecked
{
int colCount = reader.FieldCount, hash = colCount;
for (int i = 0; i < colCount; i++)
{ // binding code is only interested in names - not types
object tmp = reader.GetName(i);
hash = (hash * 31) + (tmp == null ? 0 : tmp.GetHashCode());
}
return hash;
}
}
struct DeserializerState
{
public readonly int Hash;
public readonly Func Func;
public DeserializerState(int hash, Func func)
{
Hash = hash;
Func = func;
}
}
///
/// Called if the query cache is purged via PurgeQueryCache
///
public static event EventHandler QueryCachePurged;
private static void OnQueryCachePurged()
{
var handler = QueryCachePurged;
if (handler != null) handler(null, EventArgs.Empty);
}
#if CSHARP30
private static readonly Dictionary _queryCache = new Dictionary();
// note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of
// ReaderWriterLockSlim etc; a simple lock is faster
private static void SetQueryCache(Identity key, CacheInfo value)
{
lock (_queryCache) { _queryCache[key] = value; }
}
private static bool TryGetQueryCache(Identity key, out CacheInfo value)
{
lock (_queryCache) { return _queryCache.TryGetValue(key, out value); }
}
private static void PurgeQueryCacheByType(Type type)
{
lock (_queryCache)
{
var toRemove = _queryCache.Keys.Where(id => id.type == type).ToArray();
foreach (var key in toRemove)
_queryCache.Remove(key);
}
}
///
/// Purge the query cache
///
public static void PurgeQueryCache()
{
lock (_queryCache)
{
_queryCache.Clear();
}
OnQueryCachePurged();
}
#else
static readonly System.Collections.Concurrent.ConcurrentDictionary _queryCache = new System.Collections.Concurrent.ConcurrentDictionary();
private static void SetQueryCache(Identity key, CacheInfo value)
{
if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)
{
CollectCacheGarbage();
}
_queryCache[key] = value;
}
private static void CollectCacheGarbage()
{
try
{
foreach (var pair in _queryCache)
{
if (pair.Value.GetHitCount() <= COLLECT_HIT_COUNT_MIN)
{
CacheInfo cache;
_queryCache.TryRemove(pair.Key, out cache);
}
}
}
finally
{
Interlocked.Exchange(ref collect, 0);
}
}
private const int COLLECT_PER_ITEMS = 1000, COLLECT_HIT_COUNT_MIN = 0;
private static int collect;
private static bool TryGetQueryCache(Identity key, out CacheInfo value)
{
if (_queryCache.TryGetValue(key, out value))
{
value.RecordHit();
return true;
}
value = null;
return false;
}
///
/// Purge the query cache
///
public static void PurgeQueryCache()
{
_queryCache.Clear();
OnQueryCachePurged();
}
private static void PurgeQueryCacheByType(Type type)
{
foreach (var entry in _queryCache)
{
CacheInfo cache;
if (entry.Key.type == type)
_queryCache.TryRemove(entry.Key, out cache);
}
}
///
/// Return a count of all the cached queries by dapper
///
///
public static int GetCachedSQLCount()
{
return _queryCache.Count;
}
///
/// Return a list of all the queries cached by dapper
///
///
///
public static IEnumerable> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)
{
var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount()));
if (ignoreHitCountAbove < int.MaxValue) data = data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove);
return data;
}
///
/// Deep diagnostics only: find any hash collisions in the cache
///
///
public static IEnumerable> GetHashCollissions()
{
var counts = new Dictionary();
foreach (var key in _queryCache.Keys)
{
int count;
if (!counts.TryGetValue(key.hashCode, out count))
{
counts.Add(key.hashCode, 1);
}
else
{
counts[key.hashCode] = count + 1;
}
}
return from pair in counts
where pair.Value > 1
select Tuple.Create(pair.Key, pair.Value);
}
#endif
static readonly Dictionary typeMap;
static SqlMapper()
{
typeMap = new Dictionary();
typeMap[typeof(byte)] = DbType.Byte;
typeMap[typeof(sbyte)] = DbType.SByte;
typeMap[typeof(short)] = DbType.Int16;
typeMap[typeof(ushort)] = DbType.UInt16;
typeMap[typeof(int)] = DbType.Int32;
typeMap[typeof(uint)] = DbType.UInt32;
typeMap[typeof(long)] = DbType.Int64;
typeMap[typeof(ulong)] = DbType.UInt64;
typeMap[typeof(float)] = DbType.Single;
typeMap[typeof(double)] = DbType.Double;
typeMap[typeof(decimal)] = DbType.Decimal;
typeMap[typeof(bool)] = DbType.Boolean;
typeMap[typeof(string)] = DbType.String;
typeMap[typeof(char)] = DbType.StringFixedLength;
typeMap[typeof(Guid)] = DbType.Guid;
typeMap[typeof(DateTime)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan)] = DbType.Time;
typeMap[typeof(byte[])] = DbType.Binary;
typeMap[typeof(byte?)] = DbType.Byte;
typeMap[typeof(sbyte?)] = DbType.SByte;
typeMap[typeof(short?)] = DbType.Int16;
typeMap[typeof(ushort?)] = DbType.UInt16;
typeMap[typeof(int?)] = DbType.Int32;
typeMap[typeof(uint?)] = DbType.UInt32;
typeMap[typeof(long?)] = DbType.Int64;
typeMap[typeof(ulong?)] = DbType.UInt64;
typeMap[typeof(float?)] = DbType.Single;
typeMap[typeof(double?)] = DbType.Double;
typeMap[typeof(decimal?)] = DbType.Decimal;
typeMap[typeof(bool?)] = DbType.Boolean;
typeMap[typeof(char?)] = DbType.StringFixedLength;
typeMap[typeof(Guid?)] = DbType.Guid;
typeMap[typeof(DateTime?)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan?)] = DbType.Time;
typeMap[typeof(object)] = DbType.Object;
AddTypeHandler(typeof(DataTable), new DataTableHandler());
}
///
/// Configire the specified type to be mapped to a given db-type
///
public static void AddTypeMap(Type type, DbType dbType)
{
typeMap[type] = dbType;
}
///
/// Configire the specified type to be processed by a custom handler
///
public static void AddTypeHandler(Type type, ITypeHandler handler)
{
if (type == null) throw new ArgumentNullException("type");
#pragma warning disable 618
typeof(TypeHandlerCache<>).MakeGenericType(type).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
#pragma warning restore 618
if (handler == null) typeHandlers.Remove(type);
else typeHandlers[type] = handler;
}
///
/// Configire the specified type to be processed by a custom handler
///
public static void AddTypeHandler(TypeHandler handler)
{
AddTypeHandler(typeof(T), handler);
}
///
/// Not intended for direct usage
///
[Obsolete("Not intended for direct usage", false)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static class TypeHandlerCache
{
///
/// Not intended for direct usage
///
[Obsolete("Not intended for direct usage", true)]
public static T Parse(object value)
{
return (T)handler.Parse(typeof(T), value);
}
///
/// Not intended for direct usage
///
[Obsolete("Not intended for direct usage", true)]
public static void SetValue(IDbDataParameter parameter, object value)
{
handler.SetValue(parameter, value);
}
internal static void SetHandler(ITypeHandler handler)
{
#pragma warning disable 618
TypeHandlerCache.handler = handler;
#pragma warning restore 618
}
private static ITypeHandler handler;
}
private static readonly Dictionary typeHandlers = new Dictionary();
internal const string LinqBinary = "System.Data.Linq.Binary";
internal static DbType LookupDbType(Type type, string name, out ITypeHandler handler)
{
DbType dbType;
handler = null;
var nullUnderlyingType = Nullable.GetUnderlyingType(type);
if (nullUnderlyingType != null) type = nullUnderlyingType;
if (type.IsEnum && !typeMap.ContainsKey(type))
{
type = Enum.GetUnderlyingType(type);
}
if (typeMap.TryGetValue(type, out dbType))
{
return dbType;
}
if (type.FullName == LinqBinary)
{
return DbType.Binary;
}
if (typeof(IEnumerable).IsAssignableFrom(type))
{
return DynamicParameters.EnumerableMultiParameter;
}
if (typeHandlers.TryGetValue(type, out handler))
{
return DbType.Object;
}
throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));
}
///
/// Identity of a cached query in Dapper, used for extensability
///
public partial class Identity : IEquatable
{
internal Identity ForGrid(Type primaryType, int gridIndex)
{
return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);
}
internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
{
return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
}
///
/// Create an identity for use with DynamicParameters, internal use only
///
///
///
public Identity ForDynamicParameters(Type type)
{
return new Identity(sql, commandType, connectionString, this.type, type, null, -1);
}
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
: this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)
{ }
private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex)
{
this.sql = sql;
this.commandType = commandType;
this.connectionString = connectionString;
this.type = type;
this.parametersType = parametersType;
this.gridIndex = gridIndex;
unchecked
{
hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this
hashCode = hashCode * 23 + commandType.GetHashCode();
hashCode = hashCode * 23 + gridIndex.GetHashCode();
hashCode = hashCode * 23 + (sql == null ? 0 : sql.GetHashCode());
hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode());
if (otherTypes != null)
{
foreach (var t in otherTypes)
{
hashCode = hashCode * 23 + (t == null ? 0 : t.GetHashCode());
}
}
hashCode = hashCode * 23 + (connectionString == null ? 0 : SqlMapper.connectionStringComparer.GetHashCode(connectionString));
hashCode = hashCode * 23 + (parametersType == null ? 0 : parametersType.GetHashCode());
}
}
///
///
///
///
///
public override bool Equals(object obj)
{
return Equals(obj as Identity);
}
///
/// The sql
///
public readonly string sql;
///
/// The command type
///
public readonly CommandType? commandType;
///
///
///
public readonly int hashCode, gridIndex;
///
///
///
public readonly Type type;
///
///
///
public readonly string connectionString;
///
///
///
public readonly Type parametersType;
///
///
///
///
public override int GetHashCode()
{
return hashCode;
}
///
/// Compare 2 Identity objects
///
///
///
public bool Equals(Identity other)
{
return
other != null &&
gridIndex == other.gridIndex &&
type == other.type &&
sql == other.sql &&
commandType == other.commandType &&
SqlMapper.connectionStringComparer.Equals(connectionString, other.connectionString) &&
parametersType == other.parametersType;
}
}
#if CSHARP30
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(this IDbConnection cnn, string sql, object param)
{
return Execute(cnn, sql, param, null, null, null);
}
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
{
return Execute(cnn, sql, param, transaction, null, null);
}
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(this IDbConnection cnn, string sql, object param, CommandType commandType)
{
return Execute(cnn, sql, param, null, null, commandType);
}
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
{
return Execute(cnn, sql, param, transaction, null, commandType);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param)
{
return ExecuteReader(cnn, sql, param, null, null, null);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
{
return ExecuteReader(cnn, sql, param, transaction, null, null);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, CommandType commandType)
{
return ExecuteReader(cnn, sql, param, null, null, commandType);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
{
return ExecuteReader(cnn, sql, param, transaction, null, commandType);
}
///
/// Executes a query, returning the data typed as per T
///
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable Query(this IDbConnection cnn, string sql, object param)
{
return Query(cnn, sql, param, null, true, null, null);
}
///
/// Executes a query, returning the data typed as per T
///
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
{
return Query(cnn, sql, param, transaction, true, null, null);
}
///
/// Executes a query, returning the data typed as per T
///
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable Query(this IDbConnection cnn, string sql, object param, CommandType commandType)
{
return Query(cnn, sql, param, null, true, null, commandType);
}
///
/// Executes a query, returning the data typed as per T
///
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
{
return Query(cnn, sql, param, transaction, true, null, commandType);
}
///
/// Execute a command that returns multiple result sets, and access each in turn
///
public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
{
return QueryMultiple(cnn, sql, param, transaction, null, null);
}
///
/// Execute a command that returns multiple result sets, and access each in turn
///
public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, CommandType commandType)
{
return QueryMultiple(cnn, sql, param, null, null, commandType);
}
///
/// Execute a command that returns multiple result sets, and access each in turn
///
public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
{
return QueryMultiple(cnn, sql, param, transaction, null, commandType);
}
#endif
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteImpl(cnn, ref command);
}
///
/// Execute parameterized SQL
///
/// Number of rows affected
public static int Execute(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteImpl(cnn, ref command);
}
private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition command)
{
object param = command.Parameters;
IEnumerable multiExec = param as IEnumerable;
Identity identity;
CacheInfo info = null;
if (multiExec != null && !(multiExec is string))
{
#if ASYNC
if((command.Flags & CommandFlags.Pipelined) != 0)
{
// this includes all the code for concurrent/overlapped query
return ExecuteMultiImplAsync(cnn, command, multiExec).Result;
}
#endif
bool isFirst = true;
int total = 0;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
if (wasClosed) cnn.Open();
using (var cmd = command.SetupCommand(cnn, null))
{
string masterSql = null;
foreach (var obj in multiExec)
{
if (isFirst)
{
masterSql = cmd.CommandText;
isFirst = false;
identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
info = GetCacheInfo(identity, obj);
}
else
{
cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
cmd.Parameters.Clear(); // current code is Add-tastic
}
info.ParamReader(cmd, obj);
total += cmd.ExecuteNonQuery();
}
}
} finally
{
if (wasClosed) cnn.Close();
}
return total;
}
// nice and simple
if (param != null)
{
identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
info = GetCacheInfo(identity, param);
}
return ExecuteCommand(cnn, ref command, param == null ? null : info.ParamReader);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
///
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a
/// or .
///
///
///
///
///
///
public static IDataReader ExecuteReader(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteReaderImpl(cnn, ref command);
}
///
/// Execute parameterized SQL and return an
///
/// An that can be used to iterate over the results of the SQL query.
///
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a
/// or .
///
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteReaderImpl(cnn, ref command);
}
#if !CSHARP30
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
return Query(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
}
#else
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable> Query(this IDbConnection cnn, string sql, object param)
{
return Query(cnn, sql, param, null, true, null, null);
}
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
{
return Query(cnn, sql, param, transaction, true, null, null);
}
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable> Query(this IDbConnection cnn, string sql, object param, CommandType? commandType)
{
return Query(cnn, sql, param, null, true, null, commandType);
}
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType? commandType)
{
return Query(cnn, sql, param, transaction, true, null, commandType);
}
///
/// Return a list of dynamic objects, reader is closed after the call
///
public static IEnumerable> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType)
{
return Query>(cnn, sql, param, transaction, buffered, commandTimeout, commandType);
}
#endif
///
/// Executes a query, returning the data typed as per T
///
/// the dynamic param may seem a bit odd, but this works around a major usability issue in vs, if it is Object vs completion gets annoying. Eg type new [space] get new object
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable Query(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var data = QueryImpl(cnn, command, typeof(T));
return command.Buffered ? data.ToList() : data;
}
///
/// Executes a query, returning the data typed as per the Type suggested
///
/// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
///
public static IEnumerable