diff --git a/.gitignore b/.gitignore
index ef6059592f97446c52465ede307c7e4c884379df..5467ebe9e2a16ce8c8df67afff0a91f8bd220738 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ obj/
.vs/WLib/v15/.suo
.vs/WLib/v15/Server/sqlite3/storage.ide-shm
.vs/WLib/v15/Server/sqlite3/storage.ide-wal
+licenses.licx
+Apps
diff --git a/App/Config/Params/config.db b/App/Config/Params/config.db
new file mode 100644
index 0000000000000000000000000000000000000000..f502bd4729059d699294348698a4d02b1ba9d501
Binary files /dev/null and b/App/Config/Params/config.db differ
diff --git "a/App/Config/Params/\344\270\211\350\260\203\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\277\350\245\277\344\270\211\350\260\203.db" "b/App/Config/Params/\344\270\211\350\260\203\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\277\350\245\277\344\270\211\350\260\203.db"
new file mode 100644
index 0000000000000000000000000000000000000000..f502bd4729059d699294348698a4d02b1ba9d501
Binary files /dev/null and "b/App/Config/Params/\344\270\211\350\260\203\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\277\350\245\277\344\270\211\350\260\203.db" differ
diff --git "a/App/Config/Params/\345\271\264\345\272\246\345\217\230\346\233\264\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\264\345\272\246\345\217\230\346\233\264.db" "b/App/Config/Params/\345\271\264\345\272\246\345\217\230\346\233\264\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\264\345\272\246\345\217\230\346\233\264.db"
new file mode 100644
index 0000000000000000000000000000000000000000..f502bd4729059d699294348698a4d02b1ba9d501
Binary files /dev/null and "b/App/Config/Params/\345\271\264\345\272\246\345\217\230\346\233\264\345\217\202\346\225\260\346\226\271\346\241\210/\345\271\264\345\272\246\345\217\230\346\233\264.db" differ
diff --git a/App/Config/config.db b/App/Config/config.db
new file mode 100644
index 0000000000000000000000000000000000000000..f502bd4729059d699294348698a4d02b1ba9d501
Binary files /dev/null and b/App/Config/config.db differ
diff --git a/DLL/System.ValueTuple.dll b/DLL/System.ValueTuple.dll
new file mode 100644
index 0000000000000000000000000000000000000000..65fa9eeead65ebcc4063900e21795b6fff6ed687
Binary files /dev/null and b/DLL/System.ValueTuple.dll differ
diff --git a/Dapper/CommandDefinition.cs b/Dapper/CommandDefinition.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b891f93fb351e23f9bb8d4680462b44cc47232d2
--- /dev/null
+++ b/Dapper/CommandDefinition.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Data;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Threading;
+
+namespace Dapper
+{
+ ///
+ /// Represents the key aspects of a sql operation
+ ///
+ public struct CommandDefinition
+ {
+ internal static CommandDefinition ForCallback(object parameters)
+ {
+ if (parameters is DynamicParameters)
+ {
+ return new CommandDefinition(parameters);
+ }
+ else
+ {
+ return default(CommandDefinition);
+ }
+ }
+
+ internal void OnCompleted()
+ {
+ (Parameters as SqlMapper.IParameterCallbacks)?.OnCompleted();
+ }
+
+ ///
+ /// The command (sql or a stored-procedure name) to execute
+ ///
+ public string CommandText { get; }
+
+ ///
+ /// The parameters associated with the command
+ ///
+ public object Parameters { get; }
+
+ ///
+ /// The active transaction for the command
+ ///
+ public IDbTransaction Transaction { get; }
+
+ ///
+ /// The effective timeout for the command
+ ///
+ public int? CommandTimeout { get; }
+
+ ///
+ /// The type of command that the command-text represents
+ ///
+ public CommandType? CommandType { get; }
+
+ ///
+ /// Should data be buffered before returning?
+ ///
+ public bool Buffered => (Flags & CommandFlags.Buffered) != 0;
+
+ ///
+ /// Should the plan for this query be cached?
+ ///
+ internal bool AddToCache => (Flags & CommandFlags.NoCache) == 0;
+
+ ///
+ /// Additional state flags against this command
+ ///
+ public CommandFlags Flags { get; }
+
+ ///
+ /// Can async queries be pipelined?
+ ///
+ public bool Pipelined => (Flags & CommandFlags.Pipelined) != 0;
+
+ ///
+ /// Initialize the command definition
+ ///
+ /// The text for this command.
+ /// The parameters for this command.
+ /// The transaction for this command to participate in.
+ /// The timeout (in seconds) for this command.
+ /// The for this command.
+ /// The behavior flags for this command.
+ /// The cancellation token for this command.
+ public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
+ CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered
+ , CancellationToken cancellationToken = default(CancellationToken)
+ )
+ {
+ CommandText = commandText;
+ Parameters = parameters;
+ Transaction = transaction;
+ CommandTimeout = commandTimeout;
+ CommandType = commandType;
+ Flags = flags;
+ CancellationToken = cancellationToken;
+ }
+
+ private CommandDefinition(object parameters) : this()
+ {
+ Parameters = parameters;
+ }
+
+ ///
+ /// For asynchronous operations, the cancellation-token
+ ///
+ public CancellationToken CancellationToken { get; }
+
+ internal IDbCommand SetupCommand(IDbConnection cnn, Action paramReader)
+ {
+ var cmd = cnn.CreateCommand();
+ var init = GetInit(cmd.GetType());
+ init?.Invoke(cmd);
+ if (Transaction != null)
+ cmd.Transaction = Transaction;
+ cmd.CommandText = CommandText;
+ if (CommandTimeout.HasValue)
+ {
+ cmd.CommandTimeout = CommandTimeout.Value;
+ }
+ else if (SqlMapper.Settings.CommandTimeout.HasValue)
+ {
+ cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value;
+ }
+ if (CommandType.HasValue)
+ cmd.CommandType = CommandType.Value;
+ paramReader?.Invoke(cmd, Parameters);
+ return cmd;
+ }
+
+ private static SqlMapper.Link> commandInitCache;
+
+ private static Action GetInit(Type commandType)
+ {
+ if (commandType == null)
+ return null; // GIGO
+ if (SqlMapper.Link>.TryGet(commandInitCache, commandType, out Action action))
+ {
+ return action;
+ }
+ var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
+ var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));
+
+ action = null;
+ if (bindByName != null || initialLongFetchSize != null)
+ {
+ var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
+ var il = method.GetILGenerator();
+
+ if (bindByName != null)
+ {
+ // .BindByName = true
+ il.Emit(OpCodes.Ldarg_0);
+ il.Emit(OpCodes.Castclass, commandType);
+ il.Emit(OpCodes.Ldc_I4_1);
+ il.EmitCall(OpCodes.Callvirt, bindByName, null);
+ }
+ if (initialLongFetchSize != null)
+ {
+ // .InitialLONGFetchSize = -1
+ il.Emit(OpCodes.Ldarg_0);
+ il.Emit(OpCodes.Castclass, commandType);
+ il.Emit(OpCodes.Ldc_I4_M1);
+ il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null);
+ }
+ il.Emit(OpCodes.Ret);
+ action = (Action)method.CreateDelegate(typeof(Action));
+ }
+ // cache it
+ SqlMapper.Link>.TryAdd(ref commandInitCache, commandType, ref action);
+ return action;
+ }
+
+ private static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType)
+ {
+ var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
+ if (prop?.CanWrite == true && prop.PropertyType == expectedType && prop.GetIndexParameters().Length == 0)
+ {
+ return prop.GetSetMethod();
+ }
+ return null;
+ }
+ }
+}
diff --git a/Dapper/CommandFlags.cs b/Dapper/CommandFlags.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d85a384f7295e2bb96b0fc3c5dd420b2db17fb8c
--- /dev/null
+++ b/Dapper/CommandFlags.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Dapper
+{
+ ///
+ /// 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,
+ }
+}
diff --git a/Dapper/Custom/ForeignKeyAttribute.cs b/Dapper/Custom/ForeignKeyAttribute.cs
new file mode 100644
index 0000000000000000000000000000000000000000..20fd5a8d977b0a356feee8727a7124228d1f8a57
--- /dev/null
+++ b/Dapper/Custom/ForeignKeyAttribute.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dapper
+{
+ ///
+ /// 表示外键
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class ForeignKeyAttribute : Attribute
+ {
+ ///
+ /// 外键所关联的表
+ ///
+ public string ForeignTable { get; set; }
+ ///
+ /// 外键所关联的表的字段
+ ///
+ public string ForeignField { get; set; }
+ ///
+ /// 表示外键
+ ///
+ /// 外键信息,格式为“ForeignTable.ForeginColumn”
+ public ForeignKeyAttribute(string foreignTable)
+ {
+ ForeignTable = foreignTable;
+ }
+ ///
+ /// 表示外键
+ ///
+ /// 外键所关联的表
+ /// 外键所关联的表的字段
+ public ForeignKeyAttribute(string foreignTable, string foreignField)
+ {
+ ForeignTable = foreignTable;
+ ForeignField = foreignField;
+ }
+ }
+}
diff --git a/Dapper/Custom/KeyAttribute.cs b/Dapper/Custom/KeyAttribute.cs
new file mode 100644
index 0000000000000000000000000000000000000000..14f91b652827bb95596fa7b4d043593f55e4d447
--- /dev/null
+++ b/Dapper/Custom/KeyAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dapper
+{
+ ///
+ /// 表示主键
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class KeyAttribute : Attribute
+ {
+ }
+}
diff --git a/Dapper/Custom/SqlMapper.Extension.cs b/Dapper/Custom/SqlMapper.Extension.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2840d64575fbddca57602502f224bdbfd37d2e8d
--- /dev/null
+++ b/Dapper/Custom/SqlMapper.Extension.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+namespace Dapper
+{
+ /*
+ * 自定义对Dapper组件的扩展,添加减少SQL语句对数据库进行最基本的增删改查的方法、where条件语句构造方法
+ */
+
+ public static partial class SqlMapper
+ {
+ #region 基本ORM增删改查
+ ///
+ /// 根据类名查询同名的表,转成指定类型数据
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable SimpleQuery(this IDbConnection dbConnection, string whereClause = null) where T : class
+ {
+ whereClause = string.IsNullOrWhiteSpace(whereClause) ? string.Empty : "where " + whereClause;
+ return dbConnection.Query($"select * from {typeof(T).Name} {whereClause}");
+ }
+ ///
+ /// 根据类名向同名的表插入数据
+ ///
+ ///
+ ///
+ /// 要插入的对象或对象数组(IEnumerable)
+ ///
+ public static int SimpleInsert(this IDbConnection dbConnection, T value)
+ {
+ var t = typeof(T);
+ var @params = t.GetProperties().Select(v => "@" + v.Name).Aggregate((a, b) => a + "," + b);
+ return dbConnection.Execute($"Insert into {t.Name} values ({@params})", value);
+ }
+ ///
+ /// 根据类名更新同名的表的指定数据
+ ///
+ ///
+ ///
+ /// 要更新的对象或对象数组(IEnumerable)
+ ///
+ public static int SimpleUpdate(this IDbConnection dbConnection, T value)
+ {
+ var t = typeof(T);
+ var properties = t.GetProperties();
+ var @params = properties.Select(v => $"{v.Name} = @{v.Name}").Aggregate((a, b) => a + "," + b);
+ var keyProperty = properties.FirstOrDefault(v => v.GetCustomAttributes(typeof(KeyAttribute), false) != null);
+ if (keyProperty == null)
+ throw new System.Exception($"类型{t.Name}未定义主键(Key),无法执行{nameof(SimpleUpdate)}操作");
+
+ var whereClause = $"{keyProperty.Name} = @{keyProperty.Name}";
+ return dbConnection.Execute($"update {t.Name} set {@params} where {whereClause}", value);
+ }
+ ///
+ /// 根据类名删除同名的表的指定数据
+ ///
+ ///
+ ///
+ /// 要删除的对象或对象数组(IEnumerable)
+ ///
+ public static int SimpleDelete(this IDbConnection dbConnection, T value)
+ {
+ var t = typeof(T);
+ var properties = t.GetProperties();
+ var keyProperty = properties.FirstOrDefault(v => v.GetCustomAttributes(typeof(KeyAttribute), false) != null);
+ if (keyProperty == null)
+ throw new System.Exception($"类型{t.Name}未定义主键(Key),无法执行{nameof(SimpleUpdate)}操作");
+
+ var whereClause = $"{keyProperty.Name} = @{keyProperty.Name}";
+ return dbConnection.Execute($"delete from {t.Name} where {whereClause}", value);
+ }
+ #endregion
+
+
+ #region 条件查询语句构建
+ public static string And(this string str, string fieldName) => $"{str} and {fieldName}";
+ public static string Or(this string str, string fieldName) => $"{str} or {fieldName}";
+ public static string Like(this string str, string value) => $"{str} like '{value}'";
+ public static string In(this string str, params object[] values)
+ {
+ if (values == null || values.Length == 0)
+ throw new System.ArgumentException($"参数{values}不能为null或数组个数为0!");
+
+ bool isStr = values[0] is string;
+ var inValues = string.Empty;
+ if (isStr)
+ inValues = values.Select(v => $"'{v}'").Aggregate((a, b) => a + "," + b);
+ else
+ inValues = values.Select(v => v.ToString()).Aggregate((a, b) => a + "," + b);
+ return $"{str} In ({inValues})";
+ }
+ public static string Equal(this string str, object value) => value is string ? $"{str} = '{value}'" : $"{str} = {value}";
+ public static string NotEqual(this string str, object value) => value is string ? $"{str} <> '{value}'" : $"{str} <> {value}";
+ public static string GreaterThan(this string str, object value) => value is string ? $"{str} > '{value}'" : $"{str} > {value}";
+ public static string GreaterThanEqual(this string str, object value) => value is string ? $"{str} >= '{value}'" : $"{str} >= {value}";
+ public static string LessThan(this string str, object value) => value is string ? $"{str} < '{value}'" : $"{str} < {value}";
+ public static string LessThanEqual(this string str, object value) => value is string ? $"{str} <= '{value}'" : $"{str} <= {value}";
+ #endregion
+ }
+}
diff --git a/Dapper/CustomPropertyTypeMap.cs b/Dapper/CustomPropertyTypeMap.cs
new file mode 100644
index 0000000000000000000000000000000000000000..629cf14dcc2c9345a089e9ad0cd72fe65211f829
--- /dev/null
+++ b/Dapper/CustomPropertyTypeMap.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Reflection;
+
+namespace Dapper
+{
+ ///
+ /// Implements custom property mapping by user provided criteria (usually presence of some custom attribute with column to member mapping)
+ ///
+ public sealed 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)
+ {
+ _type = type ?? throw new ArgumentNullException(nameof(type));
+ _propertySelector = propertySelector ?? throw new ArgumentNullException(nameof(propertySelector));
+ }
+
+ ///
+ /// Always returns default constructor
+ ///
+ /// DataReader column names
+ /// DataReader column types
+ /// Default constructor
+ public ConstructorInfo FindConstructor(string[] names, Type[] types) =>
+ _type.GetConstructor(new Type[0]);
+
+ ///
+ /// Always returns null
+ ///
+ ///
+ public ConstructorInfo FindExplicitConstructor() => null;
+
+ ///
+ /// Not implemented 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;
+ }
+ }
+}
diff --git a/Dapper/Dapper.csproj b/Dapper/Dapper.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..ad8c8c9e4e132ea447703e272db8f07538a899a8
--- /dev/null
+++ b/Dapper/Dapper.csproj
@@ -0,0 +1,98 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {B69EA382-D83F-481F-88B9-F0AC9DBCE928}
+ Library
+ Properties
+ Dapper
+ Dapper
+ v4.5
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Dapper/DataTableHandler.cs b/Dapper/DataTableHandler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..817961500705c8f83882ab23b718246d606929c2
--- /dev/null
+++ b/Dapper/DataTableHandler.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Data;
+#if !NETSTANDARD1_3
+namespace Dapper
+{
+ internal sealed class DataTableHandler : 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);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Dapper/DbString.cs b/Dapper/DbString.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cec7291962ec3c5cfc36c9bf4445909149e54ce1
--- /dev/null
+++ b/Dapper/DbString.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Data;
+
+namespace Dapper
+{
+ ///
+ /// 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
+ ///
+ public sealed class DbString : SqlMapper.ICustomQueryParameter
+ {
+ ///
+ /// Default value for IsAnsi.
+ ///
+ public static bool IsAnsiDefault { get; set; }
+
+ ///
+ /// 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;
+ IsAnsi = IsAnsiDefault;
+ }
+ ///
+ /// 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");
+ }
+ bool add = !command.Parameters.Contains(name);
+ IDbDataParameter param;
+ if (add)
+ {
+ param = command.CreateParameter();
+ param.ParameterName = name;
+ }
+ else
+ {
+ param = (IDbDataParameter)command.Parameters[name];
+ }
+#pragma warning disable 0618
+ param.Value = SqlMapper.SanitizeParameterValue(Value);
+#pragma warning restore 0618
+ 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);
+ if (add)
+ {
+ command.Parameters.Add(param);
+ }
+ }
+ }
+}
diff --git a/Dapper/DefaultTypeMap.cs b/Dapper/DefaultTypeMap.cs
new file mode 100644
index 0000000000000000000000000000000000000000..277c678847e9ebb5ba09d65e35aca3b6deb40bdb
--- /dev/null
+++ b/Dapper/DefaultTypeMap.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Dapper
+{
+ ///
+ /// Represents default type mapping strategy used by Dapper
+ ///
+ public sealed class DefaultTypeMap : SqlMapper.ITypeMap
+ {
+ private readonly List _fields;
+ private readonly Type _type;
+
+ ///
+ /// Creates default type map
+ ///
+ /// Entity type
+ public DefaultTypeMap(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException(nameof(type));
+
+ _fields = GetSettableFields(type);
+ Properties = GetSettableProps(type);
+ _type = type;
+ }
+#if NETSTANDARD1_3
+ private static bool IsParameterMatch(ParameterInfo[] x, ParameterInfo[] y)
+ {
+ if (ReferenceEquals(x, y)) return true;
+ if (x == null || y == null) return false;
+ if (x.Length != y.Length) return false;
+ for (int i = 0; i < x.Length; i++)
+ if (x[i].ParameterType != y[i].ParameterType) return false;
+ return true;
+ }
+#endif
+ internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type)
+ {
+ if (propertyInfo.DeclaringType == type) return propertyInfo.GetSetMethod(true);
+#if NETSTANDARD1_3
+ return propertyInfo.DeclaringType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Single(x => x.Name == propertyInfo.Name
+ && x.PropertyType == propertyInfo.PropertyType
+ && IsParameterMatch(x.GetIndexParameters(), propertyInfo.GetIndexParameters())
+ ).GetSetMethod(true);
+#else
+ return 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);
+#endif
+ }
+
+ 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] && !SqlMapper.HasTypeHandler(unboxedType))
+ && !(unboxedType.IsEnum() && Enum.GetUnderlyingType(unboxedType) == types[i])
+ && !(unboxedType == typeof(char) && types[i] == typeof(string))
+ && !(unboxedType.IsEnum() && types[i] == typeof(string)))
+ {
+ break;
+ }
+ }
+
+ if (i == ctorParameters.Length)
+ return ctor;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it.
+ ///
+ public ConstructorInfo FindExplicitConstructor()
+ {
+ var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+#if NETSTANDARD1_3
+ var withAttr = constructors.Where(c => c.CustomAttributes.Any(x => x.AttributeType == typeof(ExplicitConstructorAttribute))).ToList();
+#else
+ var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList();
+#endif
+
+ if (withAttr.Count == 1)
+ {
+ return withAttr[0];
+ }
+
+ 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.Find(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
+ ?? Properties.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
+
+ if (property == null && MatchNamesWithUnderscores)
+ {
+ property = Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
+ ?? Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
+ }
+
+ if (property != null)
+ return new SimpleMemberMap(columnName, property);
+
+ // roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
+ var backingFieldName = "<" + columnName + ">k__BackingField";
+
+ // preference order is:
+ // exact match over underscre match, exact case over wrong case, backing fields over regular fields, match-inc-underscores over match-exc-underscores
+ var field = _fields.Find(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
+ ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
+ ?? _fields.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))
+ ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
+
+ if (field == null && MatchNamesWithUnderscores)
+ {
+ var effectiveColumnName = columnName.Replace("_", "");
+ backingFieldName = "<" + effectiveColumnName + ">k__BackingField";
+
+ field = _fields.Find(p => string.Equals(p.Name, effectiveColumnName, StringComparison.Ordinal))
+ ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
+ ?? _fields.Find(p => string.Equals(p.Name, effectiveColumnName, StringComparison.OrdinalIgnoreCase))
+ ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, 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; }
+
+ ///
+ /// The settable properties for this typemap
+ ///
+ public List Properties { get; }
+ }
+}
diff --git a/Dapper/DynamicParameters.CachedOutputSetters.cs b/Dapper/DynamicParameters.CachedOutputSetters.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aff2a4cfcace9d34b4d979c2a027ee5909e69c21
--- /dev/null
+++ b/Dapper/DynamicParameters.CachedOutputSetters.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+
+namespace Dapper
+{
+ public partial class DynamicParameters
+ {
+ // The type here is used to differentiate the cache by type via generics
+ // ReSharper disable once UnusedTypeParameter
+ internal static class CachedOutputSetters
+ {
+ // Intentional, abusing generics to get our cache splits
+ // ReSharper disable once StaticMemberInGenericType
+ public static readonly Hashtable Cache = new Hashtable();
+ }
+ }
+}
diff --git a/Dapper/DynamicParameters.ParamInfo.cs b/Dapper/DynamicParameters.ParamInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..32fa1ab30f62a5da7895c31e7036cc338935aea7
--- /dev/null
+++ b/Dapper/DynamicParameters.ParamInfo.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Data;
+
+namespace Dapper
+{
+ public partial class DynamicParameters
+ {
+ private sealed 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