diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..f2a672928
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,80 @@
+# EditorConfig is awesome:http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Don't use tabs for indentation.
+[*]
+indent_style = space
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = true
+charset = utf-8-bom
+
+# Xml project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# Xml config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON files
+[*.json]
+indent_size = 2
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Suggest more modern language features when available
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+
+# CSharp code style settings:
+[*.cs]
+# Prefer "var" everywhere
+#csharp_style_var_for_built_in_types = true:suggestion
+#csharp_style_var_when_type_is_apparent = false:suggestion
+#csharp_style_var_elsewhere = true:suggestion
+
+# Prefer method-like constructs to have a expression-body
+csharp_style_expression_bodied_methods = true:none
+csharp_style_expression_bodied_constructors = true:none
+csharp_style_expression_bodied_operators = true:none
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Newline settings
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index 0a575e9a8..1cf0fcd08 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,6 @@
-*.doc diff=astextplain
+* text=auto
+
+*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
diff --git a/.gitignore b/.gitignore
index ea0f2334b..360431748 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
/*.suo
.vs/
+.vscode/
bin/
obj/
/*.user
@@ -8,5 +9,13 @@ _Resharper*
NuGet.exe
*.user
*.nupkg
+.nupkgs/
.docstats
-*.ide/
\ No newline at end of file
+*.ide/
+*.lock.json
+*.coverage
+Test.DB.*
+TestResults/
+Dapper.Tests/*.sdf
+Dapper.Tests/SqlServerTypes/
+.dotnet/*
\ No newline at end of file
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index e398d2c33..000000000
--- a/.hgignore
+++ /dev/null
@@ -1,11 +0,0 @@
-/*.suo
-bin/*
-obj/*
-/*.user
-glob:Dapper/NuGet.exe
-syntax: glob
-*.docstates
-glob:Dapper.Contrib/NuGet.exe
-*.nupkg
-*/NuGet.exe
-_ReSharper.*
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
index 4608df0ad..000000000
--- a/.hgtags
+++ /dev/null
@@ -1,6 +0,0 @@
-f4a04d92d07a1186974442dfeec1e9b44a221685 #nuget dapper release 1.5
-924b2d19b9b8b86457076a532daca84848c8b9ad #nuget dapper 1.6
-f4a04d92d07a1186974442dfeec1e9b44a221685 #nuget dapper release 1.5
-0000000000000000000000000000000000000000 #nuget dapper release 1.5
-924b2d19b9b8b86457076a532daca84848c8b9ad #nuget dapper 1.6
-0000000000000000000000000000000000000000 #nuget dapper 1.6
diff --git a/.nuget/packages.config b/.nuget/packages.config
deleted file mode 100644
index af17f43c1..000000000
--- a/.nuget/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/Dapper NET35 SNK/Dapper NET35 SNK.csproj b/Dapper NET35 SNK/Dapper NET35 SNK.csproj
deleted file mode 100644
index 1c1bd4927..000000000
--- a/Dapper NET35 SNK/Dapper NET35 SNK.csproj
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {02BA5431-EBE0-4E06-A01C-71C1A0D74379}
- 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
-
-
- true
-
-
- ..\Dapper.snk
-
-
-
-
-
-
-
-
-
- AssemblyInfo.cs
-
-
- SqlMapper.cs
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Dapper NET35/Dapper NET35.csproj b/Dapper NET35/Dapper NET35.csproj
deleted file mode 100644
index 8b031ad1c..000000000
--- a/Dapper NET35/Dapper NET35.csproj
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
- 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/Dapper NET40 SNK/Dapper NET40 SNK.csproj b/Dapper NET40 SNK/Dapper NET40 SNK.csproj
deleted file mode 100644
index 3c063af2f..000000000
--- a/Dapper NET40 SNK/Dapper NET40 SNK.csproj
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {437F12A2-6AB5-43BF-86BA-DD62DE17C2A7}
- 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
-
-
- true
-
-
- ..\Dapper.snk
-
-
-
-
-
-
-
-
-
-
- AssemblyInfo.cs
-
-
- SqlMapper.cs
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Dapper NET40/Dapper NET40.csproj b/Dapper NET40/Dapper NET40.csproj
deleted file mode 100644
index 184454708..000000000
--- a/Dapper NET40/Dapper NET40.csproj
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- 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/Dapper NET40/Properties/AssemblyInfo.cs b/Dapper NET40/Properties/AssemblyInfo.cs
deleted file mode 100644
index 6f1b77c19..000000000
--- a/Dapper NET40/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-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.40.0.0")]
-[assembly: AssemblyFileVersion("1.40.0.0")]
diff --git a/Dapper NET40/SqlMapper.cs b/Dapper NET40/SqlMapper.cs
deleted file mode 100644
index a7efe556c..000000000
--- a/Dapper NET40/SqlMapper.cs
+++ /dev/null
@@ -1,6144 +0,0 @@
-/*
- 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).
- */
-
-#if DNXCORE50
-using IDbDataParameter = global::System.Data.Common.DbParameter;
-using IDataParameter = global::System.Data.Common.DbParameter;
-using IDbTransaction = global::System.Data.Common.DbTransaction;
-using IDbConnection = global::System.Data.Common.DbConnection;
-using IDbCommand = global::System.Data.Common.DbCommand;
-using IDataReader = global::System.Data.Common.DbDataReader;
-using IDataRecord = global::System.Data.Common.DbDataReader;
-using IDataParameterCollection = global::System.Data.Common.DbParameterCollection;
-using DataException = global::System.InvalidOperationException;
-using ApplicationException = global::System.InvalidOperationException;
-#endif
-
-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;
-using System.Data.Common;
-
-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 additional 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 facilities 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);
- }
-#if !DNXCORE50
- ///
- /// 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 = SanitizeParameterValue(value);
- if (parameter is System.Data.SqlClient.SqlParameter)
- {
- ((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
- }
- }
- }
-#endif
-
- ///
- /// 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 members
- ///
- public interface ITypeMap
- {
- ///
- /// Finds best constructor
- ///
- /// DataReader column names
- /// DataReader column types
- /// Matching constructor or default one
- ConstructorInfo FindConstructor(string[] names, Type[] types);
-
- ///
- /// Returns a constructor which should *always* be used.
- ///
- /// Parameters will be default values, nulls for reference types and zero'd for value types.
- ///
- /// Use this class to force object creation away from parameterless constructors you don't control.
- ///
- ConstructorInfo FindExplicitConstructor();
-
- ///
- /// 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;
-#if !DNXCORE50
- AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), false);
-#endif
- }
-
- ///
- /// Clear the registered type handlers
- ///
- public static void ResetTypeHandlers()
- {
- typeHandlers = new Dictionary();
-#if !DNXCORE50
- AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), true);
-#endif
- }
- ///
- /// Configure 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;
- }
-
- ///
- /// Configure the specified type to be processed by a custom handler
- ///
- public static void AddTypeHandler(Type type, ITypeHandler handler)
- {
- AddTypeHandlerImpl(type, handler, true);
- }
-
- ///
- /// Configure 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;
- }
-
- ///
- /// Configure 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)]
-#if !DNXCORE50
- [Browsable(false)]
-#endif
- [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";
-
- ///
- /// Get the DbType that maps to a given value
- ///
- [Obsolete("This method is for internal use only")]
-#if !DNXCORE50
- [Browsable(false)]
-#endif
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static DbType GetDbType(object value)
- {
- if (value == null || value is DBNull) return DbType.Object;
-
- ITypeHandler handler;
- return LookupDbType(value.GetType(), "n/a", false, out handler);
-
- }
- internal static DbType LookupDbType(Type type, string name, bool demand, 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 (typeHandlers.TryGetValue(type, out handler))
- {
- return DbType.Object;
- }
- if (typeof(IEnumerable).IsAssignableFrom(type))
- {
- return DynamicParameters.EnumerableMultiParameter;
- }
-
-#if !DNXCORE50
- 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;
- }
-#endif
- if(demand)
- throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type.FullName));
- return DbType.Object;
-
- }
-
- ///
- /// Identity of a cached query in Dapper, used for extensibility
- ///
- 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;
- }
- }
-
- ///
- /// Obtains the data as a list; if it is *already* a list, the original object is returned without
- /// any duplication; otherwise, ToList() is invoked.
- ///
- public static List AsList(this IEnumerable source)
- {
- return (source == null || source is List) ? (List)source : source.ToList();
- }
-
-#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