diff --git a/src/NServiceKit.OrmLite.sln b/src/NServiceKit.OrmLite.sln
index f132a5e..0e3a6da 100644
--- a/src/NServiceKit.OrmLite.sln
+++ b/src/NServiceKit.OrmLite.sln
@@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{6CEB3EDE
..\build\build.proj = ..\build\build.proj
..\build\build.tasks = ..\build\build.tasks
..\build\copy.bat = ..\build\copy.bat
- ..\NuGet\NuGetPack.cmd = ..\NuGet\NuGetPack.cmd
- ..\README.md = ..\README.md
..\NuGet\NServiceKit.OrmLite.Firebird\NServiceKit.ormlite.firebird.nuspec = ..\NuGet\NServiceKit.OrmLite.Firebird\NServiceKit.ormlite.firebird.nuspec
..\NuGet\NServiceKit.OrmLite.MySql\NServiceKit.ormlite.mysql.nuspec = ..\NuGet\NServiceKit.OrmLite.MySql\NServiceKit.ormlite.mysql.nuspec
..\NuGet\NServiceKit.OrmLite.Oracle\NServiceKit.ormlite.oracle.nuspec = ..\NuGet\NServiceKit.OrmLite.Oracle\NServiceKit.ormlite.oracle.nuspec
@@ -18,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{6CEB3EDE
..\NuGet\NServiceKit.OrmLite.Sqlite64\NServiceKit.ormlite.sqlite64.nuspec = ..\NuGet\NServiceKit.OrmLite.Sqlite64\NServiceKit.ormlite.sqlite64.nuspec
..\NuGet\NServiceKit.OrmLite.SqlServer\NServiceKit.ormlite.sqlserver.nuspec = ..\NuGet\NServiceKit.OrmLite.SqlServer\NServiceKit.ormlite.sqlserver.nuspec
..\NuGet\NServiceKit.OrmLite.T4\NServiceKit.ormlite.t4.nuspec = ..\NuGet\NServiceKit.OrmLite.T4\NServiceKit.ormlite.t4.nuspec
+ ..\NuGet\NuGetPack.cmd = ..\NuGet\NuGetPack.cmd
+ ..\README.md = ..\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FirebirdTests", "FirebirdTests", "{AA7D8994-D1CA-484E-B3A4-EF6044CBE9BB}"
@@ -56,8 +56,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceKit.OrmLite.Sqlite3
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceKit.OrmLite.Sqlite32Tests", "..\tests\NServiceKit.OrmLite.Sqlite32Tests\NServiceKit.OrmLite.Sqlite32Tests.csproj", "{956FD518-A6CC-46B1-A93A-1C92779BF942}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlMapper", "dapper-dot-net\SqlMapper.csproj", "{A2A80512-11F4-4028-A995-505463632C84}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceKit.OrmLite.SqlServerTests", "NServiceKit.OrmLite.SqlServerTests\NServiceKit.OrmLite.SqlServerTests.csproj", "{96793C11-2A99-4217-8946-3E0DB9534A4D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceKit.OrmLite.Firebird", "NServiceKit.OrmLite.Firebird\NServiceKit.OrmLite.Firebird.csproj", "{317B64BA-D7A6-4A15-8719-821B38147C63}"
@@ -115,6 +113,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "T4", "T4", "{ACB237F8-B931-
T4\OrmLite.SP.tt = T4\OrmLite.SP.tt
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper NET35", "dapper-dot-net\Dapper NET35\Dapper NET35.csproj", "{B26305D8-3A89-4D68-A981-9BBF378B81FA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -556,29 +556,6 @@ Global
{956FD518-A6CC-46B1-A93A-1C92779BF942}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.ActiveCfg = Release|Any CPU
{956FD518-A6CC-46B1-A93A-1C92779BF942}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.Build.0 = Release|Any CPU
{956FD518-A6CC-46B1-A93A-1C92779BF942}.STATIC_ONLY NO_EXPRESSIONS|x86.ActiveCfg = Release|Any CPU
- {A2A80512-11F4-4028-A995-505463632C84}.Debug|Any CPU.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Debug|Mixed Platforms.Build.0 = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Debug|x86.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Debug|x86.Build.0 = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Default|Any CPU.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Default|Mixed Platforms.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Default|x86.ActiveCfg = Debug|x86
- {A2A80512-11F4-4028-A995-505463632C84}.MonoTouch|Any CPU.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.MonoTouch|Mixed Platforms.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.MonoTouch|Mixed Platforms.Build.0 = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.MonoTouch|x86.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.MonoTouch|x86.Build.0 = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Release|Any CPU.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Release|Mixed Platforms.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Release|Mixed Platforms.Build.0 = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Release|x86.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.Release|x86.Build.0 = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.STATIC_ONLY NO_EXPRESSIONS|Any CPU.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.Build.0 = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.STATIC_ONLY NO_EXPRESSIONS|x86.ActiveCfg = Release|x86
- {A2A80512-11F4-4028-A995-505463632C84}.STATIC_ONLY NO_EXPRESSIONS|x86.Build.0 = Release|x86
{96793C11-2A99-4217-8946-3E0DB9534A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96793C11-2A99-4217-8946-3E0DB9534A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96793C11-2A99-4217-8946-3E0DB9534A4D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -1163,6 +1140,31 @@ Global
{8CA77485-7F0F-4C59-8C25-312D43EF1F8C}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.ActiveCfg = STATIC_ONLY NO_EXPRESSIONS|Any CPU
{8CA77485-7F0F-4C59-8C25-312D43EF1F8C}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.Build.0 = STATIC_ONLY NO_EXPRESSIONS|Any CPU
{8CA77485-7F0F-4C59-8C25-312D43EF1F8C}.STATIC_ONLY NO_EXPRESSIONS|x86.ActiveCfg = STATIC_ONLY NO_EXPRESSIONS|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Default|Any CPU.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Default|Any CPU.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Default|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Default|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Default|x86.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.MonoTouch|Any CPU.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.MonoTouch|Any CPU.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.MonoTouch|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.MonoTouch|Mixed Platforms.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.MonoTouch|x86.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|x86.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.STATIC_ONLY NO_EXPRESSIONS|Any CPU.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.STATIC_ONLY NO_EXPRESSIONS|Any CPU.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.STATIC_ONLY NO_EXPRESSIONS|Mixed Platforms.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.STATIC_ONLY NO_EXPRESSIONS|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/NServiceKit.OrmLite/SqlBuilder.cs b/src/NServiceKit.OrmLite/SqlBuilder.cs
index 82a3f51..a43fd80 100644
--- a/src/NServiceKit.OrmLite/SqlBuilder.cs
+++ b/src/NServiceKit.OrmLite/SqlBuilder.cs
@@ -3,7 +3,6 @@
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
-using System.Text.RegularExpressions;
using System.Threading;
using NServiceKit.Text;
@@ -14,33 +13,19 @@ namespace NServiceKit.OrmLite
/// Nice SqlBuilder class by @samsaffron from Dapper.Contrib:
/// http://samsaffron.com/archive/2011/09/05/Digging+ourselves+out+of+the+mess+Linq-2-SQL+created
/// Modified to work in .NET 3.5.
+ /// Modifications taken from former Ormlite.SqlBuilder
///
- public class SqlBuilder
- {
- /// The data.
- readonly Dictionary data = new Dictionary();
-
- /// The sequence.
- int seq;
-
- /// A clause.
- class Clause
- {
- /// Gets or sets the SQL.
- /// The SQL.
- public string Sql { get; set; }
-
- /// Gets or sets options for controlling the operation.
- /// The parameters.
- public object Parameters { get; set; }
- }
+ public class SqlBuilder
+ {
+ readonly Dictionary data = new Dictionary();
+ int seq;
/// A dynamic parameters.
- class DynamicParameters
- {
+ class DynamicParameters
+ {
/// A property.
- class Property
- {
+ class Property
+ {
///
/// Initializes a new instance of the NServiceKit.OrmLite.SqlBuilder.DynamicParameters.Property
/// class.
@@ -48,332 +33,318 @@ class Property
/// The name.
/// The type.
/// The value.
- public Property(string name, Type type, object value)
- {
- Name = name;
- Type = type;
- Value = value;
- }
+ public Property(string name, Type type, object value)
+ {
+ Name = name;
+ Type = type;
+ Value = value;
+ }
/// The name.
- public readonly string Name;
+ public readonly string Name;
/// The type.
- public readonly Type Type;
+ public readonly Type Type;
/// The value.
- public readonly object Value;
- }
+ public readonly object Value;
+ }
/// The properties.
- private readonly List properties = new List();
+ private readonly List properties = new List();
///
/// Initializes a new instance of the NServiceKit.OrmLite.SqlBuilder.DynamicParameters class.
///
/// Options for controlling the initialise.
- public DynamicParameters(object initParams)
- {
- AddDynamicParams(initParams);
- }
+ public DynamicParameters(object initParams)
+ {
+ AddDynamicParams(initParams);
+ }
/// Adds a dynamic parameters.
/// Options for controlling the command.
- public void AddDynamicParams(object cmdParams)
- {
- if (cmdParams == null) return;
- foreach (var pi in cmdParams.GetType().GetPublicProperties())
- {
- var getterFn = pi.GetPropertyGetterFn();
- if (getterFn == null) continue;
- var value = getterFn(cmdParams);
- properties.Add(new Property(pi.Name, pi.PropertyType, value));
- }
- }
+ public void AddDynamicParams(object cmdParams)
+ {
+ if (cmdParams == null) return;
+ foreach (var pi in cmdParams.GetType().GetPublicProperties())
+ {
+ var getterFn = pi.GetPropertyGetterFn();
+ if (getterFn == null) continue;
+ var value = getterFn(cmdParams);
+ properties.Add(new Property(pi.Name, pi.PropertyType, value));
+ }
+ }
/// The property set and get methods require a special attrs:
- private const MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
+ private const MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
/// Creates dynamic type.
/// The new dynamic type.
- public object CreateDynamicType()
- {
- var assemblyName = new AssemblyName { Name = "tmpAssembly" };
- var typeBuilder =
+ public object CreateDynamicType()
+ {
+ var assemblyName = new AssemblyName { Name = "tmpAssembly" };
+ var typeBuilder =
Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
- .DefineDynamicModule("tmpModule")
- .DefineType("SqlBuilderDynamicParameters", TypeAttributes.Public | TypeAttributes.Class);
-
- var emptyCtor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
- var ctorIL = emptyCtor.GetILGenerator();
-
- var unsetValues = new List();
-
- // Loop over the attributes that will be used as the properties names in out new type
- foreach (var p in properties)
- {
- // Generate a private field
- var field = typeBuilder.DefineField("_" + p.Name, p.Type, FieldAttributes.Private);
-
- //set default values with Emit for popular types
- if (p.Type == typeof(int))
- {
- ctorIL.Emit(OpCodes.Ldarg_0);
- ctorIL.Emit(OpCodes.Ldc_I4, (int)p.Value);
- ctorIL.Emit(OpCodes.Stfld, field);
- }
- else if (p.Type == typeof(long))
- {
- ctorIL.Emit(OpCodes.Ldarg_0);
- ctorIL.Emit(OpCodes.Ldc_I8, (long)p.Value);
- ctorIL.Emit(OpCodes.Stfld, field);
- }
- else if (p.Type == typeof(string))
- {
- ctorIL.Emit(OpCodes.Ldarg_0);
- ctorIL.Emit(OpCodes.Ldstr, (string)p.Value);
- ctorIL.Emit(OpCodes.Stfld, field);
- }
- else
- {
- unsetValues.Add(p); //otherwise use reflection
- }
-
- // Generate a public property
- var property = typeBuilder.DefineProperty(p.Name, PropertyAttributes.None, p.Type, new[] { p.Type });
-
- // Define the "get" accessor method for current private field.
- var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + p.Name, GetSetAttr, p.Type, Type.EmptyTypes);
-
- // Get Property impl
- var currGetIL = currGetPropMthdBldr.GetILGenerator();
- currGetIL.Emit(OpCodes.Ldarg_0);
- currGetIL.Emit(OpCodes.Ldfld, field);
- currGetIL.Emit(OpCodes.Ret);
-
- // Define the "set" accessor method for current private field.
- var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + p.Name, GetSetAttr, null, new[] { p.Type });
-
- // Set Property impl
- var currSetIL = currSetPropMthdBldr.GetILGenerator();
- currSetIL.Emit(OpCodes.Ldarg_0);
- currSetIL.Emit(OpCodes.Ldarg_1);
- currSetIL.Emit(OpCodes.Stfld, field);
- currSetIL.Emit(OpCodes.Ret);
-
- // Hook up, getters and setters.
- property.SetGetMethod(currGetPropMthdBldr);
- property.SetSetMethod(currSetPropMthdBldr);
- }
-
- ctorIL.Emit(OpCodes.Ret);
-
- var generetedType = typeBuilder.CreateType();
- var instance = Activator.CreateInstance(generetedType);
-
- //Using reflection for less property types. Not caching since it's a generated type.
- foreach (var p in unsetValues)
- {
- generetedType.GetProperty(p.Name).GetSetMethod().Invoke(instance, new[] { p.Value });
- }
-
- return instance;
- }
- }
-
- /// A clauses.
- class Clauses : List
- {
- /// The joiner.
- readonly string joiner;
-
- /// The prefix.
- readonly string prefix;
-
- /// The postfix.
- readonly string postfix;
-
- ///
- /// Initializes a new instance of the NServiceKit.OrmLite.SqlBuilder.Clauses class.
- ///
- /// The joiner.
- /// The prefix.
- /// The postfix.
- public Clauses(string joiner, string prefix = "", string postfix = "")
- {
- this.joiner = joiner;
- this.prefix = prefix;
- this.postfix = postfix;
- }
-
- /// Resolve clauses.
- /// The DynamicParameters to process.
- /// A string.
- public string ResolveClauses(DynamicParameters p)
- {
- foreach (var item in this)
- {
- p.AddDynamicParams(item.Parameters);
- }
- return prefix + string.Join(joiner, this.Select(c => c.Sql).ToArray()) + postfix;
- }
- }
-
- /// A template.
- public class Template
- {
- /// The SQL.
- readonly string sql;
-
- /// The builder.
- readonly SqlBuilder builder;
-
- /// Options for controlling the initialise.
- readonly object initParams;
-
- /// Unresolved.
- int dataSeq = -1;
-
- ///
- /// Initializes a new instance of the NServiceKit.OrmLite.SqlBuilder.Template class.
- ///
- /// The builder.
- /// The SQL.
- /// Options for controlling the operation.
- public Template(SqlBuilder builder, string sql, object parameters)
- {
- this.initParams = parameters;
- this.sql = sql;
- this.builder = builder;
- }
-
- /// The regular expression.
- static readonly Regex regex = new Regex(@"\/\*\*.+\*\*\/", RegexOptions.Compiled | RegexOptions.Multiline);
-
- /// Resolve SQL.
- void ResolveSql()
- {
- if (dataSeq != builder.seq)
- {
- var p = new DynamicParameters(initParams);
-
- rawSql = sql;
-
- foreach (var pair in builder.data)
- {
- rawSql = rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses(p));
- }
- parameters = p.CreateDynamicType();
-
- // replace all that is left with empty
- rawSql = regex.Replace(rawSql, "");
-
- dataSeq = builder.seq;
- }
- }
-
- /// The raw SQL.
- string rawSql;
-
- /// Options for controlling the operation.
- object parameters;
-
- /// Gets the raw SQL.
- /// The raw SQL.
- public string RawSql { get { ResolveSql(); return rawSql; } }
-
- /// Gets options for controlling the operation.
- /// The parameters.
- public object Parameters { get { ResolveSql(); return parameters; } }
- }
-
- /// Adds a template to 'parameters'.
- /// The SQL.
- /// Options for controlling the operation.
- /// A Template.
- public Template AddTemplate(string sql, object parameters = null)
- {
- return new Template(this, sql, parameters);
- }
-
- /// Adds a clause.
- /// The name.
- /// The SQL.
- /// Options for controlling the operation.
- /// The joiner.
- /// The prefix.
- /// The postfix.
- void AddClause(string name, string sql, object parameters, string joiner, string prefix = "", string postfix = "")
- {
- Clauses clauses;
- if (!data.TryGetValue(name, out clauses))
- {
- clauses = new Clauses(joiner, prefix, postfix);
- data[name] = clauses;
- }
- clauses.Add(new Clause { Sql = sql, Parameters = parameters });
- seq++;
- }
-
- /// Left join.
- /// The SQL.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder LeftJoin(string sql, object parameters = null)
- {
- AddClause("leftjoin", sql, parameters, joiner: "\nLEFT JOIN ", prefix: "\nLEFT JOIN ", postfix: "\n");
- return this;
- }
-
- /// Wheres.
- /// The SQL.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder Where(string sql, object parameters = null)
- {
- AddClause("where", sql, parameters, " AND ", prefix: "WHERE ", postfix: "\n");
- return this;
- }
-
- /// Order by.
- /// The SQL.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder OrderBy(string sql, object parameters = null)
- {
- AddClause("orderby", sql, parameters, " , ", prefix: "ORDER BY ", postfix: "\n");
- return this;
- }
-
- /// Selects.
- /// The SQL.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder Select(string sql, object parameters = null)
- {
- AddClause("select", sql, parameters, " , ", prefix: "", postfix: "\n");
- return this;
- }
-
- /// Adds the parameters.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder AddParameters(object parameters)
- {
- AddClause("--parameters", "", parameters, "");
- return this;
- }
-
- /// Joins.
- /// The SQL.
- /// Options for controlling the operation.
- /// A SqlBuilder.
- public SqlBuilder Join(string sql, object parameters = null)
- {
- AddClause("join", sql, parameters, joiner: "\nJOIN ", prefix: "\nJOIN", postfix: "\n");
- return this;
- }
- }
+ .DefineDynamicModule("tmpModule")
+ .DefineType("SqlBuilderDynamicParameters", TypeAttributes.Public | TypeAttributes.Class);
+
+ var emptyCtor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
+ var ctorIL = emptyCtor.GetILGenerator();
+
+ var unsetValues = new List();
+
+ // Loop over the attributes that will be used as the properties names in out new type
+ foreach (var p in properties)
+ {
+ // Generate a private field
+ var field = typeBuilder.DefineField("_" + p.Name, p.Type, FieldAttributes.Private);
+
+ //set default values with Emit for popular types
+ if (p.Type == typeof(int))
+ {
+ ctorIL.Emit(OpCodes.Ldarg_0);
+ ctorIL.Emit(OpCodes.Ldc_I4, (int)p.Value);
+ ctorIL.Emit(OpCodes.Stfld, field);
+ }
+ else if (p.Type == typeof(long))
+ {
+ ctorIL.Emit(OpCodes.Ldarg_0);
+ ctorIL.Emit(OpCodes.Ldc_I8, (long)p.Value);
+ ctorIL.Emit(OpCodes.Stfld, field);
+ }
+ else if (p.Type == typeof(string))
+ {
+ ctorIL.Emit(OpCodes.Ldarg_0);
+ ctorIL.Emit(OpCodes.Ldstr, (string)p.Value);
+ ctorIL.Emit(OpCodes.Stfld, field);
+ }
+ else
+ {
+ unsetValues.Add(p); //otherwise use reflection
+ }
+
+ // Generate a public property
+ var property = typeBuilder.DefineProperty(p.Name, PropertyAttributes.None, p.Type, new[] { p.Type });
+
+ // Define the "get" accessor method for current private field.
+ var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + p.Name, GetSetAttr, p.Type, Type.EmptyTypes);
+
+ // Get Property impl
+ var currGetIL = currGetPropMthdBldr.GetILGenerator();
+ currGetIL.Emit(OpCodes.Ldarg_0);
+ currGetIL.Emit(OpCodes.Ldfld, field);
+ currGetIL.Emit(OpCodes.Ret);
+
+ // Define the "set" accessor method for current private field.
+ var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + p.Name, GetSetAttr, null, new[] { p.Type });
+
+ // Set Property impl
+ var currSetIL = currSetPropMthdBldr.GetILGenerator();
+ currSetIL.Emit(OpCodes.Ldarg_0);
+ currSetIL.Emit(OpCodes.Ldarg_1);
+ currSetIL.Emit(OpCodes.Stfld, field);
+ currSetIL.Emit(OpCodes.Ret);
+
+ // Hook up, getters and setters.
+ property.SetGetMethod(currGetPropMthdBldr);
+ property.SetSetMethod(currSetPropMthdBldr);
+ }
+
+ ctorIL.Emit(OpCodes.Ret);
+
+ var generetedType = typeBuilder.CreateType();
+ var instance = Activator.CreateInstance(generetedType);
+
+ //Using reflection for less property types. Not caching since it's a generated type.
+ foreach (var p in unsetValues)
+ {
+ generetedType.GetProperty(p.Name).GetSetMethod().Invoke(instance, new[] { p.Value });
+ }
+
+ return instance;
+ }
+ }
+
+ class Clause
+ {
+ public string Sql { get; set; }
+ public object Parameters { get; set; }
+ public bool IsInclusive { get; set; }
+ }
+
+ class Clauses : List
+ {
+ string joiner;
+ string prefix;
+ string postfix;
+
+ public Clauses(string joiner, string prefix = "", string postfix = "")
+ {
+ this.joiner = joiner;
+ this.prefix = prefix;
+ this.postfix = postfix;
+ }
+
+ public string ResolveClauses(DynamicParameters p)
+ {
+ foreach (var item in this)
+ {
+ p.AddDynamicParams(item.Parameters);
+ }
+ return this.Any(a => a.IsInclusive)
+ ? prefix +
+ string.Join(joiner,
+ this.Where(a => !a.IsInclusive)
+ .Select(c => c.Sql)
+ .Union(new[]
+ {
+ " ( " +
+ string.Join(" OR ", this.Where(a => a.IsInclusive).Select(c => c.Sql).ToArray()) +
+ " ) "
+ }).ToArray()) + postfix
+ : prefix + string.Join(joiner, this.Select(c => c.Sql).ToArray()) + postfix;
+ }
+ }
+
+ public class Template
+ {
+ readonly string sql;
+ readonly SqlBuilder builder;
+ readonly object initParams;
+ int dataSeq = -1; // Unresolved
+
+ public Template(SqlBuilder builder, string sql, object parameters)
+ {
+ this.initParams = parameters;
+ this.sql = sql;
+ this.builder = builder;
+ }
+
+ static System.Text.RegularExpressions.Regex regex =
+ new System.Text.RegularExpressions.Regex(@"\/\*\*.+\*\*\/", System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Multiline);
+
+ void ResolveSql()
+ {
+ if (dataSeq != builder.seq)
+ {
+ DynamicParameters p = new DynamicParameters(initParams);
+
+ rawSql = sql;
+
+ foreach (var pair in builder.data)
+ {
+ rawSql = rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses(p));
+ }
+ parameters = p.CreateDynamicType();
+
+ // replace all that is left with empty
+ rawSql = regex.Replace(rawSql, "");
+
+ dataSeq = builder.seq;
+ }
+ }
+
+ string rawSql;
+ object parameters;
+
+ public string RawSql { get { ResolveSql(); return rawSql; } }
+ public object Parameters { get { ResolveSql(); return parameters; } }
+ }
+
+
+ public SqlBuilder()
+ {
+ }
+
+ public Template AddTemplate(string sql, object parameters = null)
+ {
+ return new Template(this, sql, parameters);
+ }
+
+ void AddClause(string name, string sql, object parameters, string joiner, string prefix = "", string postfix = "", bool IsInclusive = false)
+ {
+ Clauses clauses;
+ if (!data.TryGetValue(name, out clauses))
+ {
+ clauses = new Clauses(joiner, prefix, postfix);
+ data[name] = clauses;
+ }
+ clauses.Add(new Clause { Sql = sql, Parameters = parameters });
+ seq++;
+ }
+
+ public SqlBuilder Intersect(string sql, object parameters = null)
+ {
+ AddClause("intersect", sql, parameters, joiner: "\nINTERSECT\n ", prefix: "\n ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder InnerJoin(string sql, object parameters = null)
+ {
+ AddClause("innerjoin", sql, parameters, joiner: "\nINNER JOIN ", prefix: "\nINNER JOIN ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder LeftJoin(string sql, object parameters = null)
+ {
+ AddClause("leftjoin", sql, parameters, joiner: "\nLEFT JOIN ", prefix: "\nLEFT JOIN ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder RightJoin(string sql, object parameters = null)
+ {
+ AddClause("rightjoin", sql, parameters, joiner: "\nRIGHT JOIN ", prefix: "\nRIGHT JOIN ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder Where(string sql, object parameters = null)
+ {
+ AddClause("where", sql, parameters, " AND ", prefix: "WHERE ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder OrWhere(string sql, object parameters = null)
+ {
+ AddClause("where", sql, parameters, " AND ", prefix: "WHERE ", postfix: "\n", IsInclusive: true);
+ return this;
+ }
+
+ public SqlBuilder OrderBy(string sql, object parameters = null)
+ {
+ AddClause("orderby", sql, parameters, " , ", prefix: "ORDER BY ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder Select(string sql, object parameters = null)
+ {
+ AddClause("select", sql, parameters, " , ", prefix: "", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder AddParameters(object parameters)
+ {
+ AddClause("--parameters", "", parameters, "");
+ return this;
+ }
+
+ public SqlBuilder Join(string sql, object parameters = null)
+ {
+ AddClause("join", sql, parameters, joiner: "\nJOIN ", prefix: "\nJOIN ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder GroupBy(string sql, object parameters = null)
+ {
+ AddClause("groupby", sql, parameters, joiner: " , ", prefix: "\nGROUP BY ", postfix: "\n");
+ return this;
+ }
+
+ public SqlBuilder Having(string sql, object parameters = null)
+ {
+ AddClause("having", sql, parameters, joiner: "\nAND ", prefix: "HAVING ", postfix: "\n");
+ return this;
+ }
+ }
#endif
-
-}
\ No newline at end of file
+}
diff --git a/src/dapper-dot-net/.gitattributes b/src/dapper-dot-net/.gitattributes
new file mode 100644
index 0000000..0a575e9
--- /dev/null
+++ b/src/dapper-dot-net/.gitattributes
@@ -0,0 +1,49 @@
+*.doc diff=astextplain
+*.DOC diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot diff=astextplain
+*.DOT diff=astextplain
+*.pdf diff=astextplain
+*.PDF diff=astextplain
+*.rtf diff=astextplain
+*.RTF diff=astextplain
+
+*.jpg binary
+*.png binary
+*.gif binary
+
+*.cs -text diff=csharp
+*.vb -text
+*.c -text
+*.cpp -text
+*.cxx -text
+*.h -text
+*.hxx -text
+*.py -text
+*.rb -text
+*.java -text
+*.html -text
+*.htm -text
+*.css -text
+*.scss -text
+*.sass -text
+*.less -text
+*.js -text
+*.lisp -text
+*.clj -text
+*.sql -text
+*.php -text
+*.lua -text
+*.m -text
+*.asm -text
+*.erl -text
+*.fs -text
+*.fsx -text
+*.hs -text
+
+*.csproj -text merge=union
+*.vbproj -text merge=union
+*.fsproj -text merge=union
+*.dbproj -text merge=union
+*.sln -text merge=union
diff --git a/src/dapper-dot-net/.hgignore b/src/dapper-dot-net/.hgignore
index 3bf2be9..e398d2c 100644
--- a/src/dapper-dot-net/.hgignore
+++ b/src/dapper-dot-net/.hgignore
@@ -1,3 +1,11 @@
/*.suo
bin/*
-obj/*
\ No newline at end of file
+obj/*
+/*.user
+glob:Dapper/NuGet.exe
+syntax: glob
+*.docstates
+glob:Dapper.Contrib/NuGet.exe
+*.nupkg
+*/NuGet.exe
+_ReSharper.*
diff --git a/src/dapper-dot-net/.nuget/packages.config b/src/dapper-dot-net/.nuget/packages.config
new file mode 100644
index 0000000..a025407
--- /dev/null
+++ b/src/dapper-dot-net/.nuget/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/dapper-dot-net/Dapper - VS2012.sln b/src/dapper-dot-net/Dapper - VS2012.sln
new file mode 100644
index 0000000..acdda95
--- /dev/null
+++ b/src/dapper-dot-net/Dapper - VS2012.sln
@@ -0,0 +1,213 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30324.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperTests NET40", "Tests\DapperTests NET40.csproj", "{A2A80512-11F4-4028-A995-505463632C84}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper NET35", "Dapper NET35\Dapper NET35.csproj", "{B26305D8-3A89-4D68-A981-9BBF378B81FA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib", "Dapper.Contrib\Dapper.Contrib.csproj", "{C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib.Tests", "Dapper.Contrib.Tests\Dapper.Contrib.Tests.csproj", "{A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperTests NET35", "DapperTests NET35\DapperTests NET35.csproj", "{3BAA9F79-BA0A-4092-B47B-20170DD47989}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.SqlBuilder", "Dapper.SqlBuilder\Dapper.SqlBuilder.csproj", "{BF782EF1-2B0F-42FA-9DD0-928454A94C6D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Rainbow", "Dapper.Rainbow\Dapper.Rainbow.csproj", "{21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{E043638B-9516-477C-A75A-254DD55D4113}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\packages.config = .nuget\packages.config
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper NET40", "Dapper NET40\Dapper NET40.csproj", "{DAF737E1-05B5-4189-A5AA-DAC6233B64D7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper NET45", "Dapper NET45\Dapper NET45.csproj", "{0FFF5BC7-0A4B-4D87-835E-4FAD70937507}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E757192C-9411-458D-8815-8DAB34E12D03}"
+ ProjectSection(SolutionItems) = preProject
+ Dapper.Contrib.nuspec = Dapper.Contrib.nuspec
+ Dapper.EntityFramework.nuspec = Dapper.EntityFramework.nuspec
+ Dapper.nuspec = Dapper.nuspec
+ Dapper.Rainbow.nuspec = Dapper.Rainbow.nuspec
+ License.txt = License.txt
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperTests NET45", "DapperTests NET45\DapperTests NET45.csproj", "{5A5183F5-B774-42C9-A992-0A9C85FBE770}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.EntityFramework NET45", "Dapper.EntityFramework NET45\Dapper.EntityFramework NET45.csproj", "{C4FF57CD-F033-40D3-892C-EE5290BA3999}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.EntityFramework NET40", "Dapper.EntityFramework NET40\Dapper.EntityFramework NET40.csproj", "{2544DFBA-7F64-4003-9C36-D8337F770A36}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib NET45", "Dapper.Contrib NET45\Dapper.Contrib NET45.csproj", "{302EC82F-A81B-48C5-B653-B5C75D2BD103}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Rainbow NET45", "Dapper.Rainbow NET45\Dapper.Rainbow NET45.csproj", "{DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib.Tests NET45", "Dapper.Contrib.Tests NET45\Dapper.Contrib.Tests NET45.csproj", "{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A2A80512-11F4-4028-A995-505463632C84}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Debug|x86.ActiveCfg = Debug|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Debug|x86.Build.0 = Debug|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Release|Any CPU.ActiveCfg = Release|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Release|Mixed Platforms.Build.0 = Release|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Release|x86.ActiveCfg = Release|x86
+ {A2A80512-11F4-4028-A995-505463632C84}.Release|x86.Build.0 = Release|x86
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}.Release|x86.ActiveCfg = Release|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C2FC4DF5-C8D1-4EA8-8E0C-85A3793EB0BB}.Release|x86.ActiveCfg = Release|Any CPU
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Debug|x86.ActiveCfg = Debug|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Debug|x86.Build.0 = Debug|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Release|Any CPU.ActiveCfg = Release|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Release|Mixed Platforms.Build.0 = Release|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Release|x86.ActiveCfg = Release|x86
+ {A4F4A06E-D179-4251-A232-AEF4CE9AD9B5}.Release|x86.Build.0 = Release|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Debug|x86.ActiveCfg = Debug|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Debug|x86.Build.0 = Debug|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Release|Any CPU.ActiveCfg = Release|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Release|Mixed Platforms.Build.0 = Release|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Release|x86.ActiveCfg = Release|x86
+ {3BAA9F79-BA0A-4092-B47B-20170DD47989}.Release|x86.Build.0 = Release|x86
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {BF782EF1-2B0F-42FA-9DD0-928454A94C6D}.Release|x86.ActiveCfg = Release|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {21BC6EA8-3D10-4CC9-A1B3-9FAD59F7D1BB}.Release|x86.ActiveCfg = Release|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}.Release|x86.ActiveCfg = Release|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}.Release|x86.ActiveCfg = Release|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {5A5183F5-B774-42C9-A992-0A9C85FBE770}.Release|x86.ActiveCfg = Release|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C4FF57CD-F033-40D3-892C-EE5290BA3999}.Release|x86.ActiveCfg = Release|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2544DFBA-7F64-4003-9C36-D8337F770A36}.Release|x86.ActiveCfg = Release|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Release|Any CPU.Build.0 = Release|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {302EC82F-A81B-48C5-B653-B5C75D2BD103}.Release|x86.ActiveCfg = Release|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|x86.ActiveCfg = Release|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/dapper-dot-net/Dapper NET35/Dapper NET35.csproj b/src/dapper-dot-net/Dapper NET35/Dapper NET35.csproj
new file mode 100644
index 0000000..8b031ad
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET35/Dapper NET35.csproj
@@ -0,0 +1,66 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {B26305D8-3A89-4D68-A981-9BBF378B81FA}
+ Library
+ Properties
+ Dapper
+ Dapper
+ v3.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ TRACE;DEBUG;CSHARP30
+ prompt
+ 4
+ 3
+ bin\Debug\Dapper.xml
+ true
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;CSHARP30
+ prompt
+ 4
+ 3
+ bin\Release\Dapper.xml
+ true
+ false
+
+
+
+
+
+
+
+
+
+ AssemblyInfo.cs
+
+
+ SqlMapper.cs
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/dapper-dot-net/Dapper NET40/Dapper NET40.csproj b/src/dapper-dot-net/Dapper NET40/Dapper NET40.csproj
new file mode 100644
index 0000000..1844547
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET40/Dapper NET40.csproj
@@ -0,0 +1,59 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {DAF737E1-05B5-4189-A5AA-DAC6233B64D7}
+ Library
+ Properties
+ Dapper
+ Dapper
+ v4.0
+ 512
+ Client
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+ bin\Debug\Dapper.xml
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\Dapper.xml
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/dapper-dot-net/Dapper NET40/Properties/AssemblyInfo.cs b/src/dapper-dot-net/Dapper NET40/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..52d6653
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET40/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Dapper")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Stack Exchange")]
+[assembly: AssemblyProduct("Dapper")]
+[assembly: AssemblyCopyright("Copyright © Sam Saffron 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: CLSCompliant(true)]
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("59080aa9-fa65-438f-ad2e-772d840effa9")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.34.0.0")]
+[assembly: AssemblyFileVersion("1.34.0.0")]
diff --git a/src/dapper-dot-net/Dapper NET40/SqlMapper.cs b/src/dapper-dot-net/Dapper NET40/SqlMapper.cs
new file mode 100644
index 0000000..f1b36ab
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET40/SqlMapper.cs
@@ -0,0 +1,5242 @@
+/*
+ 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;
+using System.Linq.Expressions;
+
+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,
+ ///
+ /// Should the plan cache be bypassed?
+ ///
+ NoCache = 4
+ }
+ ///
+ /// 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);
+ }
+ }
+ private readonly string commandText;
+ private readonly object parameters;
+ private readonly IDbTransaction transaction;
+ private readonly int? commandTimeout;
+ private readonly CommandType? commandType;
+ private readonly CommandFlags flags;
+
+
+ internal void OnCompleted()
+ {
+ if (parameters is SqlMapper.IParameterCallbacks)
+ {
+ ((SqlMapper.IParameterCallbacks)parameters).OnCompleted();
+ }
+ }
+ ///
+ /// 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; } }
+
+ ///
+ /// Should the plan for this query be cached?
+ ///
+ internal bool AddToCache { get { return (flags & CommandFlags.NoCache) == 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
+ }
+
+ private CommandDefinition(object parameters) : this()
+ {
+ this.parameters = parameters;
+ }
+
+#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 init = GetInit(cmd.GetType());
+ if (init != null) init(cmd);
+ 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> commandInitCache;
+ 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;
+ }
+ static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType)
+ {
+ var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
+ ParameterInfo[] indexers;
+ if (prop != null && prop.CanWrite && prop.PropertyType == expectedType
+ && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0))
+ {
+ return prop.GetSetMethod();
+ }
+ return null;
+ }
+ }
+
+ ///
+ /// 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; }
+ }
+
+ ///
+ /// Extends IDynamicParameters with facitilies for executing callbacks after commands have completed
+ ///
+ public partial interface IParameterCallbacks : IDynamicParameters
+ {
+ ///
+ /// Invoked when the command has executed
+ ///
+ void OnCompleted();
+ }
+
+ ///
+ /// 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);
+ }
+
+ ///
+ /// A type handler for data-types that are supported by the underlying provider, but which need
+ /// a well-known UdtTypeName to be specified
+ ///
+ public class UdtTypeHandler : ITypeHandler
+ {
+ private readonly string udtTypeName;
+ ///
+ /// Creates a new instance of UdtTypeHandler with the specified UdtTypeName
+ ///
+ public UdtTypeHandler(string udtTypeName)
+ {
+ if (string.IsNullOrEmpty(udtTypeName)) throw new ArgumentException("Cannot be null or empty", udtTypeName);
+ this.udtTypeName = udtTypeName;
+ }
+ object ITypeHandler.Parse(Type destinationType, object value)
+ {
+ return value is DBNull ? null : value;
+ }
+
+ void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
+ {
+ parameter.Value = ((object)value) ?? DBNull.Value;
+ if (parameter is System.Data.SqlClient.SqlParameter)
+ {
+ ((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
+ }
+ }
+ }
+
+ ///
+ /// 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 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;
+
+ AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), false);
+ }
+
+ ///
+ /// Clear the registered type handlers
+ ///
+ public static void ResetTypeHandlers()
+ {
+ typeHandlers = new Dictionary();
+ AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), true);
+ }
+ ///
+ /// Configire the specified type to be mapped to a given db-type
+ ///
+ public static void AddTypeMap(Type type, DbType dbType)
+ {
+ // use clone, mutate, replace to avoid threading issues
+ var snapshot = typeMap;
+
+ DbType oldValue;
+ if (snapshot.TryGetValue(type, out oldValue) && oldValue == dbType) return; // nothing to do
+
+ var newCopy = new Dictionary(snapshot);
+ newCopy[type] = dbType;
+ typeMap = newCopy;
+ }
+
+ ///
+ /// Configire the specified type to be processed by a custom handler
+ ///
+ public static void AddTypeHandler(Type type, ITypeHandler handler)
+ {
+ AddTypeHandlerImpl(type, handler, true);
+ }
+
+ ///
+ /// Configire the specified type to be processed by a custom handler
+ ///
+ public static void AddTypeHandlerImpl(Type type, ITypeHandler handler, bool clone)
+ {
+ if (type == null) throw new ArgumentNullException("type");
+
+ Type secondary = null;
+ if(type.IsValueType)
+ {
+ var underlying = Nullable.GetUnderlyingType(type);
+ if(underlying == null)
+ {
+ secondary = typeof(Nullable<>).MakeGenericType(type); // the Nullable
+ // type is already the T
+ }
+ else
+ {
+ secondary = type; // the Nullable
+ type = underlying; // the T
+ }
+ }
+
+ var snapshot = typeHandlers;
+ ITypeHandler oldValue;
+ if (snapshot.TryGetValue(type, out oldValue) && handler == oldValue) return; // nothing to do
+
+ var newCopy = clone ? new Dictionary(snapshot) : snapshot;
+
+#pragma warning disable 618
+ typeof(TypeHandlerCache<>).MakeGenericType(type).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
+ if(secondary != null)
+ {
+ typeof(TypeHandlerCache<>).MakeGenericType(secondary).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
+ }
+#pragma warning restore 618
+ if (handler == null)
+ {
+ newCopy.Remove(type);
+ if (secondary != null) newCopy.Remove(secondary);
+ }
+ else
+ {
+ newCopy[type] = handler;
+ if(secondary != null) newCopy[secondary] = handler;
+ }
+ typeHandlers = newCopy;
+ }
+
+ ///
+ /// Configire the specified type to be processed by a custom handler
+ ///
+ public static void AddTypeHandler(TypeHandler handler)
+ {
+ AddTypeHandlerImpl(typeof(T), handler, true);
+ }
+
+ ///
+ /// 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 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;
+ }
+ switch (type.FullName)
+ {
+ case "Microsoft.SqlServer.Types.SqlGeography":
+ AddTypeHandler(type, handler = new UdtTypeHandler("GEOGRAPHY"));
+ return DbType.Object;
+ case "Microsoft.SqlServer.Types.SqlGeometry":
+ AddTypeHandler(type, handler = new UdtTypeHandler("GEOMETRY"));
+ return DbType.Object;
+ case "Microsoft.SqlServer.Types.SqlHierarchyId":
+ AddTypeHandler(type, handler = new UdtTypeHandler("HIERARCHYID"));
+ 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, object 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);
+ }
+
+
+ ///
+ /// Execute parameterized SQL that selects a single value
+ ///
+ /// The first cell selected
+ public static object ExecuteScalar(
+#if CSHARP30
+this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
+#else
+this IDbConnection cnn, string sql, object 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 ExecuteScalarImpl
+ internal struct LiteralToken
+ {
+ private readonly string token, member;
+ ///
+ /// The text in the original command that should be replaced
+ ///
+ public string Token { get { return token; } }
+
+ ///
+ /// The name of the member referred to by the token
+ ///
+ public string Member { get { return member; } }
+ internal LiteralToken(string token, string member)
+ {
+ this.token = token;
+ this.member = member;
+ }
+
+ internal static readonly IList None = new LiteralToken[0];
+ }
+
+ ///
+ /// Replace all literal tokens with their text form
+ ///
+ public static void ReplaceLiterals(this IParameterLookup parameters, IDbCommand command)
+ {
+ var tokens = GetLiteralTokens(command.CommandText);
+ if (tokens.Count != 0) ReplaceLiterals(parameters, command, tokens);
+ }
+
+ internal static readonly MethodInfo format = typeof(SqlMapper).GetMethod("Format", BindingFlags.Public | BindingFlags.Static);
+ ///
+ /// Convert numeric values to their string form for SQL literal purposes
+ ///
+ [Obsolete("This is intended for internal usage only")]
+ public static string Format(object value)
+ {
+ if (value == null)
+ {
+ return "null";
+ }
+ else
+ {
+ switch (Type.GetTypeCode(value.GetType()))
+ {
+ case TypeCode.DBNull:
+ return "null";
+ case TypeCode.Boolean:
+ return ((bool)value) ? "1" : "0";
+ case TypeCode.Byte:
+ return ((byte)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.SByte:
+ return ((sbyte)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.UInt16:
+ return ((ushort)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Int16:
+ return ((short)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.UInt32:
+ return ((uint)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Int32:
+ return ((int)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.UInt64:
+ return ((ulong)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Int64:
+ return ((long)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Single:
+ return ((float)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Double:
+ return ((double)value).ToString(CultureInfo.InvariantCulture);
+ case TypeCode.Decimal:
+ return ((decimal)value).ToString(CultureInfo.InvariantCulture);
+ default:
+ if(value is IEnumerable && !(value is string))
+ {
+ var sb = new StringBuilder();
+ bool first = true;
+ foreach (object subval in (IEnumerable)value)
+ {
+ sb.Append(first ? '(' : ',').Append(Format(subval));
+ first = false;
+ }
+ if(first)
+ {
+ return "(select null where 1=0)";
+ }
+ else
+ {
+ return sb.Append(')').ToString();
+ }
+ }
+ throw new NotSupportedException(value.GetType().Name);
+ }
+ }
+ }
+
+
+ internal static void ReplaceLiterals(IParameterLookup parameters, IDbCommand command, IList tokens)
+ {
+ var sql = command.CommandText;
+ foreach (var token in tokens)
+ {
+ object value = parameters[token.Member];
+#pragma warning disable 0618
+ string text = Format(value);
+#pragma warning restore 0618
+ sql = sql.Replace(token.Token, text);
+ }
+ command.CommandText = sql;
+ }
+
+ internal static IList GetLiteralTokens(string sql)
+ {
+ if (string.IsNullOrEmpty(sql)) return LiteralToken.None;
+ if (!literalTokens.IsMatch(sql)) return LiteralToken.None;
+
+ var matches = literalTokens.Matches(sql);
+ var found = new HashSet(StringComparer.InvariantCulture);
+ List list = new List(matches.Count);
+ foreach(Match match in matches)
+ {
+ string token = match.Value;
+ if(found.Add(match.Value))
+ {
+ list.Add(new LiteralToken(token, match.Groups[1].Value));
+ }
+ }
+ return list.Count == 0 ? LiteralToken.None : list;
+ }
+
+ ///
+ /// Internal use only
+ ///
+ public static Action CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused)
+ {
+ return CreateParamInfoGenerator(identity, checkForDuplicates, removeUnused, GetLiteralTokens(identity.sql));
+ }
+
+ internal static Action CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused, IList literals)
+ {
+ Type type = identity.parametersType;
+
+ bool filterParams = false;
+ if (removeUnused && identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text)
+ {
+ filterParams = !smellsLikeOleDb.IsMatch(identity.sql);
+ }
+ var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);
+
+ var il = dm.GetILGenerator();
+
+ il.DeclareLocal(type); // 0
+ bool haveInt32Arg1 = false;
+ il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]
+ il.Emit(OpCodes.Unbox_Any, type); // stack is now [typed-param]
+ il.Emit(OpCodes.Stloc_0);// stack is now empty
+
+ il.Emit(OpCodes.Ldarg_0); // stack is now [command]
+ il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); // stack is now [parameters]
+
+ var propsArr = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0).ToArray();
+ var ctors = type.GetConstructors();
+ ParameterInfo[] ctorParams;
+ IEnumerable props = null;
+ // try to detect tuple patterns, e.g. anon-types, and use that to choose the order
+ // otherwise: alphabetical
+ if (ctors.Length == 1 && propsArr.Length == (ctorParams = ctors[0].GetParameters()).Length)
+ {
+ // check if reflection was kind enough to put everything in the right order for us
+ bool ok = true;
+ for (int i = 0; i < propsArr.Length; i++)
+ {
+ if (!string.Equals(propsArr[i].Name, ctorParams[i].Name, StringComparison.InvariantCultureIgnoreCase))
+ {
+ ok = false;
+ break;
+ }
+ }
+ if(ok)
+ {
+ // pre-sorted; the reflection gods have smiled upon us
+ props = propsArr;
+ }
+ else { // might still all be accounted for; check the hard way
+ var positionByName = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+ foreach(var param in ctorParams)
+ {
+ positionByName[param.Name] = param.Position;
+ }
+ if (positionByName.Count == propsArr.Length)
+ {
+ int[] positions = new int[propsArr.Length];
+ ok = true;
+ for (int i = 0; i < propsArr.Length; i++)
+ {
+ int pos;
+ if (!positionByName.TryGetValue(propsArr[i].Name, out pos))
+ {
+ ok = false;
+ break;
+ }
+ positions[i] = pos;
+ }
+ if (ok)
+ {
+ Array.Sort(positions, propsArr);
+ props = propsArr;
+ }
+ }
+ }
+ }
+ if(props == null) props = propsArr.OrderBy(x => x.Name);
+ if (filterParams)
+ {
+ props = FilterParameters(props, identity.sql);
+ }
+
+ foreach (var prop in props)
+ {
+ if (typeof(ICustomQueryParameter).IsAssignableFrom(prop.PropertyType))
+ {
+ il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
+ il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [custom]
+ il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [custom] [command]
+ il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [custom] [command] [name]
+ il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod("AddParameter"), null); // stack is now [parameters]
+ continue;
+ }
+ ITypeHandler handler;
+ DbType dbType = LookupDbType(prop.PropertyType, prop.Name, out handler);
+ if (dbType == DynamicParameters.EnumerableMultiParameter)
+ {
+ // this actually represents special handling for list types;
+ il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
+ il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
+ il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
+ il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
+ if (prop.PropertyType.IsValueType)
+ {
+ il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
+ }
+ il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]
+ continue;
+ }
+ il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters]
+
+ il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]
+
+ if (checkForDuplicates)
+ {
+ // need to be a little careful about adding; use a utility method
+ il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [command] [name]
+ il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("FindOrAddParameter"), null); // stack is [parameters] [parameter]
+ }
+ else
+ {
+ // no risk of duplicates; just blindly add
+ il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter]
+
+ il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
+ il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [parameter] [parameter] [name]
+ il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
+ }
+ if (dbType != DbType.Time && handler == null) // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time
+ {
+ il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
+ EmitInt32(il, (int)dbType);// stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type]
+
+ il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
+ }
+
+ il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
+ EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [[parameters]] [parameter] [parameter] [dir]
+ il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
+
+ il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
+ il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
+ il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [[parameters]] [parameter] [parameter] [typed-value]
+ bool checkForNull = true;
+ if (prop.PropertyType.IsValueType)
+ {
+ il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]
+ if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
+ { // struct but not Nullable; boxed value cannot be null
+ checkForNull = false;
+ }
+ }
+ if (checkForNull)
+ {
+ if ((dbType == DbType.String || dbType == DbType.AnsiString) && !haveInt32Arg1)
+ {
+ il.DeclareLocal(typeof(int));
+ haveInt32Arg1 = true;
+ }
+ // relative stack: [boxed value]
+ il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]
+ Label notNull = il.DefineLabel();
+ Label? allDone = (dbType == DbType.String || dbType == DbType.AnsiString) ? il.DefineLabel() : (Label?)null;
+ il.Emit(OpCodes.Brtrue_S, notNull);
+ // relative stack [boxed value = null]
+ il.Emit(OpCodes.Pop); // relative stack empty
+ il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); // relative stack [DBNull]
+ if (dbType == DbType.String || dbType == DbType.AnsiString)
+ {
+ EmitInt32(il, 0);
+ il.Emit(OpCodes.Stloc_1);
+ }
+ if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);
+ il.MarkLabel(notNull);
+ if (prop.PropertyType == typeof(string))
+ {
+ il.Emit(OpCodes.Dup); // [string] [string]
+ il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); // [string] [length]
+ EmitInt32(il, DbString.DefaultLength); // [string] [length] [4000]
+ il.Emit(OpCodes.Cgt); // [string] [0 or 1]
+ Label isLong = il.DefineLabel(), lenDone = il.DefineLabel();
+ il.Emit(OpCodes.Brtrue_S, isLong);
+ EmitInt32(il, DbString.DefaultLength); // [string] [4000]
+ il.Emit(OpCodes.Br_S, lenDone);
+ il.MarkLabel(isLong);
+ EmitInt32(il, -1); // [string] [-1]
+ il.MarkLabel(lenDone);
+ il.Emit(OpCodes.Stloc_1); // [string]
+ }
+ if (prop.PropertyType.FullName == LinqBinary)
+ {
+ il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod("ToArray", BindingFlags.Public | BindingFlags.Instance), null);
+ }
+ if (allDone != null) il.MarkLabel(allDone.Value);
+ // relative stack [boxed value or DBNull]
+ }
+
+ if (handler != null)
+ {
+#pragma warning disable 618
+ il.Emit(OpCodes.Call, typeof(TypeHandlerCache<>).MakeGenericType(prop.PropertyType).GetMethod("SetValue")); // stack is now [parameters] [[parameters]] [parameter]
+#pragma warning restore 618
+ }
+ else
+ {
+ il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
+ }
+
+ if (prop.PropertyType == typeof(string))
+ {
+ var endOfSize = il.DefineLabel();
+ // don't set if 0
+ il.Emit(OpCodes.Ldloc_1); // [parameters] [[parameters]] [parameter] [size]
+ il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter]
+
+ il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
+ il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]
+ il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]
+
+ il.MarkLabel(endOfSize);
+ }
+ if (checkForDuplicates)
+ {
+ // stack is now [parameters] [parameter]
+ il.Emit(OpCodes.Pop); // don't need parameter any more
+ }
+ else
+ {
+ // stack is now [parameters] [parameters] [parameter]
+ // blindly add
+ il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]
+ il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care
+ }
+ }
+
+ // stack is currently [parameters]
+ il.Emit(OpCodes.Pop); // stack is now empty
+
+ if(literals.Count != 0 && propsArr != null)
+ {
+ il.Emit(OpCodes.Ldarg_0); // command
+ il.Emit(OpCodes.Ldarg_0); // command, command
+ var cmdText = typeof(IDbCommand).GetProperty("CommandText");
+ il.EmitCall(OpCodes.Callvirt, cmdText.GetGetMethod(), null); // command, sql
+ Dictionary locals = null;
+ LocalBuilder local = null;
+ foreach (var literal in literals)
+ {
+ // find the best member, preferring case-sensitive
+ PropertyInfo exact = null, fallback = null;
+ string huntName = literal.Member;
+ for(int i = 0; i < propsArr.Length;i++)
+ {
+ string thisName = propsArr[i].Name;
+ if(string.Equals(thisName, huntName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ fallback = propsArr[i];
+ if(string.Equals(thisName, huntName, StringComparison.InvariantCulture))
+ {
+ exact = fallback;
+ break;
+ }
+ }
+ }
+ var prop = exact ?? fallback;
+
+ if(prop != null)
+ {
+ il.Emit(OpCodes.Ldstr, literal.Token);
+ il.Emit(OpCodes.Ldloc_0); // command, sql, typed parameter
+ il.EmitCall(OpCodes.Callvirt, prop.GetGetMethod(), null); // command, sql, typed value
+ Type propType = prop.PropertyType;
+ var typeCode = Type.GetTypeCode(propType);
+ switch (typeCode)
+ {
+ case TypeCode.Boolean:
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ case TypeCode.UInt16:
+ case TypeCode.Int16:
+ case TypeCode.UInt32:
+ case TypeCode.Int32:
+ case TypeCode.UInt64:
+ case TypeCode.Int64:
+ case TypeCode.Single:
+ case TypeCode.Double:
+ case TypeCode.Decimal:
+ // neeed to stloc, ldloca, call
+ // re-use existing locals (both the last known, and via a dictionary)
+ var convert = GetToString(typeCode);
+ if (local == null || local.LocalType != propType)
+ {
+ if (locals == null)
+ {
+ locals = new Dictionary();
+ local = null;
+ }
+ else
+ {
+ if (!locals.TryGetValue(propType, out local)) local = null;
+ }
+ if (local == null)
+ {
+ local = il.DeclareLocal(propType);
+ locals.Add(propType, local);
+ }
+ }
+ il.Emit(OpCodes.Stloc, local); // command, sql
+ il.Emit(OpCodes.Ldloca, local); // command, sql, ref-to-value
+ il.EmitCall(OpCodes.Call, InvariantCulture, null); // command, sql, ref-to-value, culture
+ il.EmitCall(OpCodes.Call, convert, null); // command, sql, string value
+ break;
+ default:
+ if (propType.IsValueType) il.Emit(OpCodes.Box, propType); // command, sql, object value
+ il.EmitCall(OpCodes.Call, format, null); // command, sql, string value
+ break;
+
+ }
+ il.EmitCall(OpCodes.Callvirt, StringReplace, null);
+ }
+ }
+ il.EmitCall(OpCodes.Callvirt, cmdText.GetSetMethod(), null); // empty
+ }
+
+ il.Emit(OpCodes.Ret);
+ return (Action)dm.CreateDelegate(typeof(Action));
+ }
+ static readonly Dictionary toStrings = new[]
+ {
+ typeof(bool), typeof(sbyte), typeof(byte), typeof(ushort), typeof(short),
+ typeof(uint), typeof(int), typeof(ulong), typeof(long), typeof(float), typeof(double), typeof(decimal)
+ }.ToDictionary(x => Type.GetTypeCode(x), x => x.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(IFormatProvider) }, null));
+ static MethodInfo GetToString(TypeCode typeCode)
+ {
+ MethodInfo method;
+ return toStrings.TryGetValue(typeCode, out method) ? method : null;
+ }
+ static readonly MethodInfo StringReplace = typeof(string).GetMethod("Replace", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(string), typeof(string) }, null),
+ InvariantCulture = typeof(CultureInfo).GetProperty("InvariantCulture", BindingFlags.Public | BindingFlags.Static).GetGetMethod();
+
+ private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition command, Action paramReader)
+ {
+ IDbCommand cmd = null;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ cmd = command.SetupCommand(cnn, paramReader);
+ if (wasClosed) cnn.Open();
+ int result = cmd.ExecuteNonQuery();
+ command.OnCompleted();
+ return result;
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ if (cmd != null) cmd.Dispose();
+ }
+ }
+
+ private static T ExecuteScalarImpl(IDbConnection cnn, ref CommandDefinition command)
+ {
+ Action paramReader = null;
+ object param = command.Parameters;
+ if (param != null)
+ {
+ var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
+ paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader;
+ }
+
+ IDbCommand cmd = null;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ object result;
+ try
+ {
+ cmd = command.SetupCommand(cnn, paramReader);
+ if (wasClosed) cnn.Open();
+ result =cmd.ExecuteScalar();
+ command.OnCompleted();
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ if (cmd != null) cmd.Dispose();
+ }
+ return Parse(result);
+ }
+
+ private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefinition command, CommandBehavior commandBehavior)
+ {
+ Action paramReader = GetParameterReader(cnn, ref command);
+
+ IDbCommand cmd = null;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ cmd = command.SetupCommand(cnn, paramReader);
+ if (wasClosed) cnn.Open();
+ if (wasClosed) commandBehavior |= CommandBehavior.CloseConnection;
+ var reader = cmd.ExecuteReader(commandBehavior);
+ wasClosed = false; // don't dispose before giving it to them!
+
+ // note: command.FireOutputCallbacks(); would be useless here; parameters come at the **end** of the TDS stream
+ return reader;
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ if (cmd != null) cmd.Dispose();
+ }
+ }
+
+ private static Action GetParameterReader(IDbConnection cnn, ref CommandDefinition command)
+ {
+ object param = command.Parameters;
+ IEnumerable multiExec = (object)param as IEnumerable;
+ Identity identity;
+ CacheInfo info = null;
+ if (multiExec != null && !(multiExec is string))
+ {
+ throw new NotSupportedException("MultiExec is not supported by ExecuteReader");
+ }
+
+ // nice and simple
+ if (param != null)
+ {
+ identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
+ info = GetCacheInfo(identity, param, command.AddToCache);
+ }
+ var paramReader = info == null ? null : info.ParamReader;
+ return paramReader;
+ }
+
+ private static Func GetStructDeserializer(Type type, Type effectiveType, int index)
+ {
+ // no point using special per-type handling here; it boils down to the same, plus not all are supported anyway (see: SqlDataReader.GetChar - not supported!)
+#pragma warning disable 618
+ if (type == typeof(char))
+ { // this *does* need special handling, though
+ return r => SqlMapper.ReadChar(r.GetValue(index));
+ }
+ if (type == typeof(char?))
+ {
+ return r => SqlMapper.ReadNullableChar(r.GetValue(index));
+ }
+ if (type.FullName == LinqBinary)
+ {
+ return r => Activator.CreateInstance(type, r.GetValue(index));
+ }
+#pragma warning restore 618
+
+ if (effectiveType.IsEnum)
+ { // assume the value is returned as the correct type (int/byte/etc), but box back to the typed enum
+ return r =>
+ {
+ var val = r.GetValue(index);
+ if(val is float || val is double || val is decimal)
+ {
+ val = Convert.ChangeType(val, Enum.GetUnderlyingType(effectiveType), CultureInfo.InvariantCulture);
+ }
+ return val is DBNull ? null : Enum.ToObject(effectiveType, val);
+ };
+ }
+ ITypeHandler handler;
+ if(typeHandlers.TryGetValue(type, out handler))
+ {
+ return r =>
+ {
+ var val = r.GetValue(index);
+ return val is DBNull ? null : handler.Parse(type, val);
+ };
+ }
+ return r =>
+ {
+ var val = r.GetValue(index);
+ return val is DBNull ? null : val;
+ };
+ }
+
+ private static T Parse(object value)
+ {
+ if (value == null || value is DBNull) return default(T);
+ if (value is T) return (T)value;
+ var type = typeof(T);
+ type = Nullable.GetUnderlyingType(type) ?? type;
+ if (type.IsEnum)
+ {
+ if (value is float || value is double || value is decimal)
+ {
+ value = Convert.ChangeType(value, Enum.GetUnderlyingType(type), CultureInfo.InvariantCulture);
+ }
+ return (T)Enum.ToObject(type, value);
+ }
+ ITypeHandler handler;
+ if (typeHandlers.TryGetValue(type, out handler))
+ {
+ return (T)handler.Parse(type, value);
+ }
+ return (T)Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
+ }
+
+ static readonly MethodInfo
+ enumParse = typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }),
+ getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public)
+ .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int))
+ .Select(p => p.GetGetMethod()).First();
+
+ ///
+ /// Gets type-map for the given type
+ ///
+ /// Type map implementation, DefaultTypeMap instance if no override present
+ public static ITypeMap GetTypeMap(Type type)
+ {
+ if (type == null) throw new ArgumentNullException("type");
+ var map = (ITypeMap)_typeMaps[type];
+ if (map == null)
+ {
+ lock (_typeMaps)
+ { // double-checked; store this to avoid reflection next time we see this type
+ // since multiple queries commonly use the same domain-entity/DTO/view-model type
+ map = (ITypeMap)_typeMaps[type];
+ if (map == null)
+ {
+ map = new DefaultTypeMap(type);
+ _typeMaps[type] = map;
+ }
+ }
+ }
+ return map;
+ }
+
+ // use Hashtable to get free lockless reading
+ private static readonly Hashtable _typeMaps = new Hashtable();
+
+ ///
+ /// Set custom mapping for type deserializers
+ ///
+ /// Entity type to override
+ /// Mapping rules impementation, null to remove custom map
+ public static void SetTypeMap(Type type, ITypeMap map)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ if (map == null || map is DefaultTypeMap)
+ {
+ lock (_typeMaps)
+ {
+ _typeMaps.Remove(type);
+ }
+ }
+ else
+ {
+ lock (_typeMaps)
+ {
+ _typeMaps[type] = map;
+ }
+ }
+
+ PurgeQueryCacheByType(type);
+ }
+
+ ///
+ /// Internal use only
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Func GetTypeDeserializer(
+#if CSHARP30
+Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing
+#else
+Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false
+#endif
+)
+ {
+
+ var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(IDataReader) }, true);
+ var il = dm.GetILGenerator();
+ il.DeclareLocal(typeof(int));
+ il.DeclareLocal(type);
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Stloc_0);
+
+ if (length == -1)
+ {
+ length = reader.FieldCount - startBound;
+ }
+
+ if (reader.FieldCount <= startBound)
+ {
+ throw new ArgumentException(MultiMapSplitExceptionMessage, "splitOn");
+ }
+
+ var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray();
+
+ ITypeMap typeMap = GetTypeMap(type);
+
+ int index = startBound;
+
+ ConstructorInfo specializedConstructor = null;
+
+ bool supportInitialize = false;
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Ldloca_S, (byte)1);
+ il.Emit(OpCodes.Initobj, type);
+ }
+ else
+ {
+ var types = new Type[length];
+ for (int i = startBound; i < startBound + length; i++)
+ {
+ types[i - startBound] = reader.GetFieldType(i);
+ }
+
+ var ctor = typeMap.FindConstructor(names, types);
+ if (ctor == null)
+ {
+ string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
+ throw new InvalidOperationException(string.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
+ }
+
+ if (ctor.GetParameters().Length == 0)
+ {
+ il.Emit(OpCodes.Newobj, ctor);
+ il.Emit(OpCodes.Stloc_1);
+ supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
+ if(supportInitialize)
+ {
+ il.Emit(OpCodes.Ldloc_1);
+ il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
+ }
+ }
+ else
+ specializedConstructor = ctor;
+ }
+
+ il.BeginExceptionBlock();
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Ldloca_S, (byte)1);// [target]
+ }
+ else if (specializedConstructor == null)
+ {
+ il.Emit(OpCodes.Ldloc_1);// [target]
+ }
+
+ var members = (specializedConstructor != null
+ ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n))
+ : names.Select(n => typeMap.GetMember(n))).ToList();
+
+ // stack is now [target]
+
+ bool first = true;
+ var allDone = il.DefineLabel();
+ int enumDeclareLocal = -1, valueCopyLocal = il.DeclareLocal(typeof(object)).LocalIndex;
+ foreach (var item in members)
+ {
+ if (item != null)
+ {
+ if (specializedConstructor == null)
+ il.Emit(OpCodes.Dup); // stack is now [target][target]
+ Label isDbNullLabel = il.DefineLabel();
+ Label finishLabel = il.DefineLabel();
+
+ il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
+ EmitInt32(il, index); // stack is now [target][target][reader][index]
+ il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
+ il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]
+ il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]
+ il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object]
+ StoreLocal(il, valueCopyLocal);
+ Type colType = reader.GetFieldType(index);
+ Type memberType = item.MemberType;
+
+ if (memberType == typeof(char) || memberType == typeof(char?))
+ {
+ il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(
+ memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
+ }
+ else
+ {
+ il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
+ il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
+ il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]
+
+ // unbox nullable enums as the primitive, i.e. byte etc
+
+ var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
+ var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;
+
+ if (unboxType.IsEnum)
+ {
+ Type numericType = Enum.GetUnderlyingType(unboxType);
+ if(colType == typeof(string))
+ {
+ if (enumDeclareLocal == -1)
+ {
+ enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;
+ }
+ il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [target][target][string]
+ StoreLocal(il, enumDeclareLocal); // stack is now [target][target]
+ il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
+ il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
+ LoadLocal(il, enumDeclareLocal); // stack is now [target][target][enum-type][string]
+ il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
+ il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]
+ il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
+ }
+ else
+ {
+ FlexibleConvertBoxedFromHeadOfStack(il, colType, unboxType, numericType);
+ }
+
+ if (nullUnderlyingType != null)
+ {
+ il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
+ }
+ }
+ else if (memberType.FullName == LinqBinary)
+ {
+ il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
+ il.Emit(OpCodes.Newobj, memberType.GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
+ }
+ else
+ {
+ TypeCode dataTypeCode = Type.GetTypeCode(colType), unboxTypeCode = Type.GetTypeCode(unboxType);
+ bool hasTypeHandler;
+ if ((hasTypeHandler = typeHandlers.ContainsKey(unboxType)) || colType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType))
+ {
+ if (hasTypeHandler)
+ {
+#pragma warning disable 618
+ il.EmitCall(OpCodes.Call, typeof(TypeHandlerCache<>).MakeGenericType(unboxType).GetMethod("Parse"), null); // stack is now [target][target][typed-value]
+#pragma warning restore 618
+ }
+ else
+ {
+ il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
+ }
+ }
+ else
+ {
+ // not a direct match; need to tweak the unbox
+ FlexibleConvertBoxedFromHeadOfStack(il, colType, nullUnderlyingType ?? unboxType, null);
+ if (nullUnderlyingType != null)
+ {
+ il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
+ }
+
+ }
+
+ }
+ }
+ if (specializedConstructor == null)
+ {
+ // Store the value in the property/field
+ if (item.Property != null)
+ {
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Call, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
+ }
+ else
+ {
+ il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
+ }
+ }
+ else
+ {
+ il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
+ }
+ }
+
+ il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]
+
+ il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
+ if (specializedConstructor != null)
+ {
+ il.Emit(OpCodes.Pop);
+ if (item.MemberType.IsValueType)
+ {
+ int localIndex = il.DeclareLocal(item.MemberType).LocalIndex;
+ LoadLocalAddress(il, localIndex);
+ il.Emit(OpCodes.Initobj, item.MemberType);
+ LoadLocal(il, localIndex);
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldnull);
+ }
+ }
+ else
+ {
+ il.Emit(OpCodes.Pop); // stack is now [target][target]
+ il.Emit(OpCodes.Pop); // stack is now [target]
+ }
+
+ if (first && returnNullIfFirstMissing)
+ {
+ il.Emit(OpCodes.Pop);
+ il.Emit(OpCodes.Ldnull); // stack is now [null]
+ il.Emit(OpCodes.Stloc_1);
+ il.Emit(OpCodes.Br, allDone);
+ }
+
+ il.MarkLabel(finishLabel);
+ }
+ first = false;
+ index += 1;
+ }
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Pop);
+ }
+ else
+ {
+ if (specializedConstructor != null)
+ {
+ il.Emit(OpCodes.Newobj, specializedConstructor);
+ }
+ il.Emit(OpCodes.Stloc_1); // stack is empty
+ if (supportInitialize)
+ {
+ il.Emit(OpCodes.Ldloc_1);
+ il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("EndInit"), null);
+ }
+ }
+ il.MarkLabel(allDone);
+ il.BeginCatchBlock(typeof(Exception)); // stack is Exception
+ il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
+ il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
+ LoadLocal(il, valueCopyLocal); // stack is Exception, index, reader, value
+ il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);
+ il.EndExceptionBlock();
+
+ il.Emit(OpCodes.Ldloc_1); // stack is [rval]
+ if (type.IsValueType)
+ {
+ il.Emit(OpCodes.Box, type);
+ }
+ il.Emit(OpCodes.Ret);
+
+ return (Func)dm.CreateDelegate(typeof(Func));
+ }
+
+ private static void FlexibleConvertBoxedFromHeadOfStack(ILGenerator il, Type from, Type to, Type via)
+ {
+ MethodInfo op;
+ if(from == (via ?? to))
+ {
+ il.Emit(OpCodes.Unbox_Any, to); // stack is now [target][target][typed-value]
+ }
+ else if ((op = GetOperator(from,to)) != null)
+ {
+ // this is handy for things like decimal <===> double
+ il.Emit(OpCodes.Unbox_Any, from); // stack is now [target][target][data-typed-value]
+ il.Emit(OpCodes.Call, op); // stack is now [target][target][typed-value]
+ }
+ else
+ {
+ bool handled = false;
+ OpCode opCode = default(OpCode);
+ switch (Type.GetTypeCode(from))
+ {
+ case TypeCode.Boolean:
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ case TypeCode.Int16:
+ case TypeCode.UInt16:
+ case TypeCode.Int32:
+ case TypeCode.UInt32:
+ case TypeCode.Int64:
+ case TypeCode.UInt64:
+ case TypeCode.Single:
+ case TypeCode.Double:
+ handled = true;
+ switch (Type.GetTypeCode(via ?? to))
+ {
+ case TypeCode.Byte:
+ opCode = OpCodes.Conv_Ovf_I1_Un; break;
+ case TypeCode.SByte:
+ opCode = OpCodes.Conv_Ovf_I1; break;
+ case TypeCode.UInt16:
+ opCode = OpCodes.Conv_Ovf_I2_Un; break;
+ case TypeCode.Int16:
+ opCode = OpCodes.Conv_Ovf_I2; break;
+ case TypeCode.UInt32:
+ opCode = OpCodes.Conv_Ovf_I4_Un; break;
+ case TypeCode.Boolean: // boolean is basically an int, at least at this level
+ case TypeCode.Int32:
+ opCode = OpCodes.Conv_Ovf_I4; break;
+ case TypeCode.UInt64:
+ opCode = OpCodes.Conv_Ovf_I8_Un; break;
+ case TypeCode.Int64:
+ opCode = OpCodes.Conv_Ovf_I8; break;
+ case TypeCode.Single:
+ opCode = OpCodes.Conv_R4; break;
+ case TypeCode.Double:
+ opCode = OpCodes.Conv_R8; break;
+ default:
+ handled = false;
+ break;
+ }
+ break;
+ }
+ if (handled)
+ {
+ il.Emit(OpCodes.Unbox_Any, from); // stack is now [target][target][col-typed-value]
+ il.Emit(opCode); // stack is now [target][target][typed-value]
+ if (to == typeof(bool))
+ { // compare to zero; I checked "csc" - this is the trick it uses; nice
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Ceq);
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Ceq);
+ }
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldtoken, via ?? to); // stack is now [target][target][value][member-type-token]
+ il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); // stack is now [target][target][value][member-type]
+ il.EmitCall(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), null); // stack is now [target][target][boxed-member-type-value]
+ il.Emit(OpCodes.Unbox_Any, to); // stack is now [target][target][typed-value]
+ }
+ }
+ }
+
+ static MethodInfo GetOperator(Type from, Type to)
+ {
+ if (to == null) return null;
+ MethodInfo[] fromMethods, toMethods;
+ return ResolveOperator(fromMethods = from.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")
+ ?? ResolveOperator(toMethods = to.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")
+ ?? ResolveOperator(fromMethods, from, to, "op_Explicit")
+ ?? ResolveOperator(toMethods, from, to, "op_Explicit");
+
+ }
+ static MethodInfo ResolveOperator(MethodInfo[] methods, Type from, Type to, string name)
+ {
+ for (int i = 0; i < methods.Length; i++)
+ {
+ if (methods[i].Name != name || methods[i].ReturnType != to) continue;
+ var args = methods[i].GetParameters();
+ if (args.Length != 1 || args[0].ParameterType != from) continue;
+ return methods[i];
+ }
+ return null;
+ }
+
+ private static void LoadLocal(ILGenerator il, int index)
+ {
+ if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");
+ switch (index)
+ {
+ case 0: il.Emit(OpCodes.Ldloc_0); break;
+ case 1: il.Emit(OpCodes.Ldloc_1); break;
+ case 2: il.Emit(OpCodes.Ldloc_2); break;
+ case 3: il.Emit(OpCodes.Ldloc_3); break;
+ default:
+ if (index <= 255)
+ {
+ il.Emit(OpCodes.Ldloc_S, (byte)index);
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldloc, (short)index);
+ }
+ break;
+ }
+ }
+ private static void StoreLocal(ILGenerator il, int index)
+ {
+ if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");
+ switch (index)
+ {
+ case 0: il.Emit(OpCodes.Stloc_0); break;
+ case 1: il.Emit(OpCodes.Stloc_1); break;
+ case 2: il.Emit(OpCodes.Stloc_2); break;
+ case 3: il.Emit(OpCodes.Stloc_3); break;
+ default:
+ if (index <= 255)
+ {
+ il.Emit(OpCodes.Stloc_S, (byte)index);
+ }
+ else
+ {
+ il.Emit(OpCodes.Stloc, (short)index);
+ }
+ break;
+ }
+ }
+ private static void LoadLocalAddress(ILGenerator il, int index)
+ {
+ if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");
+
+ if (index <= 255)
+ {
+ il.Emit(OpCodes.Ldloca_S, (byte)index);
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldloca, (short)index);
+ }
+ }
+ ///
+ /// Throws a data exception, only used internally
+ ///
+ [Obsolete("Intended for internal use only")]
+ public static void ThrowDataException(Exception ex, int index, IDataReader reader, object value)
+ {
+ Exception toThrow;
+ try
+ {
+ string name = "(n/a)", formattedValue = "(n/a)";
+ if (reader != null && index >= 0 && index < reader.FieldCount)
+ {
+ name = reader.GetName(index);
+ try
+ {
+ if (value == null || value is DBNull)
+ {
+ formattedValue = "";
+ }
+ else
+ {
+ formattedValue = Convert.ToString(value) + " - " + Type.GetTypeCode(value.GetType());
+ }
+ }
+ catch (Exception valEx)
+ {
+ formattedValue = valEx.Message;
+ }
+ }
+ toThrow = new DataException(string.Format("Error parsing column {0} ({1}={2})", index, name, formattedValue), ex);
+ }
+ catch
+ { // throw the **original** exception, wrapped as DataException
+ toThrow = new DataException(ex.Message, ex);
+ }
+ throw toThrow;
+ }
+ private static void EmitInt32(ILGenerator il, int value)
+ {
+ switch (value)
+ {
+ case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
+ case 0: il.Emit(OpCodes.Ldc_I4_0); break;
+ case 1: il.Emit(OpCodes.Ldc_I4_1); break;
+ case 2: il.Emit(OpCodes.Ldc_I4_2); break;
+ case 3: il.Emit(OpCodes.Ldc_I4_3); break;
+ case 4: il.Emit(OpCodes.Ldc_I4_4); break;
+ case 5: il.Emit(OpCodes.Ldc_I4_5); break;
+ case 6: il.Emit(OpCodes.Ldc_I4_6); break;
+ case 7: il.Emit(OpCodes.Ldc_I4_7); break;
+ case 8: il.Emit(OpCodes.Ldc_I4_8); break;
+ default:
+ if (value >= -128 && value <= 127)
+ {
+ il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
+ }
+ else
+ {
+ il.Emit(OpCodes.Ldc_I4, value);
+ }
+ break;
+ }
+ }
+
+
+ ///
+ /// Key used to indicate the type name associated with a DataTable
+ ///
+ private const string DataTableTypeNameKey = "dapper:TypeName";
+
+ ///
+ /// How should connection strings be compared for equivalence? Defaults to StringComparer.Ordinal.
+ /// Providing a custom implementation can be useful for allowing multi-tenancy databases with identical
+ /// schema to share startegies. Note that usual equivalence rules apply: any equivalent connection strings
+ /// MUST yield the same hash-code.
+ ///
+ public static IEqualityComparer ConnectionStringComparer
+ {
+ get { return connectionStringComparer; }
+ set { connectionStringComparer = value ?? StringComparer.Ordinal; }
+ }
+ private static IEqualityComparer connectionStringComparer = StringComparer.Ordinal;
+
+
+ ///
+ /// The grid reader provides interfaces for reading multiple result sets from a Dapper query
+ ///
+ public partial class GridReader : IDisposable
+ {
+ private IDataReader reader;
+ private IDbCommand command;
+ private Identity identity;
+
+ internal GridReader(IDbCommand command, IDataReader reader, Identity identity, SqlMapper.IParameterCallbacks callbacks)
+ {
+ this.command = command;
+ this.reader = reader;
+ this.identity = identity;
+ this.callbacks = callbacks;
+ }
+
+#if !CSHARP30
+
+ ///
+ /// Read the next grid of results, returned as a dynamic object
+ ///
+ public IEnumerable Read(bool buffered = true)
+ {
+ return ReadImpl(typeof(DapperRow), buffered);
+ }
+#endif
+
+#if CSHARP30
+ ///
+ /// Read the next grid of results
+ ///
+ public IEnumerable Read()
+ {
+ return Read(true);
+ }
+#endif
+ ///
+ /// Read the next grid of results
+ ///
+#if CSHARP30
+ public IEnumerable Read(bool buffered)
+#else
+ public IEnumerable Read(bool buffered = true)
+#endif
+ {
+ return ReadImpl(typeof(T), buffered);
+ }
+
+ ///
+ /// Read the next grid of results
+ ///
+#if CSHARP30
+ public IEnumerable Read(Type type, bool buffered)
+#else
+ public IEnumerable Read(Type type, bool buffered = true)
+#endif
+ {
+ if (type == null) throw new ArgumentNullException("type");
+ return ReadImpl(type, buffered);
+ }
+
+ private IEnumerable ReadImpl(Type type, bool buffered)
+ {
+ if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
+ if (consumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
+ var typedIdentity = identity.ForGrid(type, gridIndex);
+ CacheInfo cache = GetCacheInfo(typedIdentity, null, true);
+ var deserializer = cache.Deserializer;
+
+ int hash = GetColumnHash(reader);
+ if (deserializer.Func == null || deserializer.Hash != hash)
+ {
+ deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
+ cache.Deserializer = deserializer;
+ }
+ consumed = true;
+ var result = ReadDeferred(gridIndex, deserializer.Func, typedIdentity);
+ return buffered ? result.ToList() : result;
+ }
+
+
+ private IEnumerable MultiReadInternal(Delegate func, string splitOn)
+ {
+ var identity = this.identity.ForGrid(typeof(TReturn), new Type[] {
+ typeof(TFirst),
+ typeof(TSecond),
+ typeof(TThird),
+ typeof(TFourth),
+ typeof(TFifth),
+ typeof(TSixth),
+ typeof(TSeventh)
+ }, gridIndex);
+ try
+ {
+ foreach (var r in SqlMapper.MultiMapImpl(null, default(CommandDefinition), func, splitOn, reader, identity, false))
+ {
+ yield return r;
+ }
+ }
+ finally
+ {
+ NextResult();
+ }
+ }
+
+#if CSHARP30
+ ///
+ /// Read multiple objects from a single recordset on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn)
+ {
+ return Read(func, splitOn, true);
+ }
+#endif
+ ///
+ /// Read multiple objects from a single recordset on the grid
+ ///
+#if CSHARP30
+ public IEnumerable Read(Func func, string splitOn, bool buffered)
+#else
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+#endif
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+
+#if CSHARP30
+ ///
+ /// Read multiple objects from a single recordset on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn)
+ {
+ return Read(func, splitOn, true);
+ }
+#endif
+ ///
+ /// Read multiple objects from a single recordset on the grid
+ ///
+#if CSHARP30
+ public IEnumerable Read(Func func, string splitOn, bool buffered)
+#else
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+#endif
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+
+#if CSHARP30
+ ///
+ /// Read multiple objects from a single record set on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn)
+ {
+ return Read(func, splitOn, true);
+ }
+#endif
+
+ ///
+ /// Read multiple objects from a single record set on the grid
+ ///
+#if CSHARP30
+ public IEnumerable Read(Func func, string splitOn, bool buffered)
+#else
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+#endif
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+
+
+
+#if !CSHARP30
+ ///
+ /// Read multiple objects from a single record set on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+ ///
+ /// Read multiple objects from a single record set on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+ ///
+ /// Read multiple objects from a single record set on the grid
+ ///
+ public IEnumerable Read(Func func, string splitOn = "id", bool buffered = true)
+ {
+ var result = MultiReadInternal(func, splitOn);
+ return buffered ? result.ToList() : result;
+ }
+#endif
+
+ private IEnumerable ReadDeferred(int index, Func deserializer, Identity typedIdentity)
+ {
+ try
+ {
+ while (index == gridIndex && reader.Read())
+ {
+ yield return (T)deserializer(reader);
+ }
+ }
+ finally // finally so that First etc progresses things even when multiple rows
+ {
+ if (index == gridIndex)
+ {
+ NextResult();
+ }
+ }
+ }
+ private int gridIndex, readCount;
+ private bool consumed;
+ private SqlMapper.IParameterCallbacks callbacks;
+
+ ///
+ /// Has the underlying reader been consumed?
+ ///
+ public bool IsConsumed
+ {
+ get
+ {
+ return consumed;
+ }
+ }
+ private void NextResult()
+ {
+ if (reader.NextResult())
+ {
+ readCount++;
+ gridIndex++;
+ consumed = false;
+ }
+ else
+ {
+ // happy path; close the reader cleanly - no
+ // need for "Cancel" etc
+ reader.Dispose();
+ reader = null;
+ if (callbacks != null) callbacks.OnCompleted();
+ Dispose();
+ }
+ }
+ ///
+ /// Dispose the grid, closing and disposing both the underlying reader and command.
+ ///
+ public void Dispose()
+ {
+ if (reader != null)
+ {
+ if (!reader.IsClosed && command != null) command.Cancel();
+ reader.Dispose();
+ reader = null;
+ }
+ if (command != null)
+ {
+ command.Dispose();
+ command = null;
+ }
+ }
+ }
+
+ ///
+ /// Used to pass a DataTable as a TableValuedParameter
+ ///
+ public static ICustomQueryParameter AsTableValuedParameter(this DataTable table, string typeName
+#if !CSHARP30
+ = null
+#endif
+ )
+ {
+ return new TableValuedParameter(table, typeName);
+ }
+
+ ///
+ /// Associate a DataTable with a type name
+ ///
+ public static void SetTypeName(this DataTable table, string typeName)
+ {
+ if (table != null)
+ {
+ if (string.IsNullOrEmpty(typeName))
+ table.ExtendedProperties.Remove(DataTableTypeNameKey);
+ else
+ table.ExtendedProperties[DataTableTypeNameKey] = typeName;
+ }
+ }
+
+ ///
+ /// Fetch the type name associated with a DataTable
+ ///
+ public static string GetTypeName(this DataTable table)
+ {
+ return table == null ? null : table.ExtendedProperties[DataTableTypeNameKey] as string;
+ }
+ }
+
+ ///
+ /// A bag of parameters that can be passed to the Dapper Query and Execute methods
+ ///
+ partial class DynamicParameters : SqlMapper.IDynamicParameters, SqlMapper.IParameterLookup, SqlMapper.IParameterCallbacks
+ {
+ internal const DbType EnumerableMultiParameter = (DbType)(-1);
+ static Dictionary> paramReaderCache = new Dictionary>();
+
+ Dictionary parameters = new Dictionary();
+ List templates;
+
+ object SqlMapper.IParameterLookup.this[string member]
+ {
+ get
+ {
+ ParamInfo param;
+ return parameters.TryGetValue(member, out param) ? param.Value : null;
+ }
+ }
+
+ partial class ParamInfo
+ {
+ public string Name { get; set; }
+ public object Value { get; set; }
+ public ParameterDirection ParameterDirection { get; set; }
+ public DbType? DbType { get; set; }
+ public int? Size { get; set; }
+ public IDbDataParameter AttachedParam { get; set; }
+ internal Action OutputCallback { get; set; }
+ internal object OutputTarget { get; set; }
+ internal bool CameFromTemplate { get; set; }
+ }
+
+ ///
+ /// construct a dynamic parameter bag
+ ///
+ public DynamicParameters()
+ {
+ RemoveUnused = true;
+ }
+
+ ///
+ /// construct a dynamic parameter bag
+ ///
+ /// can be an anonymous type or a DynamicParameters bag
+ public DynamicParameters(object template)
+ {
+ RemoveUnused = true;
+ AddDynamicParams(template);
+ }
+
+ ///
+ /// Append a whole object full of params to the dynamic
+ /// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic
+ ///
+ ///
+ public void AddDynamicParams(
+#if CSHARP30
+object param
+#else
+dynamic param
+#endif
+)
+ {
+ var obj = param as object;
+ if (obj != null)
+ {
+ var subDynamic = obj as DynamicParameters;
+ if (subDynamic == null)
+ {
+ var dictionary = obj as IEnumerable>;
+ if (dictionary == null)
+ {
+ templates = templates ?? new List();
+ templates.Add(obj);
+ }
+ else
+ {
+ foreach (var kvp in dictionary)
+ {
+#if CSHARP30
+ Add(kvp.Key, kvp.Value, null, null, null);
+#else
+ Add(kvp.Key, kvp.Value);
+#endif
+ }
+ }
+ }
+ else
+ {
+ if (subDynamic.parameters != null)
+ {
+ foreach (var kvp in subDynamic.parameters)
+ {
+ parameters.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ if (subDynamic.templates != null)
+ {
+ templates = templates ?? new List();
+ foreach (var t in subDynamic.templates)
+ {
+ templates.Add(t);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Add a parameter to this dynamic parameter list
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Add(
+#if CSHARP30
+string name, object value, DbType? dbType, ParameterDirection? direction, int? size
+#else
+string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null
+#endif
+)
+ {
+ parameters[Clean(name)] = new ParamInfo() { Name = name, Value = value, ParameterDirection = direction ?? ParameterDirection.Input, DbType = dbType, Size = size };
+ }
+
+ static string Clean(string name)
+ {
+ if (!string.IsNullOrEmpty(name))
+ {
+ switch (name[0])
+ {
+ case '@':
+ case ':':
+ case '?':
+ return name.Substring(1);
+ }
+ }
+ return name;
+ }
+
+ void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
+ {
+ AddParameters(command, identity);
+ }
+
+ ///
+ /// If true, the command-text is inspected and only values that are clearly used are included on the connection
+ ///
+ public bool RemoveUnused { get; set; }
+
+ ///
+ /// Add all the parameters needed to the command just before it executes
+ ///
+ /// The raw command prior to execution
+ /// Information about the query
+ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
+ {
+ var literals = SqlMapper.GetLiteralTokens(identity.sql);
+
+ if (templates != null)
+ {
+ foreach (var template in templates)
+ {
+ var newIdent = identity.ForDynamicParameters(template.GetType());
+ Action appender;
+
+ lock (paramReaderCache)
+ {
+ if (!paramReaderCache.TryGetValue(newIdent, out appender))
+ {
+ appender = SqlMapper.CreateParamInfoGenerator(newIdent, true, RemoveUnused, literals);
+ paramReaderCache[newIdent] = appender;
+ }
+ }
+
+ appender(command, template);
+ }
+
+ // The parameters were added to the command, but not the
+ // DynamicParameters until now.
+ foreach (IDbDataParameter param in command.Parameters)
+ {
+ // If someone makes a DynamicParameters with a template,
+ // then explicitly adds a parameter of a matching name,
+ // it will already exist in 'parameters'.
+ if (!parameters.ContainsKey(param.ParameterName))
+ {
+ parameters.Add(param.ParameterName, new ParamInfo
+ {
+ AttachedParam = param,
+ CameFromTemplate = true,
+ DbType = param.DbType,
+ Name = param.ParameterName,
+ ParameterDirection = param.Direction,
+ Size = param.Size,
+ Value = param.Value
+ });
+ }
+ }
+
+ // Now that the parameters are added to the command, let's place our output callbacks
+ var tmp = outputCallbacks;
+ if (tmp != null)
+ {
+ foreach (var generator in tmp)
+ {
+ generator();
+ }
+ }
+ }
+
+ foreach (var param in parameters.Values)
+ {
+ if (param.CameFromTemplate) continue;
+
+ var dbType = param.DbType;
+ var val = param.Value;
+ string name = Clean(param.Name);
+ var isCustomQueryParameter = val is SqlMapper.ICustomQueryParameter;
+
+ SqlMapper.ITypeHandler handler = null;
+ if (dbType == null && val != null && !isCustomQueryParameter) dbType = SqlMapper.LookupDbType(val.GetType(), name, out handler);
+ if (dbType == DynamicParameters.EnumerableMultiParameter)
+ {
+#pragma warning disable 612, 618
+ SqlMapper.PackListParameters(command, name, val);
+#pragma warning restore 612, 618
+ }
+ else if (isCustomQueryParameter)
+ {
+ ((SqlMapper.ICustomQueryParameter)val).AddParameter(command, name);
+ }
+ else
+ {
+
+ bool add = !command.Parameters.Contains(name);
+ IDbDataParameter p;
+ if (add)
+ {
+ p = command.CreateParameter();
+ p.ParameterName = name;
+ }
+ else
+ {
+ p = (IDbDataParameter)command.Parameters[name];
+ }
+
+ p.Direction = param.ParameterDirection;
+ if (handler == null)
+ {
+ p.Value = val ?? DBNull.Value;
+ if (dbType != null && p.DbType != dbType)
+ {
+ p.DbType = dbType.Value;
+ }
+ var s = val as string;
+ if (s != null)
+ {
+ if (s.Length <= DbString.DefaultLength)
+ {
+ p.Size = DbString.DefaultLength;
+ }
+ }
+ if (param.Size != null)
+ {
+ p.Size = param.Size.Value;
+ }
+ }
+ else
+ {
+ if (dbType != null) p.DbType = dbType.Value;
+ if (param.Size != null) p.Size = param.Size.Value;
+ handler.SetValue(p, val ?? DBNull.Value);
+ }
+
+ if (add)
+ {
+ command.Parameters.Add(p);
+ }
+ param.AttachedParam = p;
+ }
+ }
+
+ // note: most non-priveleged implementations would use: this.ReplaceLiterals(command);
+ if(literals.Count != 0) SqlMapper.ReplaceLiterals(this, command, literals);
+ }
+
+ ///
+ /// All the names of the param in the bag, use Get to yank them out
+ ///
+ public IEnumerable ParameterNames
+ {
+ get
+ {
+ return parameters.Select(p => p.Key);
+ }
+ }
+
+
+ ///
+ /// Get the value of a parameter
+ ///
+ ///
+ ///
+ /// The value, note DBNull.Value is not returned, instead the value is returned as null
+ public T Get(string name)
+ {
+ var val = parameters[Clean(name)].AttachedParam.Value;
+ if (val == DBNull.Value)
+ {
+ if (default(T) != null)
+ {
+ throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!");
+ }
+ return default(T);
+ }
+ return (T)val;
+ }
+
+ ///
+ /// Allows you to automatically populate a target property/field from output parameters. It actually
+ /// creates an InputOutput parameter, so you can still pass data in.
+ ///
+ ///
+ /// The object whose property/field you wish to populate.
+ /// A MemberExpression targeting a property/field of the target (or descendant thereof.)
+ ///
+ /// The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.
+ /// The DynamicParameters instance
+#if CSHARP30
+ public DynamicParameters Output(T target, Expression> expression, DbType? dbType, int? size)
+#else
+ public DynamicParameters Output(T target, Expression> expression, DbType? dbType = null, int? size = null)
+#endif
+ {
+ var failMessage = "Expression must be a property/field chain off of a(n) {0} instance";
+ failMessage = string.Format(failMessage, typeof(T).Name);
+ Action @throw = () => { throw new InvalidOperationException(failMessage); };
+
+ // Is it even a MemberExpression?
+ var lastMemberAccess = expression.Body as MemberExpression;
+
+ if (lastMemberAccess == null ||
+ (lastMemberAccess.Member.MemberType != MemberTypes.Property &&
+ lastMemberAccess.Member.MemberType != MemberTypes.Field))
+ {
+ if (expression.Body.NodeType == ExpressionType.Convert &&
+ expression.Body.Type == typeof(object) &&
+ ((UnaryExpression)expression.Body).Operand is MemberExpression)
+ {
+ // It's got to be unboxed
+ lastMemberAccess = (MemberExpression)((UnaryExpression)expression.Body).Operand;
+ }
+ else @throw();
+ }
+
+ // Does the chain consist of MemberExpressions leading to a ParameterExpression of type T?
+ MemberExpression diving = lastMemberAccess;
+ ParameterExpression constant = null;
+ // Retain a list of member names and the member expressions so we can rebuild the chain.
+ List names = new List();
+ List chain = new List();
+
+ do
+ {
+ // Insert the names in the right order so expression
+ // "Post.Author.Name" becomes parameter "PostAuthorName"
+ names.Insert(0, diving.Member.Name);
+ chain.Insert(0, diving);
+
+ constant = diving.Expression as ParameterExpression;
+ diving = diving.Expression as MemberExpression;
+
+ if (constant != null &&
+ constant.Type == typeof(T))
+ {
+ break;
+ }
+ else if (diving == null ||
+ (diving.Member.MemberType != MemberTypes.Property &&
+ diving.Member.MemberType != MemberTypes.Field))
+ {
+ @throw();
+ }
+ }
+ while (diving != null);
+
+ var dynamicParamName = string.Join(string.Empty, names.ToArray());
+
+ // Before we get all emitty...
+ var lookup = string.Join("|", names.ToArray());
+
+ var cache = CachedOutputSetters.Cache;
+ var setter = (Action)cache[lookup];
+
+ if (setter != null) goto MAKECALLBACK;
+
+ // Come on let's build a method, let's build it, let's build it now!
+ var dm = new DynamicMethod(string.Format("ExpressionParam{0}", Guid.NewGuid()), null, new[] { typeof(object), this.GetType() }, true);
+ var il = dm.GetILGenerator();
+
+ il.Emit(OpCodes.Ldarg_0); // [object]
+ il.Emit(OpCodes.Castclass, typeof(T)); // [T]
+
+ // Count - 1 to skip the last member access
+ var i = 0;
+ for (; i < (chain.Count - 1); i++)
+ {
+ var member = chain[0].Member;
+
+ if (member.MemberType == MemberTypes.Property)
+ {
+ var get = ((PropertyInfo)member).GetGetMethod(true);
+ il.Emit(OpCodes.Callvirt, get); // [Member{i}]
+ }
+ else // Else it must be a field!
+ {
+ il.Emit(OpCodes.Ldfld, ((FieldInfo)member)); // [Member{i}]
+ }
+ }
+
+ var paramGetter = this.GetType().GetMethod("Get", new Type[] { typeof(string) }).MakeGenericMethod(lastMemberAccess.Type);
+
+ il.Emit(OpCodes.Ldarg_1); // [target] [DynamicParameters]
+ il.Emit(OpCodes.Ldstr, dynamicParamName); // [target] [DynamicParameters] [ParamName]
+ il.Emit(OpCodes.Callvirt, paramGetter); // [target] [value], it's already typed thanks to generic method
+
+ // GET READY
+ var lastMember = lastMemberAccess.Member;
+ if (lastMember.MemberType == MemberTypes.Property)
+ {
+ var set = ((PropertyInfo)lastMember).GetSetMethod(true);
+ il.Emit(OpCodes.Callvirt, set); // SET
+ }
+ else
+ {
+ il.Emit(OpCodes.Stfld, ((FieldInfo)lastMember)); // SET
+ }
+
+ il.Emit(OpCodes.Ret); // GO
+
+ setter = (Action)dm.CreateDelegate(typeof(Action));
+ lock (cache)
+ {
+ cache[lookup] = setter;
+ }
+
+ // Queue the preparation to be fired off when adding parameters to the DbCommand
+ MAKECALLBACK:
+ (outputCallbacks ?? (outputCallbacks = new List())).Add(() =>
+ {
+ // Finally, prep the parameter and attach the callback to it
+ ParamInfo parameter;
+ var targetMemberType = lastMemberAccess.Type;
+ int sizeToSet = (!size.HasValue && targetMemberType == typeof(string)) ? DbString.DefaultLength : size ?? 0;
+
+ if (this.parameters.TryGetValue(dynamicParamName, out parameter))
+ {
+ parameter.ParameterDirection = parameter.AttachedParam.Direction = ParameterDirection.InputOutput;
+
+ if (parameter.AttachedParam.Size == 0)
+ {
+ parameter.Size = parameter.AttachedParam.Size = sizeToSet;
+ }
+ }
+ else
+ {
+ SqlMapper.ITypeHandler handler;
+ dbType = (!dbType.HasValue) ? SqlMapper.LookupDbType(targetMemberType, targetMemberType.Name, out handler) : dbType;
+
+ // CameFromTemplate property would not apply here because this new param
+ // Still needs to be added to the command
+ this.Add(dynamicParamName, expression.Compile().Invoke(target), null, ParameterDirection.InputOutput, sizeToSet);
+ }
+
+ parameter = this.parameters[dynamicParamName];
+ parameter.OutputCallback = setter;
+ parameter.OutputTarget = target;
+ });
+
+ return this;
+ }
+
+ private List outputCallbacks;
+
+ private readonly Dictionary> cachedOutputSetters = new Dictionary>();
+
+ internal static class CachedOutputSetters
+ {
+ public static readonly Hashtable Cache = new Hashtable();
+ }
+
+ void SqlMapper.IParameterCallbacks.OnCompleted()
+ {
+ foreach (var param in (from p in parameters select p.Value))
+ {
+ if (param.OutputCallback != null) param.OutputCallback(param.OutputTarget, this);
+ }
+ }
+ }
+
+ sealed class DataTableHandler : Dapper.SqlMapper.ITypeHandler
+ {
+ public object Parse(Type destinationType, object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetValue(IDbDataParameter parameter, object value)
+ {
+ TableValuedParameter.Set(parameter, value as DataTable, null);
+ }
+ }
+
+ ///
+ /// Used to pass a DataTable as a TableValuedParameter
+ ///
+ sealed partial class TableValuedParameter : Dapper.SqlMapper.ICustomQueryParameter
+ {
+ private readonly DataTable table;
+ private readonly string typeName;
+
+ ///
+ /// Create a new instance of TableValuedParameter
+ ///
+ public TableValuedParameter(DataTable table) : this(table, null) { }
+ ///
+ /// Create a new instance of TableValuedParameter
+ ///
+ public TableValuedParameter(DataTable table, string typeName)
+ {
+ this.table = table;
+ this.typeName = typeName;
+ }
+ static readonly Action setTypeName;
+ static TableValuedParameter()
+ {
+ var prop = typeof(System.Data.SqlClient.SqlParameter).GetProperty("TypeName", BindingFlags.Instance | BindingFlags.Public);
+ if(prop != null && prop.PropertyType == typeof(string) && prop.CanWrite)
+ {
+ setTypeName = (Action)
+ Delegate.CreateDelegate(typeof(Action), prop.GetSetMethod());
+ }
+ }
+ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
+ {
+ var param = command.CreateParameter();
+ param.ParameterName = name;
+ Set(param, table, typeName);
+ command.Parameters.Add(param);
+ }
+ internal static void Set(IDbDataParameter parameter, DataTable table, string typeName)
+ {
+ parameter.Value = (object)table ?? DBNull.Value;
+ if (string.IsNullOrEmpty(typeName) && table != null)
+ {
+ typeName = SqlMapper.GetTypeName(table);
+ }
+ if (!string.IsNullOrEmpty(typeName))
+ {
+ var sqlParam = parameter as System.Data.SqlClient.SqlParameter;
+ if (sqlParam != null)
+ {
+ if (setTypeName != null) setTypeName(sqlParam, typeName);
+ sqlParam.SqlDbType = SqlDbType.Structured;
+ }
+ }
+ }
+ }
+ ///
+ /// This class represents a SQL string, it can be used if you need to denote your parameter is a Char vs VarChar vs nVarChar vs nChar
+ ///
+ sealed partial class DbString : Dapper.SqlMapper.ICustomQueryParameter
+ {
+ ///
+ /// A value to set the default value of strings
+ /// going through Dapper. Default is 4000, any value larger than this
+ /// field will not have the default value applied.
+ ///
+ public const int DefaultLength = 4000;
+
+ ///
+ /// Create a new DbString
+ ///
+ public DbString() { Length = -1; }
+ ///
+ /// Ansi vs Unicode
+ ///
+ public bool IsAnsi { get; set; }
+ ///
+ /// Fixed length
+ ///
+ public bool IsFixedLength { get; set; }
+ ///
+ /// Length of the string -1 for max
+ ///
+ public int Length { get; set; }
+ ///
+ /// The value of the string
+ ///
+ public string Value { get; set; }
+ ///
+ /// Add the parameter to the command... internal use only
+ ///
+ ///
+ ///
+ public void AddParameter(IDbCommand command, string name)
+ {
+ if (IsFixedLength && Length == -1)
+ {
+ throw new InvalidOperationException("If specifying IsFixedLength, a Length must also be specified");
+ }
+ var param = command.CreateParameter();
+ param.ParameterName = name;
+ param.Value = (object)Value ?? DBNull.Value;
+ if (Length == -1 && Value != null && Value.Length <= DefaultLength)
+ {
+ param.Size = DefaultLength;
+ }
+ else
+ {
+ param.Size = Length;
+ }
+ param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String);
+ command.Parameters.Add(param);
+ }
+ }
+
+ ///
+ /// Handles variances in features per DBMS
+ ///
+ partial class FeatureSupport
+ {
+ private static readonly FeatureSupport
+ @default = new FeatureSupport(false),
+ postgres = new FeatureSupport(true);
+
+ ///
+ /// Gets the featureset based on the passed connection
+ ///
+ public static FeatureSupport Get(IDbConnection connection)
+ {
+ string name = connection == null ? null : connection.GetType().Name;
+ if (string.Equals(name, "npgsqlconnection", StringComparison.InvariantCultureIgnoreCase)) return postgres;
+ return @default;
+ }
+ private FeatureSupport(bool arrays)
+ {
+ Arrays = arrays;
+ }
+ ///
+ /// True if the db supports array columns e.g. Postgresql
+ ///
+ public bool Arrays { get; private set; }
+ }
+
+ ///
+ /// Represents simple memeber map for one of target parameter or property or field to source DataReader column
+ ///
+ sealed partial class SimpleMemberMap : SqlMapper.IMemberMap
+ {
+ private readonly string _columnName;
+ private readonly PropertyInfo _property;
+ private readonly FieldInfo _field;
+ private readonly ParameterInfo _parameter;
+
+ ///
+ /// Creates instance for simple property mapping
+ ///
+ /// DataReader column name
+ /// Target property
+ public SimpleMemberMap(string columnName, PropertyInfo property)
+ {
+ if (columnName == null)
+ throw new ArgumentNullException("columnName");
+
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ _columnName = columnName;
+ _property = property;
+ }
+
+ ///
+ /// Creates instance for simple field mapping
+ ///
+ /// DataReader column name
+ /// Target property
+ public SimpleMemberMap(string columnName, FieldInfo field)
+ {
+ if (columnName == null)
+ throw new ArgumentNullException("columnName");
+
+ if (field == null)
+ throw new ArgumentNullException("field");
+
+ _columnName = columnName;
+ _field = field;
+ }
+
+ ///
+ /// Creates instance for simple constructor parameter mapping
+ ///
+ /// DataReader column name
+ /// Target constructor parameter
+ public SimpleMemberMap(string columnName, ParameterInfo parameter)
+ {
+ if (columnName == null)
+ throw new ArgumentNullException("columnName");
+
+ if (parameter == null)
+ throw new ArgumentNullException("parameter");
+
+ _columnName = columnName;
+ _parameter = parameter;
+ }
+
+ ///
+ /// DataReader column name
+ ///
+ public string ColumnName
+ {
+ get { return _columnName; }
+ }
+
+ ///
+ /// Target member type
+ ///
+ public Type MemberType
+ {
+ get
+ {
+ if (_field != null)
+ return _field.FieldType;
+
+ if (_property != null)
+ return _property.PropertyType;
+
+ if (_parameter != null)
+ return _parameter.ParameterType;
+
+ return null;
+ }
+ }
+
+ ///
+ /// Target property
+ ///
+ public PropertyInfo Property
+ {
+ get { return _property; }
+ }
+
+ ///
+ /// Target field
+ ///
+ public FieldInfo Field
+ {
+ get { return _field; }
+ }
+
+ ///
+ /// Target constructor parameter
+ ///
+ public ParameterInfo Parameter
+ {
+ get { return _parameter; }
+ }
+ }
+
+ ///
+ /// Represents default type mapping strategy used by Dapper
+ ///
+ sealed partial class DefaultTypeMap : SqlMapper.ITypeMap
+ {
+ private readonly List _fields;
+ private readonly List _properties;
+ private readonly Type _type;
+
+ ///
+ /// Creates default type map
+ ///
+ /// Entity type
+ public DefaultTypeMap(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ _fields = GetSettableFields(type);
+ _properties = GetSettableProps(type);
+ _type = type;
+ }
+
+ internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type)
+ {
+ return propertyInfo.DeclaringType == type ?
+ propertyInfo.GetSetMethod(true) :
+ propertyInfo.DeclaringType.GetProperty(
+ propertyInfo.Name,
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
+ Type.DefaultBinder,
+ propertyInfo.PropertyType,
+ propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(),
+ null).GetSetMethod(true);
+ }
+
+ internal static List GetSettableProps(Type t)
+ {
+ return t
+ .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(p => GetPropertySetter(p, t) != null)
+ .ToList();
+ }
+
+ internal static List GetSettableFields(Type t)
+ {
+ return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList();
+ }
+
+ ///
+ /// Finds best constructor
+ ///
+ /// DataReader column names
+ /// DataReader column types
+ /// Matching constructor or default one
+ public ConstructorInfo FindConstructor(string[] names, Type[] types)
+ {
+ var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))
+ {
+ ParameterInfo[] ctorParameters = ctor.GetParameters();
+ if (ctorParameters.Length == 0)
+ return ctor;
+
+ if (ctorParameters.Length != types.Length)
+ continue;
+
+ int i = 0;
+ for (; i < ctorParameters.Length; i++)
+ {
+ if (!String.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase))
+ break;
+ if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary)
+ continue;
+ var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;
+ if (unboxedType != types[i]
+ && !(unboxedType.IsEnum && Enum.GetUnderlyingType(unboxedType) == types[i])
+ && !(unboxedType == typeof(char) && types[i] == typeof(string)))
+ break;
+ }
+
+ if (i == ctorParameters.Length)
+ return ctor;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets mapping for constructor parameter
+ ///
+ /// Constructor to resolve
+ /// DataReader column name
+ /// Mapping implementation
+ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
+ {
+ var parameters = constructor.GetParameters();
+
+ return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)));
+ }
+
+ ///
+ /// Gets member mapping for column
+ ///
+ /// DataReader column name
+ /// Mapping implementation
+ public SqlMapper.IMemberMap GetMember(string columnName)
+ {
+ var property = _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
+ ?? _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
+
+ if (property == null && MatchNamesWithUnderscores)
+ {
+ property = _properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
+ ?? _properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
+ }
+
+ if (property != null)
+ return new SimpleMemberMap(columnName, property);
+
+ var field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
+
+ if (field == null && MatchNamesWithUnderscores)
+ {
+ field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
+ ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
+ }
+
+ if (field != null)
+ return new SimpleMemberMap(columnName, field);
+
+ return null;
+ }
+ ///
+ /// Should column names like User_Id be allowed to match properties/fields like UserId ?
+ ///
+ public static bool MatchNamesWithUnderscores { get; set; }
+ }
+
+
+
+ ///
+ /// Implements custom property mapping by user provided criteria (usually presence of some custom attribute with column to member mapping)
+ ///
+ sealed partial class CustomPropertyTypeMap : SqlMapper.ITypeMap
+ {
+ private readonly Type _type;
+ private readonly Func _propertySelector;
+
+ ///
+ /// Creates custom property mapping
+ ///
+ /// Target entity type
+ /// Property selector based on target type and DataReader column name
+ public CustomPropertyTypeMap(Type type, Func propertySelector)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ if (propertySelector == null)
+ throw new ArgumentNullException("propertySelector");
+
+ _type = type;
+ _propertySelector = propertySelector;
+ }
+
+ ///
+ /// Always returns default constructor
+ ///
+ /// DataReader column names
+ /// DataReader column types
+ /// Default constructor
+ public ConstructorInfo FindConstructor(string[] names, Type[] types)
+ {
+ return _type.GetConstructor(new Type[0]);
+ }
+
+ ///
+ /// Not impelmeneted as far as default constructor used for all cases
+ ///
+ ///
+ ///
+ ///
+ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Returns property based on selector strategy
+ ///
+ /// DataReader column name
+ /// Poperty member map
+ public SqlMapper.IMemberMap GetMember(string columnName)
+ {
+ var prop = _propertySelector(_type, columnName);
+ return prop != null ? new SimpleMemberMap(columnName, prop) : null;
+ }
+ }
+
+ // Define DAPPER_MAKE_PRIVATE if you reference Dapper by source
+ // and you like to make the Dapper types private (in order to avoid
+ // conflicts with other projects that also reference Dapper by source)
+#if !DAPPER_MAKE_PRIVATE
+
+ public partial class SqlMapper
+ {
+ }
+
+ public partial class DynamicParameters
+ {
+
+ }
+
+ public partial class DbString
+ {
+
+ }
+
+
+ public partial class SimpleMemberMap
+ {
+
+ }
+
+ public partial class DefaultTypeMap
+ {
+
+ }
+
+ public partial class CustomPropertyTypeMap
+ {
+
+ }
+
+ public partial class FeatureSupport
+ {
+
+ }
+
+#endif
+
+}
diff --git a/src/dapper-dot-net/Dapper NET45/Dapper NET45.csproj b/src/dapper-dot-net/Dapper NET45/Dapper NET45.csproj
new file mode 100644
index 0000000..b11a735
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET45/Dapper NET45.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {0FFF5BC7-0A4B-4D87-835E-4FAD70937507}
+ Library
+ Properties
+ Dapper
+ Dapper
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ TRACE;DEBUG;ASYNC
+ prompt
+ 4
+ true
+ bin\Debug\Dapper.xml
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;ASYNC
+ prompt
+ 4
+ bin\Release\Dapper.xml
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+ AssemblyInfo.cs
+
+
+ SqlMapper.cs
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/dapper-dot-net/Dapper NET45/Properties/AssemblyInfo.cs b/src/dapper-dot-net/Dapper NET45/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..fe789b3
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET45/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Dapper NET45")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Dapper NET45")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ec9ad659-1358-4d01-be77-ce45cd40b2f8")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/dapper-dot-net/Dapper NET45/SqlMapperAsync.cs b/src/dapper-dot-net/Dapper NET45/SqlMapperAsync.cs
new file mode 100644
index 0000000..c57b44c
--- /dev/null
+++ b/src/dapper-dot-net/Dapper NET45/SqlMapperAsync.cs
@@ -0,0 +1,827 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Dapper
+{
+
+
+ public static partial class SqlMapper
+ {
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return QueryAsync(cnn, typeof(DapperRow), new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
+ }
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command)
+ {
+ return QueryAsync(cnn, typeof(DapperRow), command);
+ }
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return QueryAsync(cnn, typeof(T), new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
+ }
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, Type type, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
+ {
+ if (type == null) throw new ArgumentNullException("type");
+ return QueryAsync(cnn, type, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
+ }
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command)
+ {
+ return QueryAsync(cnn, typeof(T), command);
+ }
+
+ ///
+ /// Execute a query asynchronously using .NET 4.5 Task.
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, Type type, CommandDefinition command)
+ {
+ return QueryAsync(cnn, type, command);
+ }
+
+ private static async Task> QueryAsync(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
+ {
+ object param = command.Parameters;
+ var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param == null ? null : param.GetType(), null);
+ var info = GetCacheInfo(identity, param, command.AddToCache);
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ var cancel = command.CancellationToken;
+ using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
+ {
+ DbDataReader reader = null;
+ try
+ {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(cancel).ConfigureAwait(false);
+ reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, cancel).ConfigureAwait(false);
+
+ 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));
+ if (command.AddToCache) SetQueryCache(identity, info);
+ }
+
+ var func = tuple.Func;
+
+ if (command.Buffered)
+ {
+ List buffer = new List();
+ while (await reader.ReadAsync(cancel).ConfigureAwait(false))
+ {
+ buffer.Add((T)func(reader));
+ }
+ while (await reader.NextResultAsync().ConfigureAwait(false)) { }
+ command.OnCompleted();
+ return buffer;
+ }
+ else
+ {
+ // can't use ReadAsync / cancellation; but this will have to do
+ wasClosed = false; // don't close if handing back an open reader; rely on the command-behavior
+ var deferred = ExecuteReaderSync(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 (wasClosed) cnn.Close();
+
+ }
+ }
+ }
+
+ ///
+ /// Execute a command asynchronously using .NET 4.5 Task.
+ ///
+ public static Task ExecuteAsync(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return ExecuteAsync(cnn, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
+ }
+
+ ///
+ /// Execute a command asynchronously using .NET 4.5 Task.
+ ///
+ public static Task ExecuteAsync(this IDbConnection cnn, CommandDefinition command)
+ {
+ object param = command.Parameters;
+ IEnumerable multiExec = param as IEnumerable;
+ if (multiExec != null && !(param is string))
+ {
+ return ExecuteMultiImplAsync(cnn, command, multiExec);
+ }
+ else
+ {
+ return ExecuteImplAsync(cnn, command, param);
+ }
+ }
+
+ private struct AsyncExecState
+ {
+ public readonly DbCommand Command;
+ public readonly Task Task;
+ public AsyncExecState(DbCommand command, Task task)
+ {
+ this.Command = command;
+ this.Task = task;
+ }
+ }
+ private static async Task ExecuteMultiImplAsync(IDbConnection cnn, CommandDefinition command, IEnumerable multiExec)
+ {
+ bool isFirst = true;
+ int total = 0;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
+
+ CacheInfo info = null;
+ string masterSql = null;
+ if ((command.Flags & CommandFlags.Pipelined) != 0)
+ {
+ const int MAX_PENDING = 100;
+ var pending = new Queue(MAX_PENDING);
+ DbCommand cmd = null;
+ try
+ {
+ foreach (var obj in multiExec)
+ {
+ if (isFirst)
+ {
+ isFirst = false;
+ cmd = (DbCommand)command.SetupCommand(cnn, null);
+ masterSql = cmd.CommandText;
+ var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
+ info = GetCacheInfo(identity, obj, command.AddToCache);
+ } else if(pending.Count >= MAX_PENDING)
+ {
+ var recycled = pending.Dequeue();
+ total += await recycled.Task.ConfigureAwait(false);
+ cmd = recycled.Command;
+ cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
+ cmd.Parameters.Clear(); // current code is Add-tastic
+ }
+ else
+ {
+ cmd = (DbCommand)command.SetupCommand(cnn, null);
+ }
+ info.ParamReader(cmd, obj);
+
+ var task = cmd.ExecuteNonQueryAsync(command.CancellationToken);
+ pending.Enqueue(new AsyncExecState(cmd, task));
+ cmd = null; // note the using in the finally: this avoids a double-dispose
+ }
+ while (pending.Count != 0)
+ {
+ var pair = pending.Dequeue();
+ using (pair.Command) { } // dispose commands
+ total += await pair.Task.ConfigureAwait(false);
+ }
+ } finally
+ {
+ // this only has interesting work to do if there are failures
+ using (cmd) { } // dispose commands
+ while (pending.Count != 0)
+ { // dispose tasks even in failure
+ using (pending.Dequeue().Command) { } // dispose commands
+ }
+ }
+ }
+ else
+ {
+ using (var cmd = (DbCommand)command.SetupCommand(cnn, null))
+ {
+ foreach (var obj in multiExec)
+ {
+ if (isFirst)
+ {
+ masterSql = cmd.CommandText;
+ isFirst = false;
+ var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
+ info = GetCacheInfo(identity, obj, command.AddToCache);
+ }
+ 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 += await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+
+ command.OnCompleted();
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ }
+ return total;
+ }
+ private static async Task ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, object param)
+ {
+ var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param == null ? null : param.GetType(), null);
+ var info = GetCacheInfo(identity, param, command.AddToCache);
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
+ {
+ try
+ {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
+ var result = await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false);
+ command.OnCompleted();
+ return result;
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ }
+ }
+ }
+
+ ///
+ /// Maps a query to objects
+ ///
+ /// The first type in the recordset
+ /// The second type in the recordset
+ /// The return type
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The field we should split and read the second object from (default: id)
+ /// Number of seconds before command execution timeout
+ /// Is it a stored proc or a batch?
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Maps a query to objects
+ ///
+ /// The first type in the recordset
+ /// The second type in the recordset
+ /// The return type
+ ///
+ /// The field we should split and read the second object from (default: id)
+ /// The command to execute
+ ///
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ ///
+ /// Maps a query to objects
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The Field we should split and read the second object from (default: id)
+ /// Number of seconds before command execution timeout
+ ///
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Maps a query to objects
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The field we should split and read the second object from (default: id)
+ /// The command to execute
+ ///
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 4 input parameters
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 4 input parameters
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The field we should split and read the second object from (default: id)
+ /// The command to execute
+ ///
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 5 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 5 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 6 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 6 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 7 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ return MultiMapAsync(cnn,
+ new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
+ }
+
+ ///
+ /// Perform a multi mapping query with 7 input parameters
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, CommandDefinition command, Func map, string splitOn = "Id")
+ {
+ return MultiMapAsync(cnn, command, map, splitOn);
+ }
+
+ private static async Task> MultiMapAsync(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn)
+ {
+ object param = command.Parameters;
+ var identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(TFirst), param == null ? null : param.GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) });
+ var info = GetCacheInfo(identity, param, command.AddToCache);
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
+ using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
+ using (var reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false))
+ {
+ if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior
+ var results = MultiMapImpl(null, CommandDefinition.ForCallback(command.Parameters), map, splitOn, reader, identity, true);
+ return command.Buffered ? results.ToList() : results;
+ }
+ } finally
+ {
+ if (wasClosed) cnn.Close();
+ }
+ }
+
+ ///
+ /// Perform a multi mapping query with arbitrary input parameters
+ ///
+ /// The return type
+ ///
+ ///
+ /// array of types in the recordset
+ ///
+ ///
+ ///
+ ///
+ /// The Field we should split and read the second object from (default: id)
+ /// Number of seconds before command execution timeout
+ /// Is it a stored proc or a batch?
+ ///
+ public static Task> QueryAsync(this IDbConnection cnn, string sql, Type[] types, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
+ {
+ var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken));
+ return MultiMapAsync(cnn, command, types, map, splitOn);
+ }
+
+ private static async Task> MultiMapAsync(this IDbConnection cnn, CommandDefinition command, Type[] types, Func map, string splitOn)
+ {
+ if (types.Length < 1)
+ {
+ throw new ArgumentException("you must provide at least one type to deserialize");
+ }
+
+ object param = command.Parameters;
+ var identity = new Identity(command.CommandText, command.CommandType, cnn, types[0], param == null ? null : param.GetType(), types);
+ var info = GetCacheInfo(identity, param, command.AddToCache);
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync().ConfigureAwait(false);
+ using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
+ using (var reader = await cmd.ExecuteReaderAsync(command.CancellationToken).ConfigureAwait(false)) {
+ var results = MultiMapImpl(null, default(CommandDefinition), types, map, splitOn, reader, identity, true);
+ return command.Buffered ? results.ToList() : results;
+ }
+ }
+ finally {
+ if (wasClosed) cnn.Close();
+ }
+ }
+
+ private static IEnumerable ExecuteReaderSync(IDataReader reader, Func func, object parameters)
+ {
+ using (reader)
+ {
+ while (reader.Read())
+ {
+ yield return (T)func(reader);
+ }
+ while (reader.NextResult()) { }
+ if (parameters is SqlMapper.IParameterCallbacks)
+ ((SqlMapper.IParameterCallbacks)parameters).OnCompleted();
+ }
+ }
+
+ ///
+ /// Execute a command that returns multiple result sets, and access each in turn
+ ///
+ public static Task QueryMultipleAsync(
+#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 QueryMultipleAsync(cnn, command);
+ }
+
+ partial class GridReader
+ {
+ CancellationToken cancel;
+ internal GridReader(IDbCommand command, IDataReader reader, Identity identity, DynamicParameters dynamicParams, CancellationToken cancel) : this(command, reader, identity, dynamicParams)
+ {
+ this.cancel = cancel;
+ }
+
+ ///
+ /// Read the next grid of results, returned as a dynamic object
+ ///
+ public Task> ReadAsync(bool buffered = true)
+ {
+ return ReadAsyncImpl(typeof(DapperRow), buffered);
+ }
+
+ ///
+ /// Read the next grid of results
+ ///
+ public Task> ReadAsync(Type type, bool buffered = true)
+ {
+ if (type == null) throw new ArgumentNullException("type");
+ return ReadAsyncImpl(type, buffered);
+ }
+ ///
+ /// Read the next grid of results
+ ///
+ public Task> ReadAsync(bool buffered = true)
+ {
+ return ReadAsyncImpl(typeof(T), buffered);
+ }
+
+ private async Task NextResultAsync()
+ {
+ if (await ((DbDataReader)reader).NextResultAsync(cancel).ConfigureAwait(false))
+ {
+ readCount++;
+ gridIndex++;
+ consumed = false;
+ }
+ else
+ {
+ // happy path; close the reader cleanly - no
+ // need for "Cancel" etc
+ reader.Dispose();
+ reader = null;
+ if (callbacks != null) callbacks.OnCompleted();
+ Dispose();
+ }
+ }
+
+ private Task> ReadAsyncImpl(Type type, bool buffered)
+ {
+ if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
+ if (consumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
+ var typedIdentity = identity.ForGrid(type, gridIndex);
+ CacheInfo cache = GetCacheInfo(typedIdentity, null, true);
+ var deserializer = cache.Deserializer;
+
+ int hash = GetColumnHash(reader);
+ if (deserializer.Func == null || deserializer.Hash != hash)
+ {
+ deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
+ cache.Deserializer = deserializer;
+ }
+ consumed = true;
+ if (buffered && this.reader is DbDataReader)
+ {
+ return ReadBufferedAsync(gridIndex, deserializer.Func, typedIdentity);
+ }
+ else
+ {
+ var result = ReadDeferred(gridIndex, deserializer.Func, typedIdentity);
+ if (buffered) result = result.ToList(); // for the "not a DbDataReader" scenario
+ return Task.FromResult(result);
+ }
+ }
+
+ private async Task> ReadBufferedAsync(int index, Func deserializer, Identity typedIdentity)
+ {
+ //try
+ //{
+ var reader = (DbDataReader)this.reader;
+ List buffer = new List();
+ while (index == gridIndex && await reader.ReadAsync(cancel).ConfigureAwait(false))
+ {
+ buffer.Add((T)deserializer(reader));
+ }
+ if (index == gridIndex) // need to do this outside of the finally pre-C#6
+ {
+ await NextResultAsync().ConfigureAwait(false);
+ }
+ return buffer;
+ //}
+ //finally // finally so that First etc progresses things even when multiple rows
+ //{
+ // if (index == gridIndex)
+ // {
+ // await NextResultAsync().ConfigureAwait(false);
+ // }
+ //}
+ }
+ }
+
+ ///
+ /// Execute a command that returns multiple result sets, and access each in turn
+ ///
+ public static async Task QueryMultipleAsync(this IDbConnection cnn, CommandDefinition command)
+ {
+ object param = command.Parameters;
+ Identity identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(GridReader), param == null ? null : param.GetType(), null);
+ CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
+
+ DbCommand cmd = null;
+ IDataReader reader = null;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
+ cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader);
+ reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false);
+
+ var result = new GridReader(cmd, reader, identity, command.Parameters as DynamicParameters, command.CancellationToken);
+ wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
+ // with the CloseConnection flag, so the reader will deal with the connection; we
+ // still need something in the "finally" to ensure that broken SQL still results
+ // in the connection closing itself
+ return result;
+ }
+ catch
+ {
+ if (reader != null)
+ {
+ if (!reader.IsClosed)
+ try
+ { cmd.Cancel(); }
+ catch
+ { /* don't spoil the existing exception */ }
+ reader.Dispose();
+ }
+ if (cmd != null) cmd.Dispose();
+ if (wasClosed) cnn.Close();
+ throw;
+ }
+ }
+
+
+ ///
+ /// 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 Task ExecuteReaderAsync(
+#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 ExecuteReaderImplAsync(cnn, 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 Task ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command)
+ {
+ return ExecuteReaderImplAsync(cnn, command);
+ }
+
+ private static async Task ExecuteReaderImplAsync(IDbConnection cnn, CommandDefinition command)
+ {
+ Action paramReader = GetParameterReader(cnn, ref command);
+
+ DbCommand cmd = null;
+ bool wasClosed = cnn.State == ConnectionState.Closed;
+ try
+ {
+ cmd = (DbCommand)command.SetupCommand(cnn, paramReader);
+ if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
+ var reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false);
+ wasClosed = false;
+ return reader;
+ }
+ finally
+ {
+ if (wasClosed) cnn.Close();
+ if (cmd != null) cmd.Dispose();
+ }
+ }
+
+
+ ///
+ /// Execute parameterized SQL that selects a single value
+ ///
+ /// The first cell selected
+ public static Task ExecuteScalarAsync(
+#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 ExecuteScalarImplAsync(cnn, command);
+ }
+
+ ///
+ /// Execute parameterized SQL that selects a single value
+ ///
+ /// The first cell selected
+ public static Task ExecuteScalarAsync(
+#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 ExecuteScalarImplAsync