Dapper的完整扩展(转)

真心想说:其实。。。我不想用Dapper,如果OrmLite.Net支持参数化的话,也就没Dapper的什么事情了,对于OrmLite.Net只能做后续跟踪......

这个其实是看了Dapper作者的扩展后觉得不爽,然后自己按照他的设计思路重写了代码,只支持单个数据的增删改查,根据Expression来查的真心无能为力......

另外作者似乎已经支持了属性、字段等与数据库中的映射.....

具体包含了

1、对字符串的扩展

2、对主键的定义,支持单或多主键,当单主键并且类型为数字时,认为该主键为自增列

3、对表名的定义

 

实际代码如下:

DapperExtensions部分

[csharp] view plaincopy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Data;  
  6. using System.Collections.Concurrent;  
  7. using System.Reflection;  
  8.   
  9. namespace Dapper  
  10. {  
  11.     /// <summary>  
  12.     /// Dapper扩展POCO信息类,为应用此扩展,在原Dapper的第1965行增加了对此类的应用  
  13.     /// </summary>  
  14.     public class DapperPocoInfo  
  15.     {  
  16.         private Type _type;  
  17.         private ConcurrentDictionary<string, KeyValuePair<DbType, int>> _stringColumns  
  18.             = new ConcurrentDictionary<string, KeyValuePair<DbType, int>>();  
  19.         private IEnumerable<PropertyInfo> _typeProperties;  
  20.         private IList<PropertyInfo> _keyProperties = new List<PropertyInfo>();  
  21.         private IEnumerable<PropertyInfo> _defaultKeyPropeties;  
  22.         private string _tableName;  
  23.         /// <summary>  
  24.         /// 对应表名  
  25.         /// </summary>  
  26.         public string TableName  
  27.         {  
  28.             get  
  29.             {  
  30.                 if (string.IsNullOrWhiteSpace(this._tableName))  
  31.                 {  
  32.                     this._tableName = this._type.Name;  
  33.                 }  
  34.                 return this._tableName;  
  35.             }  
  36.             set  
  37.             {  
  38.                 this._tableName = value;  
  39.             }  
  40.         }  
  41.         /// <summary>  
  42.         /// 所有Public的属性集合  
  43.         /// </summary>  
  44.         public IEnumerable<PropertyInfo> TypeProperties  
  45.         {  
  46.             get { return this._typeProperties; }  
  47.         }  
  48.         /// <summary>  
  49.         /// 获取主键集合,如果主键数量大于1,则认为是联合主键,等于0认为不存在主键,等于1并且类型为数字型则认为自增主键  
  50.         /// </summary>  
  51.         /// <returns></returns>  
  52.         public IEnumerable<PropertyInfo> KeyProperties  
  53.         {  
  54.             get  
  55.             {  
  56.                 if (this._keyProperties.Count == 0)  
  57.                 {//如果未设定KeyProperties,则默认认为第一个后缀为ID的PropertyInfo为主键  
  58.                     if (this._defaultKeyPropeties == null)  
  59.                     {  
  60.                         this._defaultKeyPropeties = this._typeProperties.Where(p => p.Name.ToLower().EndsWith("id")).Take(1).ToArray();  
  61.                     }  
  62.                     return this._defaultKeyPropeties;  
  63.                 }  
  64.                 else  
  65.                 {  
  66.                     return this._keyProperties;  
  67.                 }  
  68.             }  
  69.         }  
  70.         /// <summary>  
  71.         /// .oct  
  72.         /// </summary>  
  73.         /// <param name="type"></param>  
  74.         internal DapperPocoInfo(Type type)  
  75.         {  
  76.             if (type == null)  
  77.             {  
  78.                 throw new ArgumentNullException();  
  79.             }  
  80.             this._type = type;  
  81.             this._typeProperties = type.GetProperties();  
  82.         }  
  83.         /// <summary>  
  84.         /// 添加字符串参数化映射  
  85.         /// </summary>  
  86.         /// <param name="propertyName">属性名</param>  
  87.         /// <param name="dbType">必须为AnsiString、AnsiStringFixedLength、String、StringFixedLength</param>  
  88.         /// <param name="len">值范围为1~8000</param>  
  89.         public virtual void AddStringColumnMap(string propertyName, DbType dbType = DbType.AnsiString, int len = 50)  
  90.         {  
  91.             this.GetProperty(propertyName);  
  92.             if (len <= 0 || len > 8000)  
  93.             {//长度范围1~8000,此处暂时对应sql,如果其它关系型数据库长度范围与此不一致,可继承修改  
  94.                 throw new ArgumentException("The param len's value must between 1 and 8000.");  
  95.             }  
  96.             if (dbType != DbType.AnsiString && dbType != DbType.AnsiStringFixedLength && dbType != DbType.String && dbType != DbType.StringFixedLength)  
  97.             {  
  98.                 return;  
  99.             }  
  100.             this._stringColumns.TryAdd(propertyName, new KeyValuePair<DbType, int>(dbType, len));  
  101.         }  
  102.         /// <summary>  
  103.         /// 获取字符串映射  
  104.         /// </summary>  
  105.         /// <param name="propertyName"></param>  
  106.         /// <returns></returns>  
  107.         public KeyValuePair<DbType, int>? GetStringColumnMap(string propertyName)  
  108.         {  
  109.             KeyValuePair<DbType, int> kvp;  
  110.             if (this._stringColumns.TryGetValue(propertyName, out kvp))  
  111.             {  
  112.                 return kvp;  
  113.             }  
  114.             return null;  
  115.         }  
  116.         private PropertyInfo GetProperty(string propertyName)  
  117.         {  
  118.             if (string.IsNullOrWhiteSpace(propertyName))  
  119.             {  
  120.                 throw new ArgumentNullException("propertyName can not be null or empty value");  
  121.             }  
  122.             PropertyInfo pi = this._typeProperties.Where(p => p.Name.ToLower() == propertyName.ToLower()).FirstOrDefault();  
  123.             if (pi == null)  
  124.             {  
  125.                 throw new ArgumentOutOfRangeException(string.Format("The class '{0}' does not contains property '{1}'."this._type.FullName, propertyName));  
  126.             }  
  127.             return pi;  
  128.         }  
  129.         /// <summary>  
  130.         /// 添加主键映射  
  131.         /// </summary>  
  132.         /// <param name="propertyName"></param>  
  133.         public void AddKeyMap(string propertyName)  
  134.         {  
  135.             var pi = this.GetProperty(propertyName);  
  136.             if (this._keyProperties.Where(p => p.Name == pi.Name).FirstOrDefault() == null)  
  137.             {  
  138.                 this._keyProperties.Add(pi);  
  139.                 this._unWriteKey = null;//赋值时取消已经确认的是否可写键值  
  140.             }  
  141.         }  
  142.         /// <summary>  
  143.         /// 不需要插入数据的主键类型,除了Guid,其它均认为自增  
  144.         /// </summary>  
  145.         private static Type[] UnWriteTypes = { typeof(int), typeof(short), typeof(long), typeof(byte), typeof(Guid) };  
  146.         private bool? _unWriteKey;  
  147.         /// <summary>  
  148.         /// 主键是否可写  
  149.         /// </summary>  
  150.         /// <returns></returns>  
  151.         public bool IsUnWriteKey()  
  152.         {  
  153.             if (!this._unWriteKey.HasValue)  
  154.             {  
  155.                 this._unWriteKey = false;  
  156.                 IList<PropertyInfo> keys = this.KeyProperties.ToList();  
  157.                 if (keys.Count == 1)  
  158.                 {  
  159.                     this._unWriteKey = UnWriteTypes.Contains(keys[0].PropertyType);  
  160.                 }  
  161.             }  
  162.             return this._unWriteKey.Value;  
  163.         }  
  164.     }  
  165.     /// <summary>  
  166.     /// Dapper扩展类  
  167.     /// </summary>  
  168.     public static class DapperExtensions  
  169.     {  
  170.         private static ConcurrentDictionary<RuntimeTypeHandle, DapperPocoInfo> PocoInfos  
  171.             = new ConcurrentDictionary<RuntimeTypeHandle, DapperPocoInfo>();  
  172.         /// <summary>  
  173.         /// 已实现的ISqlAdapter集合  
  174.         /// </summary>  
  175.         private static readonly Dictionary<string, ISqlAdapter> AdapterDictionary  
  176.             = new Dictionary<string, ISqlAdapter>() {  
  177.             {"sqlconnection"new MsSqlServerAdapter()}  
  178.             };  
  179.   
  180.         public static DapperPocoInfo GetPocoInfo<T>()  
  181.             where T : class  
  182.         {  
  183.             return GetPocoInfo(typeof(T));  
  184.         }  
  185.         public static DapperPocoInfo GetPocoInfo(this Type type)  
  186.         {  
  187.             DapperPocoInfo pi;  
  188.             RuntimeTypeHandle hd = type.TypeHandle;  
  189.             if (!PocoInfos.TryGetValue(hd, out pi))  
  190.             {  
  191.                 pi = new DapperPocoInfo(type);  
  192.                 PocoInfos[hd] = pi;  
  193.             }  
  194.             return pi;  
  195.         }  
  196.   
  197.         public static ISqlAdapter GetSqlAdapter(this IDbConnection connection)  
  198.         {  
  199.             string name = connection.GetType().Name.ToLower();  
  200.             ISqlAdapter adapter;  
  201.             if (!AdapterDictionary.TryGetValue(name, out adapter))  
  202.             {  
  203.                 throw new NotImplementedException(string.Format("Unknow sql connection '{0}'", name));  
  204.             }  
  205.             return adapter;  
  206.         }  
  207.   
  208.         /// <summary>  
  209.         /// 新增数据,如果T只有一个主键,且新增的主键为数字,则会将新增的主键赋值给entity相应的字段,如果为多主键,或主键不是数字和Guid,则主键需输入  
  210.         /// 如果要进行匿名类型新增,因为匿名类型无法赋值,需调用ISqlAdapter的Insert,由数据库新增的主键需自己写方法查询  
  211.         /// </summary>  
  212.         /// <typeparam name="T"></typeparam>  
  213.         /// <param name="connection"></param>  
  214.         /// <param name="entity"></param>  
  215.         /// <param name="transaction"></param>  
  216.         /// <param name="commandTimeout"></param>  
  217.         /// <returns></returns>  
  218.         public static bool Insert<T>(this IDbConnection connection, T entity, IDbTransaction transaction = nullint? commandTimeout = null)  
  219.             where T : class  
  220.         {  
  221.             ISqlAdapter adapter = GetSqlAdapter(connection);  
  222.             return adapter.Insert<T>(connection, entity, transaction, commandTimeout);  
  223.         }  
  224.         /// <summary>  
  225.         /// 更新数据,如果entity为T,则全字段更新,如果为匿名类型,则修改包含的字段,但匿名类型必须包含主键对应的字段  
  226.         /// </summary>  
  227.         /// <typeparam name="T"></typeparam>  
  228.         /// <param name="connection"></param>  
  229.         /// <param name="entity"></param>  
  230.         /// <param name="transaction"></param>  
  231.         /// <param name="commandTimeout"></param>  
  232.         /// <returns></returns>  
  233.         public static bool Update<T>(this IDbConnection connection, object entity, IDbTransaction transaction = nullint? commandTimeout = null)  
  234.             where T : class  
  235.         {  
  236.             ISqlAdapter adapter = GetSqlAdapter(connection);  
  237.             return adapter.UpdateByKey<T>(connection, entity, transaction, commandTimeout);  
  238.         }  
  239.         /// <summary>  
  240.         /// 删除数据,支持匿名,但匿名类型必须包含主键对应的字段  
  241.         /// </summary>  
  242.         /// <typeparam name="T"></typeparam>  
  243.         /// <param name="connection"></param>  
  244.         /// <param name="param"></param>  
  245.         /// <param name="transaction"></param>  
  246.         /// <param name="commandTimeout"></param>  
  247.         /// <returns></returns>  
  248.         public static bool DeleteByKey<T>(this IDbConnection connection, object param, IDbTransaction transaction = nullint? commandTimeout = null)  
  249.             where T : class  
  250.         {  
  251.             ISqlAdapter adapter = GetSqlAdapter(connection);  
  252.             return adapter.DeleteByKey<T>(connection, param, transaction, commandTimeout);  
  253.         }  
  254.         /// <summary>  
  255.         /// 查询数据,支持匿名,但匿名类型必须包含主键对应的字段  
  256.         /// </summary>  
  257.         /// <typeparam name="T"></typeparam>  
  258.         /// <param name="connection"></param>  
  259.         /// <param name="param"></param>  
  260.         /// <param name="transaction"></param>  
  261.         /// <param name="commandTimeout"></param>  
  262.         /// <returns></returns>  
  263.         public static T QueryByKey<T>(this IDbConnection connection, object param, IDbTransaction transaction = nullint? commandTimeout = null)  
  264.             where T : class  
  265.         {  
  266.             ISqlAdapter adapter = GetSqlAdapter(connection);  
  267.             return adapter.QueryByKey<T>(connection, param, transaction, commandTimeout);  
  268.         }  
  269.         /// <summary>  
  270.         /// 获取最后新增的ID值,仅对Indentity有效  
  271.         /// </summary>  
  272.         /// <typeparam name="T"></typeparam>  
  273.         /// <param name="connection"></param>  
  274.         /// <returns></returns>  
  275.         public static long GetLastInsertIndentityID<T>(this IDbConnection connection)  
  276.             where T : class  
  277.         {  
  278.             ISqlAdapter adapter = GetSqlAdapter(connection);  
  279.             return adapter.GetLastInsertID<T>(connection);  
  280.         }  
  281.     }  
  282.   
  283.     public interface ISqlAdapter  
  284.     {  
  285.         string GetFullQueryByKeySql(Type type);  
  286.         string GetFullInsertSql(Type type);  
  287.         string GetFullUpdateByKeySql(Type type);  
  288.         string GetDeleteByKeySql(Type type);  
  289.         bool Insert<T>(IDbConnection connection, object entity, IDbTransaction transaction, int? commandTimeout)  
  290.             where T : class;  
  291.         bool UpdateByKey<T>(IDbConnection connection, object entity, IDbTransaction transaction, int? commandTimeout)  
  292.             where T : class;  
  293.         bool DeleteByKey<T>(IDbConnection connection, object param, IDbTransaction transaction, int? commandTimeout)  
  294.             where T : class;  
  295.         T QueryByKey<T>(IDbConnection connection, object param, IDbTransaction transaction, int? commandTimeout)  
  296.             where T : class;  
  297.         long GetLastInsertID<T>(IDbConnection connection)  
  298.             where T : class;  
  299.     }  
  300.   
  301.     internal class BasicSql  
  302.     {  
  303.         public string FullQueryByKeySql { getset; }  
  304.         public string FullInsertSql { getset; }  
  305.         public string FullUpdateByKeySql { getset; }  
  306.         public string DeleteByKeySql { getset; }  
  307.     }  
  308.   
  309.     public class MsSqlServerAdapter : ISqlAdapter  
  310.     {  
  311.         private static ConcurrentDictionary<RuntimeTypeHandle, BasicSql> BasicSqls  
  312.             = new ConcurrentDictionary<RuntimeTypeHandle, BasicSql>();  
  313.         private static readonly char SqlParameterChar = '@';  
  314.   
  315.         internal MsSqlServerAdapter() { }  
  316.   
  317.         private string GetParameterName(PropertyInfo pi)  
  318.         {  
  319.             return string.Format("{0}{1}", SqlParameterChar, pi.Name);  
  320.         }  
  321.         private BasicSql GetBasicSql(Type type)  
  322.         {  
  323.             BasicSql basicSql;  
  324.             RuntimeTypeHandle hd = type.TypeHandle;  
  325.             if (!BasicSqls.TryGetValue(hd, out basicSql))  
  326.             {  
  327.                 basicSql = new BasicSql();  
  328.                 BasicSqls[hd] = basicSql;  
  329.             }  
  330.             return basicSql;  
  331.         }  
  332.         private void AppendKeyParameter(StringBuilder tmp, IEnumerable<PropertyInfo> keys)  
  333.         {  
  334.             if (keys.Any())  
  335.             {  
  336.                 tmp.AppendLine(" WHERE");  
  337.                 foreach (PropertyInfo key in keys)  
  338.                 {  
  339.                     tmp.Append(key.Name);  
  340.                     tmp.Append("=");  
  341.                     tmp.Append(this.GetParameterName(key));  
  342.                     tmp.Append(" AND ");  
  343.                 }  
  344.                 tmp.Remove(tmp.Length - 5, 5);  
  345.             }  
  346.         }  
  347.         public string GetFullQueryByKeySql(Type type)  
  348.         {  
  349.             BasicSql basicSql = this.GetBasicSql(type);  
  350.             if (string.IsNullOrEmpty(basicSql.FullQueryByKeySql))  
  351.             {  
  352.                 DapperPocoInfo dpi = type.GetPocoInfo();  
  353.                 StringBuilder tmp = new StringBuilder();  
  354.                 tmp.Append("SELECT * FROM ");  
  355.                 tmp.Append(dpi.TableName);  
  356.                 tmp.Append(" (NOLOCK) ");  
  357.                 this.AppendKeyParameter(tmp, dpi.KeyProperties);  
  358.                 basicSql.FullQueryByKeySql = tmp.ToString();  
  359.             }  
  360.             return basicSql.FullQueryByKeySql;  
  361.         }  
  362.   
  363.         public string GetFullInsertSql(Type type)  
  364.         {  
  365.             BasicSql basicSql = this.GetBasicSql(type);  
  366.             if (string.IsNullOrEmpty(basicSql.FullInsertSql))  
  367.             {  
  368.                 DapperPocoInfo dpi = type.GetPocoInfo();  
  369.                 basicSql.FullInsertSql = this.GetInsertSql(dpi, dpi.TypeProperties);  
  370.             }  
  371.             return basicSql.FullInsertSql;  
  372.         }  
  373.   
  374.         private string GetInsertSql(DapperPocoInfo dpi, IEnumerable<PropertyInfo> props)  
  375.         {  
  376.             StringBuilder tmp = new StringBuilder();  
  377.             tmp.Append("INSERT INTO ");  
  378.             tmp.AppendLine(dpi.TableName);  
  379.             tmp.Append('(');  
  380.   
  381.             IEnumerable<PropertyInfo> valueProps = props;  
  382.             if (dpi.IsUnWriteKey())  
  383.             {  
  384.                 valueProps = this.GetExceptProps(props, dpi.KeyProperties);  
  385.             }  
  386.   
  387.             this.AppendColumnList(tmp, valueProps, '\0');  
  388.             tmp.Append(')');  
  389.             tmp.AppendLine(" VALUES ");  
  390.             tmp.Append('(');  
  391.             this.AppendColumnList(tmp, valueProps, SqlParameterChar);  
  392.             tmp.Append(')');  
  393.   
  394.             return tmp.ToString();  
  395.         }  
  396.         private void AppendColumnList(StringBuilder tmp, IEnumerable<PropertyInfo> valueProps, char addChar)  
  397.         {  
  398.             foreach (PropertyInfo p in valueProps)  
  399.             {  
  400.                 tmp.Append(addChar);  
  401.                 tmp.Append(p.Name);  
  402.                 tmp.Append(',');  
  403.             }  
  404.             tmp.Remove(tmp.Length - 1, 1);  
  405.         }  
  406.         private IEnumerable<PropertyInfo> GetExceptProps(IEnumerable<PropertyInfo> props1, IEnumerable<PropertyInfo> props2)  
  407.         {  
  408.             //return props1.Except(props2, new EqualityCompareProperty()).ToArray();  
  409.             IList<PropertyInfo> list = new List<PropertyInfo>();  
  410.             foreach (PropertyInfo pi in props1)  
  411.             {  
  412.                 string name = pi.Name.ToLower();  
  413.                 if (!props2.Any(p => p.Name.ToLower() == name))  
  414.                 {  
  415.                     list.Add(pi);  
  416.                 }  
  417.             }  
  418.             return list;  
  419.         }  
  420.         private string GetUpdateSql(DapperPocoInfo dpi, IEnumerable<PropertyInfo> props)  
  421.         {  
  422.             StringBuilder tmp = new StringBuilder();  
  423.             tmp.Append("UPDATE ");  
  424.             tmp.AppendLine(dpi.TableName);  
  425.             tmp.Append("SET ");  
  426.             IEnumerable<PropertyInfo> valueProps = this.GetExceptProps(props, dpi.KeyProperties);  
  427.             foreach (PropertyInfo p in valueProps)  
  428.             {  
  429.                 tmp.Append(p.Name);  
  430.                 tmp.Append('=');  
  431.                 tmp.Append(SqlParameterChar);  
  432.                 tmp.Append(p.Name);  
  433.                 tmp.Append(',');  
  434.             }  
  435.             tmp.Remove(tmp.Length - 1, 1);  
  436.             tmp.AppendLine();  
  437.             this.AppendKeyParameter(tmp, dpi.KeyProperties);  
  438.             return tmp.ToString();  
  439.         }  
  440.   
  441.         public string GetFullUpdateByKeySql(Type type)  
  442.         {  
  443.             BasicSql basicSql = this.GetBasicSql(type);  
  444.             if (string.IsNullOrEmpty(basicSql.FullUpdateByKeySql))  
  445.             {  
  446.                 DapperPocoInfo dpi = type.GetPocoInfo();  
  447.                 basicSql.FullUpdateByKeySql = this.GetUpdateSql(dpi, dpi.TypeProperties);  
  448.             }  
  449.             return basicSql.FullUpdateByKeySql;  
  450.         }  
  451.   
  452.         public string GetDeleteByKeySql(Type type)  
  453.         {  
  454.             BasicSql basicSql = this.GetBasicSql(type);  
  455.             if (string.IsNullOrEmpty(basicSql.DeleteByKeySql))  
  456.             {  
  457.                 DapperPocoInfo dpi = type.GetPocoInfo();  
  458.                 StringBuilder tmp = new StringBuilder();  
  459.                 tmp.Append("DELETE FROM ");  
  460.                 tmp.AppendLine(dpi.TableName);  
  461.                 this.AppendKeyParameter(tmp, dpi.KeyProperties);  
  462.                 basicSql.DeleteByKeySql = tmp.ToString();  
  463.             }  
  464.             return basicSql.DeleteByKeySql;  
  465.         }  
  466.   
  467.         public bool Insert<T>(IDbConnection connection, object entity, IDbTransaction transaction = nullint? commandTimeout = null) where T : class  
  468.         {  
  469.             Type type = typeof(T);  
  470.             string insertSql;  
  471.             Type entityType = entity.GetType();  
  472.             DapperPocoInfo dpi = type.GetPocoInfo();  
  473.             if (entityType.IsAssignableFrom(type))  
  474.             {  
  475.                 insertSql = this.GetFullInsertSql(type);  
  476.             }  
  477.             else  
  478.             {  
  479.                 insertSql = this.GetInsertSql(dpi, entityType.GetProperties());  
  480.             }  
  481.   
  482.             if (connection.Execute<T>(insertSql, entity, transaction, commandTimeout) > 0)  
  483.             {  
  484.                 if (entityType.IsAssignableFrom(type) && dpi.IsUnWriteKey())  
  485.                 {  
  486.                     PropertyInfo key = dpi.KeyProperties.First();  
  487.                     if (key.PropertyType != typeof(Guid))  
  488.                     {  
  489.                         var idValue = this.GetLastInsertID(connection, dpi, transaction, commandTimeout);  
  490.                         key.SetValue(entity, idValue, null);  
  491.                     }  
  492.                 }  
  493.                 return true;  
  494.             }  
  495.             return false;  
  496.         }  
  497.   
  498.         public bool UpdateByKey<T>(IDbConnection connection, object entity, IDbTransaction transaction = nullint? commandTimeout = null) where T : class  
  499.         {  
  500.             Type type = typeof(T);  
  501.             string updateSql;  
  502.             Type entityType = entity.GetType();  
  503.             DapperPocoInfo dpi = type.GetPocoInfo();  
  504.             if (entityType.IsAssignableFrom(type))  
  505.             {  
  506.                 updateSql = this.GetFullUpdateByKeySql(type);  
  507.             }  
  508.             else  
  509.             {  
  510.                 updateSql = this.GetUpdateSql(dpi, entityType.GetProperties());  
  511.             }  
  512.             return connection.Execute<T>(updateSql, entity, transaction, commandTimeout) > 0;  
  513.         }  
  514.   
  515.         public bool DeleteByKey<T>(IDbConnection connection, object param, IDbTransaction transaction = nullint? commandTimeout = null) where T : class  
  516.         {  
  517.             string deleteSql = this.GetDeleteByKeySql(typeof(T));  
  518.             return connection.Execute<T>(deleteSql, param, transaction: transaction, commandTimeout: commandTimeout) > 0;  
  519.         }  
  520.   
  521.         public T QueryByKey<T>(IDbConnection connection, object param, IDbTransaction transaction = nullint? commandTimeout = null) where T : class  
  522.         {  
  523.             string querySql = this.GetFullQueryByKeySql(typeof(T));  
  524.             return connection.Query<T>(querySql, param, transaction: transaction, commandTimeout: commandTimeout).FirstOrDefault();  
  525.         }  
  526.   
  527.         private object GetLastInsertID(IDbConnection connection, DapperPocoInfo dpi, IDbTransaction transaction = nullint? commandTimeout = null)  
  528.         {  
  529.             var r = connection.Query("SELECT IDENT_CURRENT('" + dpi.TableName + "') ID", transaction: transaction, commandTimeout: commandTimeout);  
  530.             return Convert.ChangeType(r.First().ID, dpi.KeyProperties.First().PropertyType);  
  531.         }  
  532.   
  533.         public long GetLastInsertID<T>(IDbConnection connection)  
  534.             where T : class  
  535.         {  
  536.             DapperPocoInfo dpi = typeof(T).GetPocoInfo();  
  537.             return Convert.ToInt64(this.GetLastInsertID(connection, dpi));  
  538.         }  
  539.     }  
  540. }  

 

原Dapper部分,在1965行开始做了些修改:

[csharp] view plaincopy
  1. /* 
  2.  License: http://www.apache.org/licenses/LICENSE-2.0  
  3.  Home page: http://code.google.com/p/dapper-dot-net/ 
  4.  
  5.  Note: to build on C# 3.0 + .NET 3.5, include the CSHARP30 compiler symbol (and yes, 
  6.  I know the difference between language and runtime versions; this is a compromise). 
  7.  */  
  8.   
  9. using System;  
  10. using System.Collections;  
  11. using System.Collections.Generic;  
  12. using System.ComponentModel;  
  13. using System.Data;  
  14. using System.Linq;  
  15. using System.Reflection;  
  16. using System.Reflection.Emit;  
  17. using System.Text;  
  18. using System.Threading;  
  19. using System.Text.RegularExpressions;  
  20. using System.Diagnostics;  
  21.   
  22.   
  23. namespace Dapper  
  24. {  
  25.     /// <summary>  
  26.     /// Dapper, a light weight object mapper for ADO.NET  
  27.     /// </summary>  
  28.     static partial class SqlMapper  
  29.     {  
  30.         /// <summary>  
  31.         /// Implement this interface to pass an arbitrary db specific set of parameters to Dapper  
  32.         /// </summary>  
  33.         public partial interface IDynamicParameters  
  34.         {  
  35.             /// <summary>  
  36.             /// Add all the parameters needed to the command just before it executes  
  37.             /// </summary>  
  38.             /// <param name="command">The raw command prior to execution</param>  
  39.             /// <param name="identity">Information about the query</param>  
  40.             void AddParameters(IDbCommand command, Identity identity);  
  41.         }  
  42.   
  43.         /// <summary>  
  44.         /// Implement this interface to pass an arbitrary db specific parameter to Dapper  
  45.         /// </summary>  
  46.         public interface ICustomQueryParameter  
  47.         {  
  48.             /// <summary>  
  49.             /// Add the parameter needed to the command before it executes  
  50.             /// </summary>  
  51.             /// <param name="command">The raw command prior to execution</param>  
  52.             /// <param name="name">Parameter name</param>  
  53.             void AddParameter(IDbCommand command, string name);  
  54.         }  
  55.   
  56.         /// <summary>  
  57.         /// Implement this interface to change default mapping of reader columns to type memebers  
  58.         /// </summary>  
  59.         public interface ITypeMap  
  60.         {  
  61.             /// <summary>  
  62.             /// Finds best constructor  
  63.             /// </summary>  
  64.             /// <param name="names">DataReader column names</param>  
  65.             /// <param name="types">DataReader column types</param>  
  66.             /// <returns>Matching constructor or default one</returns>  
  67.             ConstructorInfo FindConstructor(string[] names, Type[] types);  
  68.   
  69.             /// <summary>  
  70.             /// Gets mapping for constructor parameter  
  71.             /// </summary>  
  72.             /// <param name="constructor">Constructor to resolve</param>  
  73.             /// <param name="columnName">DataReader column name</param>  
  74.             /// <returns>Mapping implementation</returns>  
  75.             IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName);  
  76.   
  77.             /// <summary>  
  78.             /// Gets member mapping for column  
  79.             /// </summary>  
  80.             /// <param name="columnName">DataReader column name</param>  
  81.             /// <returns>Mapping implementation</returns>  
  82.             IMemberMap GetMember(string columnName);  
  83.         }  
  84.   
  85.         /// <summary>  
  86.         /// Implements this interface to provide custom member mapping  
  87.         /// </summary>  
  88.         public interface IMemberMap  
  89.         {  
  90.             /// <summary>  
  91.             /// Source DataReader column name  
  92.             /// </summary>  
  93.             string ColumnName { get; }  
  94.   
  95.             /// <summary>  
  96.             ///  Target member type  
  97.             /// </summary>  
  98.             Type MemberType { get; }  
  99.   
  100.             /// <summary>  
  101.             /// Target property  
  102.             /// </summary>  
  103.             PropertyInfo Property { get; }  
  104.   
  105.             /// <summary>  
  106.             /// Target field  
  107.             /// </summary>  
  108.             FieldInfo Field { get; }  
  109.   
  110.             /// <summary>  
  111.             /// Target constructor parameter  
  112.             /// </summary>  
  113.             ParameterInfo Parameter { get; }  
  114.         }  
  115.   
  116.         static Link<Type, Action<IDbCommand, bool>> bindByNameCache;  
  117.         static Action<IDbCommand, bool> GetBindByName(Type commandType)  
  118.         {  
  119.             if (commandType == nullreturn null// GIGO  
  120.             Action<IDbCommand, bool> action;  
  121.             if (Link<Type, Action<IDbCommand, bool>>.TryGet(bindByNameCache, commandType, out action))  
  122.             {  
  123.                 return action;  
  124.             }  
  125.             var prop = commandType.GetProperty("BindByName", BindingFlags.Public | BindingFlags.Instance);  
  126.             action = null;  
  127.             ParameterInfo[] indexers;  
  128.             MethodInfo setter;  
  129.             if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)  
  130.                 && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)  
  131.                 && (setter = prop.GetSetMethod()) != null  
  132.                 )  
  133.             {  
  134.                 var method = new DynamicMethod(commandType.Name + "_BindByName"nullnew Type[] { typeof(IDbCommand), typeof(bool) });  
  135.                 var il = method.GetILGenerator();  
  136.                 il.Emit(OpCodes.Ldarg_0);  
  137.                 il.Emit(OpCodes.Castclass, commandType);  
  138.                 il.Emit(OpCodes.Ldarg_1);  
  139.                 il.EmitCall(OpCodes.Callvirt, setter, null);  
  140.                 il.Emit(OpCodes.Ret);  
  141.                 action = (Action<IDbCommand, bool>)method.CreateDelegate(typeof(Action<IDbCommand, bool>));  
  142.             }  
  143.             // cache it              
  144.             Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);  
  145.             return action;  
  146.         }  
  147.         /// <summary>  
  148.         /// This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),  
  149.         /// and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**  
  150.         /// equality. The type is fully thread-safe.  
  151.         /// </summary>  
  152.         partial class Link<TKey, TValue> where TKey : class  
  153.         {  
  154.             public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value)  
  155.             {  
  156.                 while (link != null)  
  157.                 {  
  158.                     if ((object)key == (object)link.Key)  
  159.                     {  
  160.                         value = link.Value;  
  161.                         return true;  
  162.                     }  
  163.                     link = link.Tail;  
  164.                 }  
  165.                 value = default(TValue);  
  166.                 return false;  
  167.             }  
  168.             public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue value)  
  169.             {  
  170.                 bool tryAgain;  
  171.                 do  
  172.                 {  
  173.                     var snapshot = Interlocked.CompareExchange(ref head, nullnull);  
  174.                     TValue found;  
  175.                     if (TryGet(snapshot, key, out found))  
  176.                     { // existing match; report the existing value instead  
  177.                         value = found;  
  178.                         return false;  
  179.                     }  
  180.                     var newNode = new Link<TKey, TValue>(key, value, snapshot);  
  181.                     // did somebody move our cheese?  
  182.                     tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot;  
  183.                 } while (tryAgain);  
  184.                 return true;  
  185.             }  
  186.             private Link(TKey key, TValue value, Link<TKey, TValue> tail)  
  187.             {  
  188.                 Key = key;  
  189.                 Value = value;  
  190.                 Tail = tail;  
  191.             }  
  192.             public TKey Key { getprivate set; }  
  193.             public TValue Value { getprivate set; }  
  194.             public Link<TKey, TValue> Tail { getprivate set; }  
  195.         }  
  196.         partial class CacheInfo  
  197.         {  
  198.             public DeserializerState Deserializer { getset; }  
  199.             public Func<IDataReader, object>[] OtherDeserializers { getset; }  
  200.             public Action<IDbCommand, object> ParamReader { getset; }  
  201.             private int hitCount;  
  202.             public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }  
  203.             public void RecordHit() { Interlocked.Increment(ref hitCount); }  
  204.         }  
  205.         static int GetColumnHash(IDataReader reader)  
  206.         {  
  207.             unchecked  
  208.             {  
  209.                 int colCount = reader.FieldCount, hash = colCount;  
  210.                 for (int i = 0; i < colCount; i++)  
  211.                 {   // binding code is only interested in names - not types  
  212.                     object tmp = reader.GetName(i);  
  213.                     hash = (hash * 31) + (tmp == null ? 0 : tmp.GetHashCode());  
  214.                 }  
  215.                 return hash;  
  216.             }  
  217.         }  
  218.         struct DeserializerState  
  219.         {  
  220.             public readonly int Hash;  
  221.             public readonly Func<IDataReader, object> Func;  
  222.   
  223.             public DeserializerState(int hash, Func<IDataReader, object> func)  
  224.             {  
  225.                 Hash = hash;  
  226.                 Func = func;  
  227.             }  
  228.         }  
  229.   
  230.         /// <summary>  
  231.         /// Called if the query cache is purged via PurgeQueryCache  
  232.         /// </summary>  
  233.         public static event EventHandler QueryCachePurged;  
  234.         private static void OnQueryCachePurged()  
  235.         {  
  236.             var handler = QueryCachePurged;  
  237.             if (handler != null) handler(null, EventArgs.Empty);  
  238.         }  
  239. #if CSHARP30  
  240.         private static readonly Dictionary<Identity, CacheInfo> _queryCache = new Dictionary<Identity, CacheInfo>();  
  241.         // note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of  
  242.         // ReaderWriterLockSlim etc; a simple lock is faster  
  243.         private static void SetQueryCache(Identity key, CacheInfo value)  
  244.         {  
  245.             lock (_queryCache) { _queryCache[key] = value; }  
  246.         }  
  247.         private static bool TryGetQueryCache(Identity key, out CacheInfo value)  
  248.         {  
  249.             lock (_queryCache) { return _queryCache.TryGetValue(key, out value); }  
  250.         }  
  251.         private static void PurgeQueryCacheByType(Type type)  
  252.         {  
  253.             lock (_queryCache)  
  254.             {  
  255.                 var toRemove = _queryCache.Keys.Where(id => id.type == type).ToArray();  
  256.                 foreach (var key in toRemove)  
  257.                     _queryCache.Remove(key);  
  258.             }  
  259.         }  
  260.         /// <summary>  
  261.         /// Purge the query cache   
  262.         /// </summary>  
  263.         public static void PurgeQueryCache()  
  264.         {  
  265.             lock (_queryCache)  
  266.             {  
  267.                 _queryCache.Clear();  
  268.             }  
  269.             OnQueryCachePurged();  
  270.         }  
  271. #else  
  272.         static readonly System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo> _queryCache = new System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo>();  
  273.         private static void SetQueryCache(Identity key, CacheInfo value)  
  274.         {  
  275.             if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)  
  276.             {  
  277.                 CollectCacheGarbage();  
  278.             }  
  279.             _queryCache[key] = value;  
  280.         }  
  281.   
  282.         private static void CollectCacheGarbage()  
  283.         {  
  284.             try  
  285.             {  
  286.                 foreach (var pair in _queryCache)  
  287.                 {  
  288.                     if (pair.Value.GetHitCount() <= COLLECT_HIT_COUNT_MIN)  
  289.                     {  
  290.                         CacheInfo cache;  
  291.                         _queryCache.TryRemove(pair.Key, out cache);  
  292.                     }  
  293.                 }  
  294.             }  
  295.   
  296.             finally  
  297.             {  
  298.                 Interlocked.Exchange(ref collect, 0);  
  299.             }  
  300.         }  
  301.   
  302.         private const int COLLECT_PER_ITEMS = 1000, COLLECT_HIT_COUNT_MIN = 0;  
  303.         private static int collect;  
  304.         private static bool TryGetQueryCache(Identity key, out CacheInfo value)  
  305.         {  
  306.             if (_queryCache.TryGetValue(key, out value))  
  307.             {  
  308.                 value.RecordHit();  
  309.                 return true;  
  310.             }  
  311.             value = null;  
  312.             return false;  
  313.         }  
  314.   
  315.         /// <summary>  
  316.         /// Purge the query cache   
  317.         /// </summary>  
  318.         public static void PurgeQueryCache()  
  319.         {  
  320.             _queryCache.Clear();  
  321.             OnQueryCachePurged();  
  322.         }  
  323.   
  324.         private static void PurgeQueryCacheByType(Type type)  
  325.         {  
  326.             foreach (var entry in _queryCache)  
  327.             {  
  328.                 CacheInfo cache;  
  329.                 if (entry.Key.type == type)  
  330.                     _queryCache.TryRemove(entry.Key, out cache);  
  331.             }  
  332.         }  
  333.   
  334.         /// <summary>  
  335.         /// Return a count of all the cached queries by dapper  
  336.         /// </summary>  
  337.         /// <returns></returns>  
  338.         public static int GetCachedSQLCount()  
  339.         {  
  340.             return _queryCache.Count;  
  341.         }  
  342.   
  343.         /// <summary>  
  344.         /// Return a list of all the queries cached by dapper  
  345.         /// </summary>  
  346.         /// <param name="ignoreHitCountAbove"></param>  
  347.         /// <returns></returns>  
  348.         public static IEnumerable<Tuple<stringstringint>> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)  
  349.         {  
  350.             var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount()));  
  351.             if (ignoreHitCountAbove < int.MaxValue) data = data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove);  
  352.             return data;  
  353.         }  
  354.   
  355.         /// <summary>  
  356.         /// Deep diagnostics only: find any hash collisions in the cache  
  357.         /// </summary>  
  358.         /// <returns></returns>  
  359.         public static IEnumerable<Tuple<intint>> GetHashCollissions()  
  360.         {  
  361.             var counts = new Dictionary<intint>();  
  362.             foreach (var key in _queryCache.Keys)  
  363.             {  
  364.                 int count;  
  365.                 if (!counts.TryGetValue(key.hashCode, out count))  
  366.                 {  
  367.                     counts.Add(key.hashCode, 1);  
  368.                 }  
  369.                 else  
  370.                 {  
  371.                     counts[key.hashCode] = count + 1;  
  372.                 }  
  373.             }  
  374.             return from pair in counts  
  375.                    where pair.Value > 1  
  376.                    select Tuple.Create(pair.Key, pair.Value);  
  377.   
  378.         }  
  379. #endif  
  380.   
  381.   
  382.         static readonly Dictionary<Type, DbType> typeMap;  
  383.   
  384.         static SqlMapper()  
  385.         {  
  386.             typeMap = new Dictionary<Type, DbType>();  
  387.             typeMap[typeof(byte)] = DbType.Byte;  
  388.             typeMap[typeof(sbyte)] = DbType.SByte;  
  389.             typeMap[typeof(short)] = DbType.Int16;  
  390.             typeMap[typeof(ushort)] = DbType.UInt16;  
  391.             typeMap[typeof(int)] = DbType.Int32;  
  392.             typeMap[typeof(uint)] = DbType.UInt32;  
  393.             typeMap[typeof(long)] = DbType.Int64;  
  394.             typeMap[typeof(ulong)] = DbType.UInt64;  
  395.             typeMap[typeof(float)] = DbType.Single;  
  396.             typeMap[typeof(double)] = DbType.Double;  
  397.             typeMap[typeof(decimal)] = DbType.Decimal;  
  398.             typeMap[typeof(bool)] = DbType.Boolean;  
  399.             typeMap[typeof(string)] = DbType.String;  
  400.             typeMap[typeof(char)] = DbType.StringFixedLength;  
  401.             typeMap[typeof(Guid)] = DbType.Guid;  
  402.             typeMap[typeof(DateTime)] = DbType.DateTime;  
  403.             typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;  
  404.             typeMap[typeof(TimeSpan)] = DbType.Time;  
  405.             typeMap[typeof(byte[])] = DbType.Binary;  
  406.             typeMap[typeof(byte?)] = DbType.Byte;  
  407.             typeMap[typeof(sbyte?)] = DbType.SByte;  
  408.             typeMap[typeof(short?)] = DbType.Int16;  
  409.             typeMap[typeof(ushort?)] = DbType.UInt16;  
  410.             typeMap[typeof(int?)] = DbType.Int32;  
  411.             typeMap[typeof(uint?)] = DbType.UInt32;  
  412.             typeMap[typeof(long?)] = DbType.Int64;  
  413.             typeMap[typeof(ulong?)] = DbType.UInt64;  
  414.             typeMap[typeof(float?)] = DbType.Single;  
  415.             typeMap[typeof(double?)] = DbType.Double;  
  416.             typeMap[typeof(decimal?)] = DbType.Decimal;  
  417.             typeMap[typeof(bool?)] = DbType.Boolean;  
  418.             typeMap[typeof(char?)] = DbType.StringFixedLength;  
  419.             typeMap[typeof(Guid?)] = DbType.Guid;  
  420.             typeMap[typeof(DateTime?)] = DbType.DateTime;  
  421.             typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;  
  422.             typeMap[typeof(TimeSpan?)] = DbType.Time;  
  423.             typeMap[typeof(Object)] = DbType.Object;  
  424.         }  
  425.         /// <summary>  
  426.         /// Configire the specified type to be mapped to a given db-type  
  427.         /// </summary>  
  428.         public static void AddTypeMap(Type type, DbType dbType)  
  429.         {  
  430.             typeMap[type] = dbType;  
  431.         }  
  432.   
  433.         internal const string LinqBinary = "System.Data.Linq.Binary";  
  434.         internal static DbType LookupDbType(Type type, string name)  
  435.         {  
  436.             DbType dbType;  
  437.             var nullUnderlyingType = Nullable.GetUnderlyingType(type);  
  438.             if (nullUnderlyingType != null) type = nullUnderlyingType;  
  439.             if (type.IsEnum && !typeMap.ContainsKey(type))  
  440.             {  
  441.                 type = Enum.GetUnderlyingType(type);  
  442.             }  
  443.             if (typeMap.TryGetValue(type, out dbType))  
  444.             {  
  445.                 return dbType;  
  446.             }  
  447.             if (type.FullName == LinqBinary)  
  448.             {  
  449.                 return DbType.Binary;  
  450.             }  
  451.             if (typeof(IEnumerable).IsAssignableFrom(type))  
  452.             {  
  453.                 return DynamicParameters.EnumerableMultiParameter;  
  454.             }  
  455.   
  456.   
  457.             throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));  
  458.         }  
  459.   
  460.   
  461.         /// <summary>  
  462.         /// Identity of a cached query in Dapper, used for extensability  
  463.         /// </summary>  
  464.         public partial class Identity : IEquatable<Identity>  
  465.         {  
  466.             internal Identity ForGrid(Type primaryType, int gridIndex)  
  467.             {  
  468.                 return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);  
  469.             }  
  470.   
  471.             internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)  
  472.             {  
  473.                 return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);  
  474.             }  
  475.             /// <summary>  
  476.             /// Create an identity for use with DynamicParameters, internal use only  
  477.             /// </summary>  
  478.             /// <param name="type"></param>  
  479.             /// <returns></returns>  
  480.             public Identity ForDynamicParameters(Type type)  
  481.             {  
  482.                 return new Identity(sql, commandType, connectionString, this.type, type, null, -1);  
  483.             }  
  484.   
  485.             internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)  
  486.                 : this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)  
  487.             { }  
  488.             private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex)  
  489.             {  
  490.                 this.sql = sql;  
  491.                 this.commandType = commandType;  
  492.                 this.connectionString = connectionString;  
  493.                 this.type = type;  
  494.                 this.parametersType = parametersType;  
  495.                 this.gridIndex = gridIndex;  
  496.                 unchecked  
  497.                 {  
  498.                     hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this  
  499.                     hashCode = hashCode * 23 + commandType.GetHashCode();  
  500.                     hashCode = hashCode * 23 + gridIndex.GetHashCode();  
  501.                     hashCode = hashCode * 23 + (sql == null ? 0 : sql.GetHashCode());  
  502.                     hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode());  
  503.                     if (otherTypes != null)  
  504.                     {  
  505.                         foreach (var t in otherTypes)  
  506.                         {  
  507.                             hashCode = hashCode * 23 + (t == null ? 0 : t.GetHashCode());  
  508.                         }  
  509.                     }  
  510.                     hashCode = hashCode * 23 + (connectionString == null ? 0 : SqlMapper.connectionStringComparer.GetHashCode(connectionString));  
  511.                     hashCode = hashCode * 23 + (parametersType == null ? 0 : parametersType.GetHashCode());  
  512.                 }  
  513.             }  
  514.   
  515.             /// <summary>  
  516.             ///   
  517.             /// </summary>  
  518.             /// <param name="obj"></param>  
  519.             /// <returns></returns>  
  520.             public override bool Equals(object obj)  
  521.             {  
  522.                 return Equals(obj as Identity);  
  523.             }  
  524.             /// <summary>  
  525.             /// The sql  
  526.             /// </summary>  
  527.             public readonly string sql;  
  528.             /// <summary>  
  529.             /// The command type   
  530.             /// </summary>  
  531.             public readonly CommandType? commandType;  
  532.   
  533.             /// <summary>  
  534.             ///   
  535.             /// </summary>  
  536.             public readonly int hashCode, gridIndex;  
  537.             /// <summary>  
  538.             ///   
  539.             /// </summary>  
  540.             public readonly Type type;  
  541.             /// <summary>  
  542.             ///   
  543.             /// </summary>  
  544.             public readonly string connectionString;  
  545.             /// <summary>  
  546.             ///   
  547.             /// </summary>  
  548.             public readonly Type parametersType;  
  549.             /// <summary>  
  550.             ///   
  551.             /// </summary>  
  552.             /// <returns></returns>  
  553.             public override int GetHashCode()  
  554.             {  
  555.                 return hashCode;  
  556.             }  
  557.             /// <summary>  
  558.             /// Compare 2 Identity objects  
  559.             /// </summary>  
  560.             /// <param name="other"></param>  
  561.             /// <returns></returns>  
  562.             public bool Equals(Identity other)  
  563.             {  
  564.                 return  
  565.                     other != null &&  
  566.                     gridIndex == other.gridIndex &&  
  567.                     type == other.type &&  
  568.                     sql == other.sql &&  
  569.                     commandType == other.commandType &&  
  570.                     SqlMapper.connectionStringComparer.Equals(connectionString, other.connectionString) &&  
  571.                     parametersType == other.parametersType;  
  572.             }  
  573.         }  
  574.  
  575. #if CSHARP30  
  576.         /// <summary>  
  577.         /// Execute parameterized SQL    
  578.         /// </summary>  
  579.         /// <returns>Number of rows affected</returns>  
  580.         public static int Execute(this IDbConnection cnn, string sql, object param)  
  581.         {  
  582.             return Execute(cnn, sql, param, nullnullnull);  
  583.         }  
  584.   
  585.         /// <summary>  
  586.         /// Execute parameterized SQL  
  587.         /// </summary>  
  588.         /// <returns>Number of rows affected</returns>  
  589.         public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)  
  590.         {  
  591.             return Execute(cnn, sql, param, transaction, nullnull);  
  592.         }  
  593.   
  594.         /// <summary>  
  595.         /// Execute parameterized SQL  
  596.         /// </summary>  
  597.         /// <returns>Number of rows affected</returns>  
  598.         public static int Execute(this IDbConnection cnn, string sql, object param, CommandType commandType)  
  599.         {  
  600.             return Execute(cnn, sql, param, nullnull, commandType);  
  601.         }  
  602.   
  603.         /// <summary>  
  604.         /// Execute parameterized SQL  
  605.         /// </summary>  
  606.         /// <returns>Number of rows affected</returns>  
  607.         public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)  
  608.         {  
  609.             return Execute(cnn, sql, param, transaction, null, commandType);  
  610.         }  
  611.   
  612.         /// <summary>  
  613.         /// Executes a query, returning the data typed as per T  
  614.         /// </summary>  
  615.         /// <returns>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  
  616.         /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).  
  617.         /// </returns>  
  618.         public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param)  
  619.         {  
  620.             return Query<T>(cnn, sql, param, nulltruenullnull);  
  621.         }  
  622.   
  623.         /// <summary>  
  624.         /// Executes a query, returning the data typed as per T  
  625.         /// </summary>  
  626.         /// <returns>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  
  627.         /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).  
  628.         /// </returns>  
  629.         public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)  
  630.         {  
  631.             return Query<T>(cnn, sql, param, transaction, truenullnull);  
  632.         }  
  633.   
  634.         /// <summary>  
  635.         /// Executes a query, returning the data typed as per T  
  636.         /// </summary>  
  637.         /// <returns>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  
  638.         /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).  
  639.         /// </returns>  
  640.         public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param, CommandType commandType)  
  641.         {  
  642.             return Query<T>(cnn, sql, param, nulltruenull, commandType);  
  643.         }  
  644.   
  645.         /// <summary>  
  646.         /// Executes a query, returning the data typed as per T  
  647.         /// </summary>  
  648.         /// <returns>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  
  649.         /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).  
  650.         /// </returns>  
  651.         public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)  
  652.         {  
  653.             return Query<T>(cnn, sql, param, transaction, truenull, commandType);  
  654.         }  
  655.   
  656.         /// <summary>  
  657.         /// Execute a command that returns multiple result sets, and access each in turn  
  658.         /// </summary>  
  659.         public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)  
  660.         {  
  661.             return QueryMultiple(cnn, sql, param, transaction, nullnull);  
  662.         }  
  663.   
  664.         /// <summary>  
  665.         /// Execute a command that returns multiple result sets, and access each in turn  
  666.         /// </summary>  
  667.         public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, CommandType commandType)  
  668.         {  
  669.             return QueryMultiple(cnn, sql, param, nullnull, commandType);  
  670.         }  
  671.   
  672.         /// <summary>  
  673.         /// Execute a command that returns multiple result sets, and access each in turn  
  674.         /// </summary>  
  675.         public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)  
  676.         {  
  677.             return QueryMultiple(cnn, sql, param, transaction, null, commandType);  
  678.         }  
  679. #endif  
  680.   
  681.         private static int Execute(  
  682. #if CSHARP30  
  683. this IDbConnection cnn, string sql,Type type, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType  
  684. #else  
  685. this IDbConnection cnn, string sql, Type type, dynamic param = null, IDbTransaction transaction = nullint? commandTimeout = null, CommandType? commandType = null  
  686. #endif  
  687. )  
  688.         {  
  689.             IEnumerable multiExec = (object)param as IEnumerable;  
  690.             Identity identity;  
  691.             CacheInfo info = null;  
  692.             if (multiExec != null && !(multiExec is string))  
  693.             {  
  694.                 bool isFirst = true;  
  695.                 int total = 0;  
  696.                 using (var cmd = SetupCommand(cnn, transaction, sql, nullnull, commandTimeout, commandType))  
  697.                 {  
  698.   
  699.                     string masterSql = null;  
  700.                     foreach (var obj in multiExec)  
  701.                     {  
  702.                         if (isFirst)  
  703.                         {  
  704.                             masterSql = cmd.CommandText;  
  705.                             isFirst = false;  
  706.                             identity = new Identity(sql, cmd.CommandType, cnn, type, obj.GetType(), null);  
  707.                             info = GetCacheInfo(identity);  
  708.                         }  
  709.                         else  
  710.                         {  
  711.                             cmd.CommandText = masterSql; // because we do magic replaces on "in" etc  
  712.                             cmd.Parameters.Clear(); // current code is Add-tastic  
  713.                         }  
  714.                         info.ParamReader(cmd, obj);  
  715.                         total += cmd.ExecuteNonQuery();  
  716.                     }  
  717.                 }  
  718.                 return total;  
  719.             }  
  720.   
  721.             // nice and simple  
  722.             if ((object)param != null)  
  723.             {  
  724.                 identity = new Identity(sql, commandType, cnn, type, (object)param == null ? null : ((object)param).GetType(), null);  
  725.                 info = GetCacheInfo(identity);  
  726.             }  
  727.             return ExecuteCommand(cnn, transaction, sql, (object)param == null ? null : info.ParamReader, (object)param, commandTimeout, commandType);  
  728.         }  
  729.   
  730.         /// <summary>  
  731.         /// Execute parameterized SQL    
  732.         /// </summary>  
  733.         /// <returns>Number of rows affected</returns>  
  734.         public static int Execute<T>(  
  735. #if CSHARP30  
  736. this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType  
  737. #else  
  738. this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = nullint? commandTimeout = null, CommandType? commandType = null  
  739. #endif  
  740. )  
  741.             where T : class  
  742.         {  
  743.             return Execute(cnn, sql, typeof(T), param, transaction, commandTimeout, commandType);  
  744.         }  
  745.         /// <summary>  
  746.         /// Execute parameterized SQL    
  747.         /// </summary>  
  748.         /// <returns>Number of rows affected</returns>  
  749.         public static int Execute(  
  750. #if CSHARP30  
  751. this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType  
  752. #else  
  753. this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = nullint? commandTimeout = null, CommandType? commandType = null  
  754. #endif  
  755. )  
  756.         {  
  757.             return Execute(cnn, sql, null, param, transaction, commandTimeout, commandType);  
  758.         }  
  759.  
  760. #if !CSHARP30  
  761.         /// <summary>  
  762.         /// Return a list of dynamic objects, reader is closed after the call  
  763.         /// </summary>  
  764.         public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = nullbool buffered = trueint? commandTimeout = null, CommandType? commandType = null)  
  765.         {  
  766.             return Query<DapperRow>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);  
  767.         }  
  768. #else  
  769.         /// <summary>  
  770.         /// Return a list of dynamic objects, reader is closed after the call  
  771.         /// </summary>  
  772.         public static IEnumerable<IDictionary<stringobject>> Query(this IDbConnection cnn, string sql, object param)  
  773.         {  
  774.             return Query(cnn, sql, param, nulltruenullnull);  
  775.         }  
  776.   
  777.         /// <summary>  
  778.         /// Return a list of dynamic objects, reader is closed after the call  
  779.         /// </summary>  
  780.         public static IEnumerable<IDictionary<stringobject>> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)  
  781.         {  
  782.             return Query(cnn, sql, param, transaction, truenullnull);  
  783.         }  
  784.   
  785.         /// <summary>  
  786.         /// Return a list of dynamic objects, reader is closed after the call  
  787.         /// </summary>  
  788.         public static IEnumerable<IDictionary<stringobject>> Query(this IDbConnection cnn, string sql, object param, CommandType? commandType)  
  789.         {  
  790.             return Query(cnn, sql, param, nulltruenull, commandType);  
  791.         }  
  792.   
  793.         /// <summary>  
  794.         /// Return a list of dynamic objects, reader is closed after the call  
  795.         /// </summary>  
  796.         public static IEnumerable<IDictionary<stringobject>> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType? commandType)  
  797.         {  
  798.             return Query(cnn, sql, param, transaction, truenull, commandType);  
  799.         }  
  800.   
  801.         /// <summary>  
  802.         /// Return a list of dynamic objects, reader is closed after the call  
  803.         /// </summary>  
  804.         public static IEnumerable<IDictionary<stringobject>> Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType)  
  805.         {  
  806.             return Query<IDictionary<stringobject>>(cnn, sql, param, transaction, buffered, commandTimeout, commandType);  
  807.         }  
  808. #endif  
  809.   
  810.         /// <summary>  
  811.         /// Executes a query, returning the data typed as per T  
  812.         /// </summary>  
  813.         /// <remarks>the dynamic param may seem a bit odd, but this works around a major usability issue in vs, if it is Object vs completion gets annoying. Eg type new [space] get new object</remarks>  
  814.         /// <returns>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  
  815.         /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).  
  816.         /// </returns>  
  817.         public static IEnumerable<T> Query<T>(  
  818. #if CSHARP30  
  819. this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType  
  820. #else  
  821. this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = nullbool buffered = trueint? commandTimeout = null, CommandType? commandType = null  
  822. #endif  
  823. )  
  824.         {  
  825.             var data = QueryInternal<T>(cnn, sql, param as object, transaction, commandTimeout, commandType);  
  826.             return buffered ? data.ToList() : data;  
  827.         }  
  828.   
  829.         /// <summary>  
  830.         /// Execute a command that returns multiple result sets, and access each in turn  
  831.         /// </summary>  
  832.         public static GridReader QueryMultiple(  
  833. #if CSHARP30  
  834. this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType  
  835. #else  
  836.             this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = nullint? commandTimeout = null, CommandType? commandType = null  
  837. #endif  
  838. )  
  839.         {  
  840.             Identity identity = new Identity(sql, commandType, cnn, typeof(GridReader), (object)param == null ? null : ((object)param).GetType(), null);  
  841.             CacheInfo info = GetCacheInfo(identity);  
  842.   
  843.             IDbCommand cmd = null;  
  844.             IDataReader reader = null;  
  845.             bool wasClosed = cnn.State == ConnectionState.Closed;  
  846.             try  
  847.             {  
  848.                 if (wasClosed) cnn.Open();  
  849.                 cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);  
  850.                 reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);  
  851.   
  852.                 var result = new GridReader(cmd, reader, identity);  
  853.                 wasClosed = false// *if* the connection was closed and we got this far, then we now have a reader  
  854.                 // with the CloseConnection flag, so the reader will deal with the connection; we  
  855.                 // still need something in the "finally" to ensure that broken SQL still results  
  856.                 // in the connection closing itself  
  857.                 return result;  
  858.             }  
  859.             catch  
  860.             {  
  861.                 if (reader != null)  
  862.                 {  
  863.                     if (!reader.IsClosed) try { cmd.Cancel(); }  
  864.                         catch { /* don't spoil the existing exception */ }  
  865.                     reader.Dispose();  
  866.                 }  
  867.                 if (cmd != null) cmd.Dispose();  
  868.                 if (wasClosed) cnn.Close();  
  869.                 throw;  
  870.             }  
  871.         }  
  872.   
  873.         /// <summary>  
  874.         /// Return a typed list of objects, reader is closed after the call  
  875.         /// </summary>  
  876.         private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)  
  877.         {  
  878.             var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);  
  879.             var info = GetCacheInfo(identity);  
  880.   
  881.             IDbCommand cmd = null;  
  882.             IDataReader reader = null;  
  883.   
  884.             bool wasClosed = cnn.State == ConnectionState.Closed;  
  885.             try  
  886.             {  
  887.                 cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType);  
  888.   
  889.                 if (wasClosed) cnn.Open();  
  890.                 reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);  
  891.                 wasClosed = false// *if* the connection was closed and we got this far, then we now have a reader  
  892.                 // with the CloseConnection flag, so the reader will deal with the connection; we  
  893.                 // still need something in the "finally" to ensure that broken SQL still results  
  894.                 // in the connection closing itself  
  895.                 var tuple = info.Deserializer;  
  896.                 int hash = GetColumnHash(reader);  
  897.                 if (tuple.Func == null || tuple.Hash != hash)  
  898.                 {  
  899.                     tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(typeof(T), reader, 0, -1, false));  
  900.                     SetQueryCache(identity, info);  
  901.                 }  
  902.   
  903.                 var func = tuple.Func;  
  904.   
  905.                 while (reader.Read())  
  906.                 {  
  907.                     yield return (T)func(reader);  
  908.                 }  
  909.                 // happy path; close the reader cleanly - no  
  910.                 // need for "Cancel" etc  
  911.                 reader.Dispose();  
  912.                 reader = null;  
  913.             }  
  914.             finally  
  915.             {  
  916.                 if (reader != null)  
  917.                 {  
  918.                     if (!reader.IsClosed) try { cmd.Cancel(); }  
  919.                         catch { /* don't spoil the existing exception */ }  
  920.                     reader.Dispose();  
  921.                 }  
  922.                 if (wasClosed) cnn.Close();  
  923.                 if (cmd != null) cmd.Dispose();  
  924.             }  
  925.         }  
  926.   
  927.         /// <summary>  
  928.         /// Maps a query to objects  
  929.         /// </summary>  
  930.         /// <typeparam name="TFirst">The first type in the recordset</typeparam>  
  931.         /// <typeparam name="TSecond">The second type in the recordset</typeparam>  
  932.         /// <typeparam name="TReturn">The return type</typeparam>  
  933.         /// <param name="cnn"></param>  
  934.         /// <param name="sql"></param>  
  935.         /// <param name="map"></param>  
  936.         /// <param name="param"></param>  
  937.         /// <param name="transaction"></param>  
  938.         /// <param name="buffered"></param>  
  939.         /// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>  
  940.         /// <param name="commandTimeout">Number of seconds before command execution timeout</param>  
  941.         /// <param name="commandType">Is it a stored proc or a batch?</param>  
  942.         /// <returns></returns>  
  943.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(  
  944. #if CSHARP30  
  945. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType  
  946. #else  
  947. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null  
  948. #endif  
  949. )  
  950.         {  
  951.             return MultiMap<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  952.         }  
  953.   
  954.         /// <summary>  
  955.         /// Maps a query to objects  
  956.         /// </summary>  
  957.         /// <typeparam name="TFirst"></typeparam>  
  958.         /// <typeparam name="TSecond"></typeparam>  
  959.         /// <typeparam name="TThird"></typeparam>  
  960.         /// <typeparam name="TReturn"></typeparam>  
  961.         /// <param name="cnn"></param>  
  962.         /// <param name="sql"></param>  
  963.         /// <param name="map"></param>  
  964.         /// <param name="param"></param>  
  965.         /// <param name="transaction"></param>  
  966.         /// <param name="buffered"></param>  
  967.         /// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>  
  968.         /// <param name="commandTimeout">Number of seconds before command execution timeout</param>  
  969.         /// <param name="commandType"></param>  
  970.         /// <returns></returns>  
  971.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(  
  972. #if CSHARP30  
  973. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType  
  974. #else  
  975. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null  
  976. #endif  
  977. )  
  978.         {  
  979.             return MultiMap<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  980.         }  
  981.   
  982.         /// <summary>  
  983.         /// Perform a multi mapping query with 4 input parameters  
  984.         /// </summary>  
  985.         /// <typeparam name="TFirst"></typeparam>  
  986.         /// <typeparam name="TSecond"></typeparam>  
  987.         /// <typeparam name="TThird"></typeparam>  
  988.         /// <typeparam name="TFourth"></typeparam>  
  989.         /// <typeparam name="TReturn"></typeparam>  
  990.         /// <param name="cnn"></param>  
  991.         /// <param name="sql"></param>  
  992.         /// <param name="map"></param>  
  993.         /// <param name="param"></param>  
  994.         /// <param name="transaction"></param>  
  995.         /// <param name="buffered"></param>  
  996.         /// <param name="splitOn"></param>  
  997.         /// <param name="commandTimeout"></param>  
  998.         /// <param name="commandType"></param>  
  999.         /// <returns></returns>  
  1000.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(  
  1001. #if CSHARP30  
  1002. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType  
  1003. #else  
  1004. this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null  
  1005. #endif  
  1006. )  
  1007.         {  
  1008.             return MultiMap<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  1009.         }  
  1010.  
  1011. #if !CSHARP30  
  1012.         /// <summary>  
  1013.         /// Perform a multi mapping query with 5 input parameters  
  1014.         /// </summary>  
  1015.         /// <typeparam name="TFirst"></typeparam>  
  1016.         /// <typeparam name="TSecond"></typeparam>  
  1017.         /// <typeparam name="TThird"></typeparam>  
  1018.         /// <typeparam name="TFourth"></typeparam>  
  1019.         /// <typeparam name="TFifth"></typeparam>  
  1020.         /// <typeparam name="TReturn"></typeparam>  
  1021.         /// <param name="cnn"></param>  
  1022.         /// <param name="sql"></param>  
  1023.         /// <param name="map"></param>  
  1024.         /// <param name="param"></param>  
  1025.         /// <param name="transaction"></param>  
  1026.         /// <param name="buffered"></param>  
  1027.         /// <param name="splitOn"></param>  
  1028.         /// <param name="commandTimeout"></param>  
  1029.         /// <param name="commandType"></param>  
  1030.         /// <returns></returns>  
  1031.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(  
  1032.             this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null  
  1033. )  
  1034.         {  
  1035.             return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  1036.         }  
  1037.   
  1038.         /// <summary>  
  1039.         /// Perform a multi mapping query with 6 input parameters  
  1040.         /// </summary>  
  1041.         /// <typeparam name="TFirst"></typeparam>  
  1042.         /// <typeparam name="TSecond"></typeparam>  
  1043.         /// <typeparam name="TThird"></typeparam>  
  1044.         /// <typeparam name="TFourth"></typeparam>  
  1045.         /// <typeparam name="TFifth"></typeparam>  
  1046.         /// <typeparam name="TSixth"></typeparam>  
  1047.         /// <typeparam name="TReturn"></typeparam>  
  1048.         /// <param name="cnn"></param>  
  1049.         /// <param name="sql"></param>  
  1050.         /// <param name="map"></param>  
  1051.         /// <param name="param"></param>  
  1052.         /// <param name="transaction"></param>  
  1053.         /// <param name="buffered"></param>  
  1054.         /// <param name="splitOn"></param>  
  1055.         /// <param name="commandTimeout"></param>  
  1056.         /// <param name="commandType"></param>  
  1057.         /// <returns></returns>  
  1058.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(  
  1059.             this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null  
  1060. )  
  1061.         {  
  1062.             return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  1063.         }  
  1064.   
  1065.   
  1066.         /// <summary>  
  1067.         /// Perform a multi mapping query with 7 input parameters  
  1068.         /// </summary>  
  1069.         /// <typeparam name="TFirst"></typeparam>  
  1070.         /// <typeparam name="TSecond"></typeparam>  
  1071.         /// <typeparam name="TThird"></typeparam>  
  1072.         /// <typeparam name="TFourth"></typeparam>  
  1073.         /// <typeparam name="TFifth"></typeparam>  
  1074.         /// <typeparam name="TSixth"></typeparam>  
  1075.         /// <typeparam name="TSeventh"></typeparam>  
  1076.         /// <typeparam name="TReturn"></typeparam>  
  1077.         /// <param name="cnn"></param>  
  1078.         /// <param name="sql"></param>  
  1079.         /// <param name="map"></param>  
  1080.         /// <param name="param"></param>  
  1081.         /// <param name="transaction"></param>  
  1082.         /// <param name="buffered"></param>  
  1083.         /// <param name="splitOn"></param>  
  1084.         /// <param name="commandTimeout"></param>  
  1085.         /// <param name="commandType"></param>  
  1086.         /// <returns></returns>  
  1087.         public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, dynamic param = null, IDbTransaction transaction = nullbool buffered = truestring splitOn = "Id"int? commandTimeout = null, CommandType? commandType = null)  
  1088.         {  
  1089.             return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);  
  1090.         }  
  1091. #endif  
  1092.         partial class DontMap { }  
  1093.         static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(  
  1094.             this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType)  
  1095.         {  
  1096.             var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param, transaction, splitOn, commandTimeout, commandType, nullnull);  
  1097.             return buffered ? results.ToList() : results;  
  1098.         }  
  1099.   
  1100.   
  1101.         static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, IDataReader reader, Identity identity)  
  1102.         {  
  1103.             identity = identity ?? new Identity(sql, commandType, cnn, typeof(TFirst), (object)param == null ? null : ((object)param).GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) });  
  1104.             CacheInfo cinfo = GetCacheInfo(identity);  
  1105.   
  1106.             IDbCommand ownedCommand = null;  
  1107.             IDataReader ownedReader = null;  
  1108.   
  1109.             bool wasClosed = cnn != null && cnn.State == ConnectionState.Closed;  
  1110.             try  
  1111.             {  
  1112.                 if (reader == null)  
  1113.                 {  
  1114.                     ownedCommand = SetupCommand(cnn, transaction, sql, cinfo.ParamReader, (object)param, commandTimeout, commandType);  
  1115.                     if (wasClosed) cnn.Open();  
  1116.                     ownedReader = ownedCommand.ExecuteReader();  
  1117.                     reader = ownedReader;  
  1118.                 }  
  1119.                 DeserializerState deserializer = default(DeserializerState);  
  1120.                 Func<IDataReader, object>[] otherDeserializers = null;  
  1121.   
  1122.                 int hash = GetColumnHash(reader);  
  1123.                 if ((deserializer = cinfo.Deserializer).Func == null || (otherDeserializers = cinfo.OtherDeserializers) == null || hash != deserializer.Hash)  
  1124.                 {  
  1125.                     var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) }, splitOn, reader);  
  1126.                     deserializer = cinfo.Deserializer = new DeserializerState(hash, deserializers[0]);  
  1127.                     otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray();  
  1128.                     SetQueryCache(identity, cinfo);  
  1129.                 }  
  1130.   
  1131.                 Func<IDataReader, TReturn> mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(deserializer.Func, otherDeserializers, map);  
  1132.   
  1133.                 if (mapIt != null)  
  1134.                 {  
  1135.                     while (reader.Read())  
  1136.                     {  
  1137.                         yield return mapIt(reader);  
  1138.                     }  
  1139.                 }  
  1140.             }  
  1141.             finally  
  1142.             {  
  1143.                 try  
  1144.                 {  
  1145.                     if (ownedReader != null)  
  1146.                     {  
  1147.                         ownedReader.Dispose();  
  1148.                     }  
  1149.                 }  
  1150.                 finally  
  1151.                 {  
  1152.                     if (ownedCommand != null)  
  1153.                     {  
  1154.                         ownedCommand.Dispose();  
  1155.                     }  
  1156.                     if (wasClosed) cnn.Close();  
  1157.                 }  
  1158.             }  
  1159.         }  
  1160.   
  1161.         private static Func<IDataReader, TReturn> GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Func<IDataReader, object> deserializer, Func<IDataReader, object>[] otherDeserializers, object map)  
  1162.         {  
  1163.             switch (otherDeserializers.Length)  
  1164.             {  
  1165.                 case 1:  
  1166.                     return r => ((Func<TFirst, TSecond, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r));  
  1167.                 case 2:  
  1168.                     return r => ((Func<TFirst, TSecond, TThird, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r));  
  1169.                 case 3:  
  1170.                     return r => ((Func<TFirst, TSecond, TThird, TFourth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r));  
  1171. #if !CSHARP30  
  1172.                 case 4:  
  1173.                     return r => ((Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r));  
  1174.                 case 5:  
  1175.                     return r => ((Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r), (TSixth)otherDeserializers[4](r));  
  1176.                 case 6:  
  1177.                     return r => ((Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r), (TSixth)otherDeserializers[4](r), (TSeventh)otherDeserializers[5](r));  
  1178. #endif  
  1179.                 default:  
  1180.                     throw new NotSupportedException();  
  1181.             }  
  1182.         }  
  1183.   
  1184.         private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)  
  1185.         {  
  1186.             int current = 0;  
  1187.             var splits = splitOn.Split(',').ToArray();  
  1188.             var splitIndex = 0;  
  1189.   
  1190.             Func<Type, int> nextSplit = type =>  
  1191.             {  
  1192.                 var currentSplit = splits[splitIndex].Trim();  
  1193.                 if (splits.Length > splitIndex + 1)  
  1194.                 {  
  1195.                     splitIndex++;  
  1196.                 }  
  1197.   
  1198.                 bool skipFirst = false;  
  1199.                 int startingPos = current + 1;  
  1200.                 // if our current type has the split, skip the first time you see it.   
  1201.                 if (type != typeof(Object))  
  1202.                 {  
  1203.                     var props = DefaultTypeMap.GetSettableProps(type);  
  1204.                     var fields = DefaultTypeMap.GetSettableFields(type);  
  1205.   
  1206.                     foreach (var name in props.Select(p => p.Name).Concat(fields.Select(f => f.Name)))  
  1207.                     {  
  1208.                         if (string.Equals(name, currentSplit, StringComparison.OrdinalIgnoreCase))  
  1209.                         {  
  1210.                             skipFirst = true;  
  1211.                             startingPos = current;  
  1212.                             break;  
  1213.                         }  
  1214.                     }  
  1215.   
  1216.                 }  
  1217.   
  1218.                 int pos;  
  1219.                 for (pos = startingPos; pos < reader.FieldCount; pos++)  
  1220.                 {  
  1221.                     // some people like ID some id ... assuming case insensitive splits for now  
  1222.                     if (splitOn == "*")  
  1223.                     {  
  1224.                         break;  
  1225.                     }  
  1226.                     if (string.Equals(reader.GetName(pos), currentSplit, StringComparison.OrdinalIgnoreCase))  
  1227.                     {  
  1228.                         if (skipFirst)  
  1229.                         {  
  1230.                             skipFirst = false;  
  1231.                         }  
  1232.                         else  
  1233.                         {  
  1234.                             break;  
  1235.                         }  
  1236.                     }  
  1237.                 }  
  1238.                 current = pos;  
  1239.                 return pos;  
  1240.             };  
  1241.   
  1242.             var deserializers = new List<Func<IDataReader, object>>();  
  1243.             int split = 0;  
  1244.             bool first = true;  
  1245.             foreach (var type in types)  
  1246.             {  
  1247.                 if (type != typeof(DontMap))  
  1248.                 {  
  1249.                     int next = nextSplit(type);  
  1250.                     deserializers.Add(GetDeserializer(type, reader, split, next - split, /* returnNullIfFirstMissing: */ !first));  
  1251.                     first = false;  
  1252.                     split = next;  
  1253.                 }  
  1254.             }  
  1255.   
  1256.             return deserializers.ToArray();  
  1257.         }  
  1258.   
  1259.         private static CacheInfo GetCacheInfo(Identity identity)  
  1260.         {  
  1261.             CacheInfo info;  
  1262.             if (!TryGetQueryCache(identity, out info))  
  1263.             {  
  1264.                 info = new CacheInfo();  
  1265.                 if (identity.parametersType != null)  
  1266.                 {  
  1267.                     if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))  
  1268.                     {  
  1269.                         info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd, identity); };  
  1270.                     }  
  1271. #if !CSHARP30  
  1272.                     else if (typeof(IEnumerable<KeyValuePair<stringobject>>).IsAssignableFrom(identity.parametersType) && typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(identity.parametersType))  
  1273.                     {  
  1274.                         info.ParamReader = (cmd, obj) =>  
  1275.                         {  
  1276.                             IDynamicParameters mapped = new DynamicParameters(obj);  
  1277.                             mapped.AddParameters(cmd, identity);  
  1278.                         };  
  1279.                     }  
  1280. #endif  
  1281.                     else  
  1282.                     {  
  1283.                         info.ParamReader = CreateParamInfoGenerator(identity, falsetrue);  
  1284.                     }  
  1285.                 }  
  1286.                 SetQueryCache(identity, info);  
  1287.             }  
  1288.             return info;  
  1289.         }  
  1290.   
  1291.         private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)  
  1292.         {  
  1293. #if !CSHARP30  
  1294.             // dynamic is passed in as Object ... by c# design  
  1295.             if (type == typeof(object)  
  1296.                 || type == typeof(DapperRow))  
  1297.             {  
  1298.                 return GetDapperRowDeserializer(reader, startBound, length, returnNullIfFirstMissing);  
  1299.             }  
  1300. #else  
  1301.             if (type.IsAssignableFrom(typeof(Dictionary<stringobject>)))  
  1302.             {  
  1303.                 return GetDictionaryDeserializer(reader, startBound, length, returnNullIfFirstMissing);  
  1304.             }  
  1305. #endif  
  1306.             Type underlyingType = null;  
  1307.             if (!(typeMap.ContainsKey(type) || type.IsEnum || type.FullName == LinqBinary ||  
  1308.                 (type.IsValueType && (underlyingType = Nullable.GetUnderlyingType(type)) != null && underlyingType.IsEnum)))  
  1309.             {  
  1310.                 return GetTypeDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);  
  1311.             }  
  1312.             return GetStructDeserializer(type, underlyingType ?? type, startBound);  
  1313.   
  1314.         }  
  1315.  
  1316. #if !CSHARP30  
  1317.         private sealed partial class DapperTable  
  1318.         {  
  1319.             string[] fieldNames;  
  1320.             readonly Dictionary<stringint> fieldNameLookup;  
  1321.   
  1322.             internal string[] FieldNames { get { return fieldNames; } }  
  1323.   
  1324.             public DapperTable(string[] fieldNames)  
  1325.             {  
  1326.                 if (fieldNames == nullthrow new ArgumentNullException("fieldNames");  
  1327.                 this.fieldNames = fieldNames;  
  1328.   
  1329.                 fieldNameLookup = new Dictionary<stringint>(fieldNames.Length, StringComparer.Ordinal);  
  1330.                 // if there are dups, we want the **first** key to be the "winner" - so iterate backwards  
  1331.                 for (int i = fieldNames.Length - 1; i >= 0; i--)  
  1332.                 {  
  1333.                     string key = fieldNames[i];  
  1334.                     if (key != null) fieldNameLookup[key] = i;  
  1335.                 }  
  1336.             }  
  1337.   
  1338.             internal int IndexOfName(string name)  
  1339.             {  
  1340.                 int result;  
  1341.                 return (name != null && fieldNameLookup.TryGetValue(name, out result)) ? result : -1;  
  1342.             }  
  1343.             internal int AddField(string name)  
  1344.             {  
  1345.                 if (name == nullthrow new ArgumentNullException("name");  
  1346.                 if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);  
  1347.                 int oldLen = fieldNames.Length;  
  1348.                 Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case  
  1349.                 fieldNames[oldLen] = name;  
  1350.                 fieldNameLookup[name] = oldLen;  
  1351.                 return oldLen;  
  1352.             }  
  1353.   
  1354.   
  1355.             internal bool FieldExists(string key)  
  1356.             {  
  1357.                 return key != null && fieldNameLookup.ContainsKey(key);  
  1358.             }  
  1359.   
  1360.             public int FieldCount { get { return fieldNames.Length; } }  
  1361.         }  
  1362.   
  1363.         sealed partial class DapperRowMetaObject : System.Dynamic.DynamicMetaObject  
  1364.         {  
  1365.             static readonly MethodInfo getValueMethod = typeof(IDictionary<stringobject>).GetProperty("Item").GetGetMethod();  
  1366.             static readonly MethodInfo setValueMethod = typeof(DapperRow).GetMethod("SetValue"new Type[] { typeof(string), typeof(object) });  
  1367.   
  1368.             public DapperRowMetaObject(  
  1369.                 System.Linq.Expressions.Expression expression,  
  1370.                 System.Dynamic.BindingRestrictions restrictions  
  1371.                 )  
  1372.                 : base(expression, restrictions)  
  1373.             {  
  1374.             }  
  1375.   
  1376.             public DapperRowMetaObject(  
  1377.                 System.Linq.Expressions.Expression expression,  
  1378.                 System.Dynamic.BindingRestrictions restrictions,  
  1379.                 object value  
  1380.                 )  
  1381.                 : base(expression, restrictions, value)  
  1382.             {  
  1383.             }  
  1384.   
  1385.             System.Dynamic.DynamicMetaObject CallMethod(  
  1386.                 MethodInfo method,  
  1387.                 System.Linq.Expressions.Expression[] parameters  
  1388.                 )  
  1389.             {  
  1390.                 var callMethod = new System.Dynamic.DynamicMetaObject(  
  1391.                     System.Linq.Expressions.Expression.Call(  
  1392.                         System.Linq.Expressions.Expression.Convert(Expression, LimitType),  
  1393.                         method,  
  1394.                         parameters),  
  1395.                     System.Dynamic.BindingRestrictions.GetTypeRestriction(Expression, LimitType)  
  1396.                     );  
  1397.                 return callMethod;  
  1398.             }  
  1399.   
  1400.             public override System.Dynamic.DynamicMetaObject BindGetMember(System.Dynamic.GetMemberBinder binder)  
  1401.             {  
  1402.                 var parameters = new System.Linq.Expressions.Expression[]  
  1403.                                      {  
  1404.                                          System.Linq.Expressions.Expression.Constant(binder.Name)  
  1405.                                      };  
  1406.   
  1407.                 var callMethod = CallMethod(getValueMethod, parameters);  
  1408.   
  1409.                 return callMethod;  
  1410.             }  
  1411.   
  1412.             // Needed for Visual basic dynamic support  
  1413.             public override System.Dynamic.DynamicMetaObject BindInvokeMember(System.Dynamic.InvokeMemberBinder binder, System.Dynamic.DynamicMetaObject[] args)  
  1414.             {  
  1415.                 var parameters = new System.Linq.Expressions.Expression[]  
  1416.                                      {  
  1417.                                          System.Linq.Expressions.Expression.Constant(binder.Name)  
  1418.                                      };  
  1419.   
  1420.                 var callMethod = CallMethod(getValueMethod, parameters);  
  1421.   
  1422.                 return callMethod;  
  1423.             }  
  1424.   
  1425.             public override System.Dynamic.DynamicMetaObject BindSetMember(System.Dynamic.SetMemberBinder binder, System.Dynamic.DynamicMetaObject value)  
  1426.             {  
  1427.                 var parameters = new System.Linq.Expressions.Expression[]  
  1428.                                      {  
  1429.                                          System.Linq.Expressions.Expression.Constant(binder.Name),  
  1430.                                          value.Expression,  
  1431.                                      };  
  1432.   
  1433.                 var callMethod = CallMethod(setValueMethod, parameters);  
  1434.   
  1435.                 return callMethod;  
  1436.             }  
  1437.         }  
  1438.   
  1439.         private sealed partial class DapperRow  
  1440.             : System.Dynamic.IDynamicMetaObjectProvider  
  1441.             , IDictionary<stringobject>  
  1442.         {  
  1443.             readonly DapperTable table;  
  1444.             object[] values;  
  1445.   
  1446.             public DapperRow(DapperTable table, object[] values)  
  1447.             {  
  1448.                 if (table == nullthrow new ArgumentNullException("table");  
  1449.                 if (values == nullthrow new ArgumentNullException("values");  
  1450.                 this.table = table;  
  1451.                 this.values = values;  
  1452.             }  
  1453.             private sealed class DeadValue  
  1454.             {  
  1455.                 public static readonly DeadValue Default = new DeadValue();  
  1456.                 private DeadValue() { }  
  1457.             }  
  1458.             int ICollection<KeyValuePair<stringobject>>.Count  
  1459.             {  
  1460.                 get  
  1461.                 {  
  1462.                     int count = 0;  
  1463.                     for (int i = 0; i < values.Length; i++)  
  1464.                     {  
  1465.                         if (!(values[i] is DeadValue)) count++;  
  1466.                     }  
  1467.                     return count;  
  1468.                 }  
  1469.             }  
  1470.   
  1471.             public bool TryGetValue(string name, out object value)  
  1472.             {  
  1473.                 var index = table.IndexOfName(name);  
  1474.                 if (index < 0)  
  1475.                 { // doesn't exist  
  1476.                     value = null;  
  1477.                     return false;  
  1478.                 }  
  1479.                 // exists, **even if** we don't have a value; consider table rows heterogeneous  
  1480.                 value = index < values.Length ? values[index] : null;  
  1481.                 if (value is DeadValue)  
  1482.                 { // pretend it isn't here  
  1483.                     value = null;  
  1484.                     return false;  
  1485.                 }  
  1486.                 return true;  
  1487.             }  
  1488.   
  1489.             public override string ToString()  
  1490.             {  
  1491.                 var sb = new StringBuilder("{DapperRow");  
  1492.                 foreach (var kv in this)  
  1493.                 {  
  1494.                     var value = kv.Value;  
  1495.                     sb.Append(", ").Append(kv.Key);  
  1496.                     if (value != null)  
  1497.                     {  
  1498.                         sb.Append(" = '").Append(kv.Value).Append('\'');  
  1499.                     }  
  1500.                     else  
  1501.                     {  
  1502.                         sb.Append(" = NULL");  
  1503.                     }  
  1504.                 }  
  1505.   
  1506.                 return sb.Append('}').ToString();  
  1507.             }  
  1508.   
  1509.             System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(  
  1510.                 System.Linq.Expressions.Expression parameter)  
  1511.             {  
  1512.                 return new DapperRowMetaObject(parameter, System.Dynamic.BindingRestrictions.Empty, this);  
  1513.             }  
  1514.   
  1515.             public IEnumerator<KeyValuePair<stringobject>> GetEnumerator()  
  1516.             {  
  1517.                 var names = table.FieldNames;  
  1518.                 for (var i = 0; i < names.Length; i++)  
  1519.                 {  
  1520.                     object value = i < values.Length ? values[i] : null;  
  1521.                     if (!(value is DeadValue))  
  1522.                     {  
  1523.                         yield return new KeyValuePair<stringobject>(names[i], value);  
  1524.                     }  
  1525.                 }  
  1526.             }  
  1527.   
  1528.             IEnumerator IEnumerable.GetEnumerator()  
  1529.             {  
  1530.                 return GetEnumerator();  
  1531.             }  
  1532.  
  1533.         #region Implementation of ICollection<KeyValuePair<string,object>>  
  1534.   
  1535.             void ICollection<KeyValuePair<stringobject>>.Add(KeyValuePair<stringobject> item)  
  1536.             {  
  1537.                 IDictionary<stringobject> dic = this;  
  1538.                 dic.Add(item.Key, item.Value);  
  1539.             }  
  1540.   
  1541.             void ICollection<KeyValuePair<stringobject>>.Clear()  
  1542.             { // removes values for **this row**, but doesn't change the fundamental table  
  1543.                 for (int i = 0; i < values.Length; i++)  
  1544.                     values[i] = DeadValue.Default;  
  1545.             }  
  1546.   
  1547.             bool ICollection<KeyValuePair<stringobject>>.Contains(KeyValuePair<stringobject> item)  
  1548.             {  
  1549.                 object value;  
  1550.                 return TryGetValue(item.Key, out value) && Equals(value, item.Value);  
  1551.             }  
  1552.   
  1553.             void ICollection<KeyValuePair<stringobject>>.CopyTo(KeyValuePair<stringobject>[] array, int arrayIndex)  
  1554.             {  
  1555.                 foreach (var kv in this)  
  1556.                 {  
  1557.                     array[arrayIndex++] = kv; // if they didn't leave enough space; not our fault  
  1558.                 }  
  1559.             }  
  1560.   
  1561.             bool ICollection<KeyValuePair<stringobject>>.Remove(KeyValuePair<stringobject> item)  
  1562.             {  
  1563.                 IDictionary<stringobject> dic = this;  
  1564.                 return dic.Remove(item.Key);  
  1565.             }  
  1566.   
  1567.             bool ICollection<KeyValuePair<stringobject>>.IsReadOnly  
  1568.             {  
  1569.                 get { return false; }  
  1570.             }  
  1571.  
  1572.             #endregion  
  1573.  
  1574.         #region Implementation of IDictionary<string,object>  
  1575.   
  1576.             bool IDictionary<stringobject>.ContainsKey(string key)  
  1577.             {  
  1578.                 int index = table.IndexOfName(key);  
  1579.                 if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;  
  1580.                 return true;  
  1581.             }  
  1582.   
  1583.             void IDictionary<stringobject>.Add(string key, object value)  
  1584.             {  
  1585.                 SetValue(key, value, true);  
  1586.             }  
  1587.   
  1588.             bool IDictionary<stringobject>.Remove(string key)  
  1589.             {  
  1590.                 int index = table.IndexOfName(key);  
  1591.                 if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;  
  1592.                 values[index] = DeadValue.Default;  
  1593.                 return true;  
  1594.             }  
  1595.   
  1596.             object IDictionary<stringobject>.this[string key]  
  1597.             {  
  1598.                 get { object val; TryGetValue(key, out val); return val; }  
  1599.                 set { SetValue(key, value, false); }  
  1600.             }  
  1601.   
  1602.             public object SetValue(string key, object value)  
  1603.             {  
  1604.                 return SetValue(key, value, false);  
  1605.             }  
  1606.             private object SetValue(string key, object value, bool isAdd)  
  1607.             {  
  1608.                 if (key == nullthrow new ArgumentNullException("key");  
  1609.                 int index = table.IndexOfName(key);  
  1610.                 if (index < 0)  
  1611.                 {  
  1612.                     index = table.AddField(key);  
  1613.                 }  
  1614.                 else if (isAdd && index < values.Length && !(values[index] is DeadValue))  
  1615.                 {  
  1616.                     // then semantically, this value already exists  
  1617.                     throw new ArgumentException("An item with the same key has already been added""key");  
  1618.                 }  
  1619.                 int oldLength = values.Length;  
  1620.                 if (oldLength <= index)  
  1621.                 {  
  1622.                     // we'll assume they're doing lots of things, and  
  1623.                     // grow it to the full width of the table  
  1624.                     Array.Resize(ref values, table.FieldCount);  
  1625.                     for (int i = oldLength; i < values.Length; i++)  
  1626.                     {  
  1627.                         values[i] = DeadValue.Default;  
  1628.                     }  
  1629.                 }  
  1630.                 return values[index] = value;  
  1631.             }  
  1632.   
  1633.             ICollection<string> IDictionary<stringobject>.Keys  
  1634.             {  
  1635.                 get { return this.Select(kv => kv.Key).ToArray(); }  
  1636.             }  
  1637.   
  1638.             ICollection<object> IDictionary<stringobject>.Values  
  1639.             {  
  1640.                 get { return this.Select(kv => kv.Value).ToArray(); }  
  1641.             }  
  1642.  
  1643.             #endregion  
  1644.         }  
  1645. #endif  
  1646.         private const string MultiMapSplitExceptionMessage = "When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id";  
  1647. #if !CSHARP30  
  1648.         internal static Func<IDataReader, object> GetDapperRowDeserializer(IDataRecord reader, int startBound, int length, bool returnNullIfFirstMissing)  
  1649.         {  
  1650.             var fieldCount = reader.FieldCount;  
  1651.             if (length == -1)  
  1652.             {  
  1653.                 length = fieldCount - startBound;  
  1654.             }  
  1655.   
  1656.             if (fieldCount <= startBound)  
  1657.             {  
  1658.                 throw new ArgumentException(MultiMapSplitExceptionMessage, "splitOn");  
  1659.             }  
  1660.   
  1661.             var effectiveFieldCount = Math.Min(fieldCount - startBound, length);  
  1662.   
  1663.             DapperTable table = null;  
  1664.   
  1665.             return  
  1666.                 r =>  
  1667.                 {  
  1668.                     if (table == null)  
  1669.                     {  
  1670.                         string[] names = new string[effectiveFieldCount];  
  1671.                         for (int i = 0; i < effectiveFieldCount; i++)  
  1672.                         {  
  1673.                             names[i] = r.GetName(i + startBound);  
  1674.                         }  
  1675.                         table = new DapperTable(names);  
  1676.                     }  
  1677.   
  1678.                     var values = new object[effectiveFieldCount];  
  1679.   
  1680.                     if (returnNullIfFirstMissing)  
  1681.                     {  
  1682.                         values[0] = r.GetValue(startBound);  
  1683.                         if (values[0] is DBNull)  
  1684.                         {  
  1685.                             return null;  
  1686.                         }  
  1687.                     }  
  1688.   
  1689.                     if (startBound == 0)  
  1690.                     {  
  1691.                         r.GetValues(values);  
  1692.                         for (int i = 0; i < values.Length; i++)  
  1693.                             if (values[i] is DBNull) values[i] = null;  
  1694.                     }  
  1695.                     else  
  1696.                     {  
  1697.                         var begin = returnNullIfFirstMissing ? 1 : 0;  
  1698.                         for (var iter = begin; iter < effectiveFieldCount; ++iter)  
  1699.                         {  
  1700.                             object obj = r.GetValue(iter + startBound);  
  1701.                             values[iter] = obj is DBNull ? null : obj;  
  1702.                         }  
  1703.                     }  
  1704.                     return new DapperRow(table, values);  
  1705.                 };  
  1706.         }  
  1707. #else  
  1708.         internal static Func<IDataReader, object> GetDictionaryDeserializer(IDataRecord reader, int startBound, int length, bool returnNullIfFirstMissing)  
  1709.         {  
  1710.             var fieldCount = reader.FieldCount;  
  1711.             if (length == -1)  
  1712.             {  
  1713.                 length = fieldCount - startBound;  
  1714.             }  
  1715.   
  1716.             if (fieldCount <= startBound)  
  1717.             {  
  1718.                 throw new ArgumentException(MultiMapSplitExceptionMessage, "splitOn");  
  1719.             }  
  1720.   
  1721.             return  
  1722.                  r =>  
  1723.                  {  
  1724.                      IDictionary<stringobject> row = new Dictionary<stringobject>(length);  
  1725.                      for (var i = startBound; i < startBound + length; i++)  
  1726.                      {  
  1727.                          var tmp = r.GetValue(i);  
  1728.                          tmp = tmp == DBNull.Value ? null : tmp;  
  1729.                          row[r.GetName(i)] = tmp;  
  1730.                          if (returnNullIfFirstMissing && i == startBound && tmp == null)  
  1731.                          {  
  1732.                              return null;  
  1733.                          }  
  1734.                      }  
  1735.                      return row;  
  1736.                  };  
  1737.         }  
  1738. #endif  
  1739.         /// <summary>  
  1740.         /// Internal use only  
  1741.         /// </summary>  
  1742.         /// <param name="value"></param>  
  1743.         /// <returns></returns>  
  1744.         [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
  1745.         [Obsolete("This method is for internal usage only"false)]  
  1746.         public static char ReadChar(object value)  
  1747.         {  
  1748.             if (value == null || value is DBNull) throw new ArgumentNullException("value");  
  1749.             string s = value as string;  
  1750.             if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected""value");  
  1751.             return s[0];  
  1752.         }  
  1753.   
  1754.         [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
  1755.         [Obsolete("This method is for internal usage only"false)]  
  1756.         public static bool ReadBool(object value)  
  1757.         {  
  1758.             if (value.GetType() != typeof(bool))  
  1759.             {  
  1760.                 return Convert.ToBoolean(value);  
  1761.             }  
  1762.             else  
  1763.                 return (bool)value;  
  1764.         }  
  1765.   
  1766.         /// <summary>  
  1767.         /// Internal use only  
  1768.         /// </summary>  
  1769.         [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
  1770.         [Obsolete("This method is for internal usage only"false)]  
  1771.         public static char? ReadNullableChar(object value)  
  1772.         {  
  1773.             if (value == null || value is DBNull) return null;  
  1774.             string s = value as string;  
  1775.             if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected""value");  
  1776.             return s[0];  
  1777.         }  
  1778.   
  1779.   
  1780.         /// <summary>  
  1781.         /// Internal use only  
  1782.         /// </summary>  
  1783.         [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
  1784.         [Obsolete("This method is for internal usage only"true)]  
  1785.         public static IDbDataParameter FindOrAddParameter(IDataParameterCollection parameters, IDbCommand command, string name)  
  1786.         {  
  1787.             IDbDataParameter result;  
  1788.             if (parameters.Contains(name))  
  1789.             {  
  1790.                 result = (IDbDataParameter)parameters[name];  
  1791.             }  
  1792.             else  
  1793.             {  
  1794.                 result = command.CreateParameter();  
  1795.                 result.ParameterName = name;  
  1796.                 parameters.Add(result);  
  1797.             }  
  1798.             return result;  
  1799.         }  
  1800.   
  1801.         /// <summary>  
  1802.         /// Internal use only  
  1803.         /// </summary>  
  1804.         [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]  
  1805.         [Obsolete("This method is for internal usage only"false)]  
  1806.         public static void PackListParameters(IDbCommand command, string namePrefix, object value)  
  1807.         {  
  1808.             // initially we tried TVP, however it performs quite poorly.  
  1809.             // keep in mind SQL support up to 2000 params easily in sp_executesql, needing more is rare  
  1810.   
  1811.             var list = value as IEnumerable;  
  1812.             var count = 0;  
  1813.   
  1814.             if (list != null)  
  1815.             {  
  1816.                 if (FeatureSupport.Get(command.Connection).Arrays)  
  1817.                 {  
  1818.                     var arrayParm = command.CreateParameter();  
  1819.                     arrayParm.Value = list;  
  1820.                     arrayParm.ParameterName = namePrefix;  
  1821.                     command.Parameters.Add(arrayParm);  
  1822.                 }  
  1823.                 else  
  1824.                 {  
  1825.                     bool isString = value is IEnumerable<string>;  
  1826.                     bool isDbString = value is IEnumerable<DbString>;  
  1827.                     foreach (var item in list)  
  1828.                     {  
  1829.                         count++;  
  1830.                         var listParam = command.CreateParameter();  
  1831.                         listParam.ParameterName = namePrefix + count;  
  1832.                         listParam.Value = item ?? DBNull.Value;  
  1833.                         if (isString)  
  1834.                         {  
  1835.                             listParam.Size = 4000;  
  1836.                             if (item != null && ((string)item).Length > 4000)  
  1837.                             {  
  1838.                                 listParam.Size = -1;  
  1839.                             }  
  1840.                         }  
  1841.                         if (isDbString && item as DbString != null)  
  1842.                         {  
  1843.                             var str = item as DbString;  
  1844.                             str.AddParameter(command, listParam.ParameterName);  
  1845.                         }  
  1846.                         else  
  1847.                         {  
  1848.                             command.Parameters.Add(listParam);  
  1849.                         }  
  1850.                     }  
  1851.   
  1852.                     if (count == 0)  
  1853.                     {  
  1854.                         command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), "(SELECT NULL WHERE 1 = 0)");  
  1855.                     }  
  1856.                     else  
  1857.                     {  
  1858.                         command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), match =>  
  1859.                         {  
  1860.                             var grp = match.Value;  
  1861.                             var sb = new StringBuilder("(").Append(grp).Append(1);  
  1862.                             for (int i = 2; i <= count; i++)  
  1863.                             {  
  1864.                                 sb.Append(',').Append(grp).Append(i);  
  1865.                             }  
  1866.                             return sb.Append(')').ToString();  
  1867.                         });  
  1868.                     }  
  1869.                 }  
  1870.             }  
  1871.   
  1872.         }  
  1873.   
  1874.         private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyInfo> parameters, string sql)  
  1875.         {  
  1876.             return parameters.Where(p => Regex.IsMatch(sql, @"[?@:]" + p.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline));  
  1877.         }  
  1878.   
  1879.   
  1880.         // look for ? / @ / : *by itself*  
  1881.         static readonly Regex smellsLikeOleDb = new Regex(@"(?<![a-zA-Z0-9_])[?@:](?![a-zA-Z0-9_])", RegexOptions.Compiled);  
  1882.           
  1883.         /// <summary>  
  1884.         /// Internal use only  
  1885.         /// </summary>  
  1886.         public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused)  
  1887.         {  
  1888.             Type type = identity.parametersType;  
  1889.               
  1890.             bool filterParams = false;  
  1891.             if (removeUnused && identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text)  
  1892.             {  
  1893.                 filterParams = !smellsLikeOleDb.IsMatch(identity.sql);  
  1894.             }  
  1895.             var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), nullnew[] { typeof(IDbCommand), typeof(object) }, type, true);  
  1896.   
  1897.             var il = dm.GetILGenerator();  
  1898.   
  1899.             il.DeclareLocal(type); // 0  
  1900.             bool haveInt32Arg1 = false;  
  1901.             il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]  
  1902.             il.Emit(OpCodes.Unbox_Any, type); // stack is now [typed-param]  
  1903.             il.Emit(OpCodes.Stloc_0);// stack is now empty  
  1904.   
  1905.             il.Emit(OpCodes.Ldarg_0); // stack is now [command]  
  1906.             il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); // stack is now [parameters]  
  1907.   
  1908.             var propsArr = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0).ToArray();  
  1909.             var ctors = type.GetConstructors();  
  1910.             ParameterInfo[] ctorParams;  
  1911.             IEnumerable<PropertyInfo> props = null;  
  1912.             // try to detect tuple patterns, e.g. anon-types, and use that to choose the order  
  1913.             // otherwise: alphabetical  
  1914.             if (ctors.Length == 1 && propsArr.Length == (ctorParams = ctors[0].GetParameters()).Length)  
  1915.             {  
  1916.                 // check if reflection was kind enough to put everything in the right order for us  
  1917.                 bool ok = true;  
  1918.                 for (int i = 0; i < propsArr.Length; i++)  
  1919.                 {  
  1920.                     if (!string.Equals(propsArr[i].Name, ctorParams[i].Name, StringComparison.InvariantCultureIgnoreCase))  
  1921.                     {  
  1922.                         ok = false;  
  1923.                         break;  
  1924.                     }  
  1925.                 }  
  1926.                 if(ok)  
  1927.                 {  
  1928.                     // pre-sorted; the reflection gods have smiled upon us  
  1929.                     props = propsArr;  
  1930.                 }  
  1931.                 else { // might still all be accounted for; check the hard way  
  1932.                     var positionByName = new Dictionary<string,int>(StringComparer.InvariantCultureIgnoreCase);  
  1933.                     foreach(var param in ctorParams)  
  1934.                     {  
  1935.                         positionByName[param.Name] = param.Position;  
  1936.                     }  
  1937.                     if (positionByName.Count == propsArr.Length)  
  1938.                     {  
  1939.                         int[] positions = new int[propsArr.Length];  
  1940.                         ok = true;  
  1941.                         for (int i = 0; i < propsArr.Length; i++)  
  1942.                         {  
  1943.                             int pos;  
  1944.                             if (!positionByName.TryGetValue(propsArr[i].Name, out pos))  
  1945.                             {  
  1946.                                 ok = false;  
  1947.                                 break;  
  1948.                             }  
  1949.                             positions[i] = pos;  
  1950.                         }  
  1951.                         if (ok)  
  1952.                         {  
  1953.                             Array.Sort(positions, propsArr);  
  1954.                             props = propsArr;  
  1955.                         }  
  1956.                     }  
  1957.                 }  
  1958.             }  
  1959.             if(props == null) props = propsArr.OrderBy(x => x.Name);  
  1960.             if (filterParams)  
  1961.             {  
  1962.                 props = FilterParameters(props, identity.sql);  
  1963.             }  
  1964.   
  1965.             DapperPocoInfo dpi = null;  
  1966.             if (identity.type != null)  
  1967.             {  
  1968.                 dpi = identity.type.GetPocoInfo();  
  1969.             }  
  1970.   
  1971.             foreach (var prop in props)  
  1972.             {  
  1973.                 if (filterParams)  
  1974.                 {  
  1975.                     if (identity.sql.IndexOf("@" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0  
  1976.                         && identity.sql.IndexOf(":" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0  
  1977.                         && identity.sql.IndexOf("?" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0)  
  1978.                     { // can't see the parameter in the text (even in a comment, etc) - burn it with fire  
  1979.                         continue;  
  1980.                     }  
  1981.                 }  
  1982.                 if (typeof(ICustomQueryParameter).IsAssignableFrom(prop.PropertyType))  
  1983.                 {  
  1984.                     il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]  
  1985.                     il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [dbstring]  
  1986.                     il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [dbstring] [command]  
  1987.                     il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [dbstring] [command] [name]  
  1988.                     il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod("AddParameter"), null); // stack is now [parameters]  
  1989.                     continue;  
  1990.                 }  
  1991.                 DbType dbType = LookupDbType(prop.PropertyType, prop.Name);  
  1992.                 KeyValuePair<DbType, int>? kvp = null;  
  1993.                 if (dbType == DbType.String && dpi != null)//默认所有字符串在Dapper中被param成 DbType.String  
  1994.                 {  
  1995.                     kvp = dpi.GetStringColumnMap(prop.Name);  
  1996.                 }  
  1997.                 if (dbType == DynamicParameters.EnumerableMultiParameter)  
  1998.                 {  
  1999.                     // this actually represents special handling for list types;  
  2000.                     il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]  
  2001.                     il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]  
  2002.                     il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]  
  2003.                     il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]  
  2004.                     if (prop.PropertyType.IsValueType)  
  2005.                     {  
  2006.                         il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]  
  2007.                     }  
  2008.                     il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]  
  2009.                     continue;  
  2010.                 }  
  2011.                 il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters]  
  2012.   
  2013.                 il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]  
  2014.   
  2015.                 if (checkForDuplicates)  
  2016.                 {  
  2017.                     // need to be a little careful about adding; use a utility method  
  2018.                     il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [command] [name]  
  2019.                     il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("FindOrAddParameter"), null); // stack is [parameters] [parameter]  
  2020.                 }  
  2021.                 else  
  2022.                 {  
  2023.                     // no risk of duplicates; just blindly add  
  2024.                     il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter]  
  2025.   
  2026.                     il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]  
  2027.                     il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [parameter] [parameter] [name]  
  2028.                     il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]  
  2029.                 }  
  2030.                 if (dbType != DbType.Time) // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time  
  2031.                 {  
  2032.                     //string parameter extensions  对于字符串参数化的扩展  
  2033.                     int dbTypeValue = (int)dbType;  
  2034.                     if (kvp.HasValue)  
  2035.                     {  
  2036.                         dbTypeValue = (int)kvp.Value.Key;  
  2037.                     }  
  2038.   
  2039.                     il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]  
  2040.                     EmitInt32(il, dbTypeValue);// stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type]  
  2041.   
  2042.                     il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]  
  2043.                 }  
  2044.   
  2045.                 il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]  
  2046.                 EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [[parameters]] [parameter] [parameter] [dir]  
  2047.                 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]  
  2048.   
  2049.                 il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]  
  2050.                 il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]  
  2051.                 il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [[parameters]] [parameter] [parameter] [typed-value]  
  2052.                 bool checkForNull = true;  
  2053.                 if (prop.PropertyType.IsValueType)  
  2054.                 {  
  2055.                     il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]  
  2056.                     if (Nullable.GetUnderlyingType(prop.PropertyType) == null)  
  2057.                     {   // struct but not Nullable<T>; boxed value cannot be null  
  2058.                         checkForNull = false;  
  2059.                     }  
  2060.                 }  
  2061.                 if (checkForNull)  
  2062.                 {  
  2063.                     if (dbType == DbType.String && !haveInt32Arg1)  
  2064.                     {  
  2065.                         il.DeclareLocal(typeof(int));  
  2066.                         haveInt32Arg1 = true;  
  2067.                     }  
  2068.                     // relative stack: [boxed value]  
  2069.                     il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]  
  2070.                     Label notNull = il.DefineLabel();  
  2071.                     Label? allDone = dbType == DbType.String ? il.DefineLabel() : (Label?)null;  
  2072.                     il.Emit(OpCodes.Brtrue_S, notNull);  
  2073.                     // relative stack [boxed value = null]  
  2074.                     il.Emit(OpCodes.Pop); // relative stack empty  
  2075.                     il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); // relative stack [DBNull]  
  2076.                     if (dbType == DbType.String)  
  2077.                     {  
  2078.                         EmitInt32(il, 0);  
  2079.                         il.Emit(OpCodes.Stloc_1);  
  2080.                     }  
  2081.                     if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);  
  2082.                     il.MarkLabel(notNull);  
  2083.                     //if (prop.PropertyType == typeof(string))  
  2084.                     //{  
  2085.                     //    il.Emit(OpCodes.Dup); // [string] [string]  
  2086.                     //    il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); // [string] [length]  
  2087.                     //    EmitInt32(il, 4000); // [string] [length] [4000]  
  2088.                     //    il.Emit(OpCodes.Clt); // [string] [0 or 1]  
  2089.                     //    Label isLong = il.DefineLabel(), lenDone = il.DefineLabel();  
  2090.                     //    il.Emit(OpCodes.Brtrue_S, isLong);  
  2091.                     //    EmitInt32(il, 4000); // [string] [4000]  
  2092.                     //    il.Emit(OpCodes.Br_S, lenDone);  
  2093.                     //    il.MarkLabel(isLong);  
  2094.                     //    EmitInt32(il, -1); // [string] [-1]  
  2095.                     //    il.MarkLabel(lenDone);  
  2096.                     //    il.Emit(OpCodes.Stloc_1); // [string]   
  2097.                     //}  
  2098.                     if (prop.PropertyType.FullName == LinqBinary)  
  2099.                     {  
  2100.                         il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod("ToArray", BindingFlags.Public | BindingFlags.Instance), null);  
  2101.                     }  
  2102.                     if (allDone != null) il.MarkLabel(allDone.Value);  
  2103.                     // relative stack [boxed value or DBNull]  
  2104.                 }  
  2105.                 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]  
  2106.   
  2107.                 if (prop.PropertyType == typeof(string))  
  2108.                 {  
  2109.                     //var endOfSize = il.DefineLabel();  
  2110.                     //// don't set if 0  
  2111.                     //il.Emit(OpCodes.Ldloc_1); // [parameters] [[parameters]] [parameter] [size]  
  2112.                     //il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter]  
  2113.   
  2114.                     //il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]  
  2115.                     //il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]  
  2116.                     //il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]  
  2117.   
  2118.                     //il.MarkLabel(endOfSize);  
  2119.   
  2120.                     if (kvp.HasValue)  
  2121.                     {  
  2122.                         il.Emit(OpCodes.Dup);  
  2123.                         EmitInt32(il, kvp.Value.Value);  
  2124.                         il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]  
  2125.                     }  
  2126.                 }  
  2127.                 if (checkForDuplicates)  
  2128.                 {  
  2129.                     // stack is now [parameters] [parameter]  
  2130.                     il.Emit(OpCodes.Pop); // don't need parameter any more  
  2131.                 }  
  2132.                 else  
  2133.                 {  
  2134.                     // stack is now [parameters] [parameters] [parameter]  
  2135.                     // blindly add  
  2136.                     il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]  
  2137.                     il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care  
  2138.                 }  
  2139.             }  
  2140.             // stack is currently [parameters]  
  2141.             il.Emit(OpCodes.Pop); // stack is now empty  
  2142.             il.Emit(OpCodes.Ret);  
  2143.             return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>));  
  2144.         }  
  2145.   
  2146.         private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType)  
  2147.         {  
  2148.             var cmd = cnn.CreateCommand();  
  2149.             var bindByName = GetBindByName(cmd.GetType());  
  2150.             if (bindByName != null) bindByName(cmd, true);  
  2151.             if (transaction != null)  
  2152.                 cmd.Transaction = transaction;  
  2153.             cmd.CommandText = sql;  
  2154.             if (commandTimeout.HasValue)  
  2155.                 cmd.CommandTimeout = commandTimeout.Value;  
  2156.             if (commandType.HasValue)  
  2157.                 cmd.CommandType = commandType.Value;  
  2158.             if (paramReader != null)  
  2159.             {  
  2160.                 paramReader(cmd, obj);  
  2161.             }  
  2162.             return cmd;  
  2163.         }  
  2164.   
  2165.   
  2166.         private static int ExecuteCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType)  
  2167.         {  
  2168.             IDbCommand cmd = null;  
  2169.             bool wasClosed = cnn.State == ConnectionState.Closed;  
  2170.             try  
  2171.             {  
  2172.                 cmd = SetupCommand(cnn, transaction, sql, paramReader, obj, commandTimeout, commandType);  
  2173.                 if (wasClosed) cnn.Open();  
  2174.                 return cmd.ExecuteNonQuery();  
  2175.             }  
  2176.             finally  
  2177.             {  
  2178.                 if (wasClosed) cnn.Close();  
  2179.                 if (cmd != null) cmd.Dispose();  
  2180.             }  
  2181.         }  
  2182.   
  2183.         private static Func<IDataReader, object> GetStructDeserializer(Type type, Type effectiveType, int index)  
  2184.         {  
  2185.             // 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!)  
  2186. #pragma warning disable 618  
  2187.             if (type == typeof(char))  
  2188.             { // this *does* need special handling, though  
  2189.                 return r => SqlMapper.ReadChar(r.GetValue(index));  
  2190.             }  
  2191.             if (type == typeof(char?))  
  2192.             {  
  2193.                 return r => SqlMapper.ReadNullableChar(r.GetValue(index));  
  2194.             }  
  2195.             if (type.FullName == LinqBinary)  
  2196.             {  
  2197.                 return r => Activator.CreateInstance(type, r.GetValue(index));  
  2198.             }  
  2199. #pragma warning restore 618  
  2200.   
  2201.             if (effectiveType.IsEnum)  
  2202.             {   // assume the value is returned as the correct type (int/byte/etc), but box back to the typed enum  
  2203.                 return r =>  
  2204.                 {  
  2205.                     var val = r.GetValue(index);  
  2206.                     return val is DBNull ? null : Enum.ToObject(effectiveType, val);  
  2207.                 };  
  2208.             }  
  2209.             return r =>  
  2210.             {  
  2211.                 var val = r.GetValue(index);  
  2212.                 return val is DBNull ? null : val;  
  2213.             };  
  2214.         }  
  2215.   
  2216.         static readonly MethodInfo  
  2217.                     enumParse = typeof(Enum).GetMethod("Parse"new Type[] { typeof(Type), typeof(string), typeof(bool) }),  
  2218.                     getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public)  
  2219.                         .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int))  
  2220.                         .Select(p => p.GetGetMethod()).First();  
  2221.   
  2222.         /// <summary>  
  2223.         /// Gets type-map for the given type  
  2224.         /// </summary>  
  2225.         /// <returns>Type map implementation, DefaultTypeMap instance if no override present</returns>  
  2226.         public static ITypeMap GetTypeMap(Type type)  
  2227.         {  
  2228.             if (type == nullthrow new ArgumentNullException("type");  
  2229.             var map = (ITypeMap)_typeMaps[type];  
  2230.             if (map == null)  
  2231.             {  
  2232.                 lock (_typeMaps)  
  2233.                 {   // double-checked; store this to avoid reflection next time we see this type  
  2234.                     // since multiple queries commonly use the same domain-entity/DTO/view-model type  
  2235.                     map = (ITypeMap)_typeMaps[type];  
  2236.                     if (map == null)  
  2237.                     {  
  2238.                         map = new DefaultTypeMap(type);  
  2239.                         _typeMaps[type] = map;  
  2240.                     }  
  2241.                 }  
  2242.             }  
  2243.             return map;  
  2244.         }  
  2245.   
  2246.         // use Hashtable to get free lockless reading  
  2247.         private static readonly Hashtable _typeMaps = new Hashtable();  
  2248.   
  2249.         /// <summary>  
  2250.         /// Set custom mapping for type deserializers  
  2251.         /// </summary>  
  2252.         /// <param name="type">Entity type to override</param>  
  2253.         /// <param name="map">Mapping rules impementation, null to remove custom map</param>  
  2254.         public static void SetTypeMap(Type type, ITypeMap map)  
  2255.         {  
  2256.             if (type == null)  
  2257.                 throw new ArgumentNullException("type");  
  2258.   
  2259.             if (map == null || map is DefaultTypeMap)  
  2260.             {  
  2261.                 lock (_typeMaps)  
  2262.                 {  
  2263.                     _typeMaps.Remove(type);  
  2264.                 }  
  2265.             }  
  2266.             else  
  2267.             {  
  2268.                 lock (_typeMaps)  
  2269.                 {  
  2270.                     _typeMaps[type] = map;  
  2271.                 }  
  2272.             }  
  2273.   
  2274.             PurgeQueryCacheByType(type);  
  2275.         }  
  2276.   
  2277.         /// <summary>  
  2278.         /// Internal use only  
  2279.         /// </summary>  
  2280.         /// <param name="type"></param>  
  2281.         /// <param name="reader"></param>  
  2282.         /// <param name="startBound"></param>  
  2283.         /// <param name="length"></param>  
  2284.         /// <param name="returnNullIfFirstMissing"></param>  
  2285.         /// <returns></returns>  
  2286.         public static Func<IDataReader, object> GetTypeDeserializer(  
  2287. #if CSHARP30  
  2288. Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing  
  2289. #else  
  2290. Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false  
  2291. #endif  
  2292. )  
  2293.         {  
  2294.   
  2295.             var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(IDataReader) }, true);  
  2296.             var il = dm.GetILGenerator();  
  2297.             il.DeclareLocal(typeof(int));  
  2298.             il.DeclareLocal(type);  
  2299.             il.Emit(OpCodes.Ldc_I4_0);  
  2300.             il.Emit(OpCodes.Stloc_0);  
  2301.   
  2302.             if (length == -1)  
  2303.             {  
  2304.                 length = reader.FieldCount - startBound;  
  2305.             }  
  2306.   
  2307.             if (reader.FieldCount <= startBound)  
  2308.             {  
  2309.                 throw new ArgumentException(MultiMapSplitExceptionMessage, "splitOn");  
  2310.             }  
  2311.   
  2312.             var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray();  
  2313.   
  2314.             ITypeMap typeMap = GetTypeMap(type);  
  2315.   
  2316.             int index = startBound;  
  2317.   
  2318.             ConstructorInfo specializedConstructor = null;  
  2319.   
  2320.             if (type.IsValueType)  
  2321.             {  
  2322.                 il.Emit(OpCodes.Ldloca_S, (byte)1);  
  2323.                 il.Emit(OpCodes.Initobj, type);  
  2324.             }  
  2325.             else  
  2326.             {  
  2327.                 var types = new Type[length];  
  2328.                 for (int i = startBound; i < startBound + length; i++)  
  2329.                 {  
  2330.                     types[i - startBound] = reader.GetFieldType(i);  
  2331.                 }  
  2332.   
  2333.                 if (type.IsValueType)  
  2334.                 {  
  2335.                     il.Emit(OpCodes.Ldloca_S, (byte)1);  
  2336.                     il.Emit(OpCodes.Initobj, type);  
  2337.                 }  
  2338.                 else  
  2339.                 {  
  2340.                     var ctor = typeMap.FindConstructor(names, types);  
  2341.                     if (ctor == null)  
  2342.                     {  
  2343.                         string proposedTypes = "(" + String.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";  
  2344.                         throw new InvalidOperationException(String.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));  
  2345.                     }  
  2346.   
  2347.                     if (ctor.GetParameters().Length == 0)  
  2348.                     {  
  2349.                         il.Emit(OpCodes.Newobj, ctor);  
  2350.                         il.Emit(OpCodes.Stloc_1);  
  2351.                     }  
  2352.                     else  
  2353.                         specializedConstructor = ctor;  
  2354.                 }  
  2355.             }  
  2356.   
  2357.             il.BeginExceptionBlock();  
  2358.             if (type.IsValueType)  
  2359.             {  
  2360.                 il.Emit(OpCodes.Ldloca_S, (byte)1);// [target]  
  2361.             }  
  2362.             else if (specializedConstructor == null)  
  2363.             {  
  2364.                 il.Emit(OpCodes.Ldloc_1);// [target]  
  2365.             }  
  2366.   
  2367.             var members = (specializedConstructor != null  
  2368.                 ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n))  
  2369.                 : names.Select(n => typeMap.GetMember(n))).ToList();  
  2370.   
  2371.             // stack is now [target]  
  2372.   
  2373.             bool first = true;  
  2374.             var allDone = il.DefineLabel();  
  2375.             int enumDeclareLocal = -1;  
  2376.             foreach (var item in members)  
  2377.             {  
  2378.                 if (item != null)  
  2379.                 {  
  2380.                     if (specializedConstructor == null)  
  2381.                         il.Emit(OpCodes.Dup); // stack is now [target][target]  
  2382.                     Label isDbNullLabel = il.DefineLabel();  
  2383.                     Label finishLabel = il.DefineLabel();  
  2384.   
  2385.                     il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]  
  2386.                     EmitInt32(il, index); // stack is now [target][target][reader][index]  
  2387.                     il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]  
  2388.                     il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]  
  2389.                     il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]  
  2390.   
  2391.                     Type memberType = item.MemberType;  
  2392.   
  2393.                     if (memberType == typeof(char) || memberType == typeof(char?))  
  2394.                     {  
  2395.                         il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(  
  2396.                             memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]  
  2397.                     }  
  2398.                     else  
  2399.                     {  
  2400.                         il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]  
  2401.                         il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]  
  2402.                         il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]  
  2403.   
  2404.                         // unbox nullable enums as the primitive, i.e. byte etc  
  2405.   
  2406.                         var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);  
  2407.                         var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;  
  2408.   
  2409.                         if (unboxType.IsEnum)  
  2410.                         {  
  2411.                             if (enumDeclareLocal == -1)  
  2412.                             {  
  2413.                                 enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;  
  2414.                             }  
  2415.   
  2416.                             Label isNotString = il.DefineLabel();  
  2417.                             il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]  
  2418.                             il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null]  
  2419.                             il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null]  
  2420.                             StoreLocal(il, enumDeclareLocal); // stack is now [target][target][value-as-object][string or null]  
  2421.                             il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object]  
  2422.   
  2423.                             il.Emit(OpCodes.Pop); // stack is now [target][target]  
  2424.   
  2425.                             il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]  
  2426.                             il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]  
  2427.                             il.Emit(OpCodes.Ldloc_2); // stack is now [target][target][enum-type][string]  
  2428.                             il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]  
  2429.                             il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]  
  2430.   
  2431.                             il.MarkLabel(isNotString);  
  2432.   
  2433.                             il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]  
  2434.   
  2435.                             if (nullUnderlyingType != null)  
  2436.                             {  
  2437.                                 il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][enum-value]  
  2438.                             }  
  2439.                         }  
  2440.                         else if (memberType.FullName == LinqBinary)  
  2441.                         {  
  2442.                             il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]  
  2443.                             il.Emit(OpCodes.Newobj, memberType.GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]  
  2444.                         }  
  2445.                         else  
  2446.                         {  
  2447.                             Type dataType = reader.GetFieldType(index);  
  2448.                             TypeCode dataTypeCode = Type.GetTypeCode(dataType), unboxTypeCode = Type.GetTypeCode(unboxType);  
  2449.                             if (dataType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType))  
  2450.                             {  
  2451.                                 il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]  
  2452.                             }  
  2453.                             else  
  2454.                             {  
  2455.                                 // not a direct match; need to tweak the unbox  
  2456.                                 MethodInfo op;  
  2457.                                 if ((op = GetOperator(dataType, nullUnderlyingType ?? unboxType)) != null)  
  2458.                                 { // this is handy for things like decimal <===> double  
  2459.                                     il.Emit(OpCodes.Unbox_Any, dataType); // stack is now [target][target][data-typed-value]  
  2460.                                     il.Emit(OpCodes.Call, op); // stack is now [target][target][typed-value]  
  2461.                                 }  
  2462.                                 else  
  2463.                                 {  
  2464.                                     bool handled = true;  
  2465.                                     OpCode opCode = default(OpCode);  
  2466.                                     if (dataTypeCode == TypeCode.Decimal || unboxTypeCode == TypeCode.Decimal)  
  2467.                                     {   // no IL level conversions to/from decimal; I guess we could use the static operators, but  
  2468.                                         // this feels an edge-case  
  2469.                                         handled = false;  
  2470.                                     }  
  2471.                                     else  
  2472.                                     {  
  2473.                                         switch (unboxTypeCode)  
  2474.                                         {  
  2475.                                             case TypeCode.Byte:  
  2476.                                                 opCode = OpCodes.Conv_Ovf_I1_Un; break;  
  2477.                                             case TypeCode.SByte:  
  2478.                                                 opCode = OpCodes.Conv_Ovf_I1; break;  
  2479.                                             case TypeCode.UInt16:  
  2480.                                                 opCode = OpCodes.Conv_Ovf_I2_Un; break;  
  2481.                                             case TypeCode.Int16:  
  2482.                                                 opCode = OpCodes.Conv_Ovf_I2; break;  
  2483.                                             case TypeCode.UInt32:  
  2484.                                                 opCode = OpCodes.Conv_Ovf_I4_Un; break;  
  2485.                                             case TypeCode.Boolean: // boolean is basically an int, at least at this level  
  2486.                                             case TypeCode.Int32:  
  2487.                                                 opCode = OpCodes.Conv_Ovf_I4; break;  
  2488.                                             case TypeCode.UInt64:  
  2489.                                                 opCode = OpCodes.Conv_Ovf_I8_Un; break;  
  2490.                                             case TypeCode.Int64:  
  2491.                                                 opCode = OpCodes.Conv_Ovf_I8; break;  
  2492.                                             case TypeCode.Single:  
  2493.                                                 opCode = OpCodes.Conv_R4; break;  
  2494.                                             case TypeCode.Double:  
  2495.                                                 opCode = OpCodes.Conv_R8; break;  
  2496.                                             default:  
  2497.                                                 handled = false;  
  2498.                                                 break;  
  2499.                                         }  
  2500.                                     }  
  2501.                                     if (handled)  
  2502.                                     { // unbox as the data-type, then use IL-level convert  
  2503.                                         il.Emit(OpCodes.Unbox_Any, dataType); // stack is now [target][target][data-typed-value]  
  2504.                                         il.Emit(opCode); // stack is now [target][target][typed-value]  
  2505.                                         if (unboxTypeCode == TypeCode.Boolean)  
  2506.                                         { // compare to zero; I checked "csc" - this is the trick it uses; nice  
  2507.                                             il.Emit(OpCodes.Ldc_I4_0);  
  2508.                                             il.Emit(OpCodes.Ceq);  
  2509.                                             il.Emit(OpCodes.Ldc_I4_0);  
  2510.                                             il.Emit(OpCodes.Ceq);  
  2511.                                         }  
  2512.                                     }  
  2513.                                     else  
  2514.                                     { // use flexible conversion  
  2515.                                         il.Emit(OpCodes.Ldtoken, nullUnderlyingType ?? unboxType); // stack is now [target][target][value][member-type-token]  
  2516.                                         il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); // stack is now [target][target][value][member-type]  
  2517.                                         il.EmitCall(OpCodes.Call, typeof(Convert).GetMethod("ChangeType"new Type[] { typeof(object), typeof(Type) }), null); // stack is now [target][target][boxed-member-type-value]  
  2518.                                         il.Emit(OpCodes.Unbox_Any, nullUnderlyingType ?? unboxType); // stack is now [target][target][typed-value]  
  2519.                                     }  
  2520.                                 }  
  2521.                                 if (nullUnderlyingType != null)  
  2522.                                 {  
  2523.                                     il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]  
  2524.                                 }  
  2525.   
  2526.                             }  
  2527.   
  2528.                         }  
  2529.                     }  
  2530.                     if (specializedConstructor == null)  
  2531.                     {  
  2532.                         // Store the value in the property/field  
  2533.                         if (item.Property != null)  
  2534.                         {  
  2535.                             if (type.IsValueType)  
  2536.                             {  
  2537.                                 il.Emit(OpCodes.Call, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]  
  2538.                             }  
  2539.                             else  
  2540.                             {  
  2541.                                 il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]  
  2542.                             }  
  2543.                         }  
  2544.                         else  
  2545.                         {  
  2546.                             il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]  
  2547.                         }  
  2548.                     }  
  2549.   
  2550.                     il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]  
  2551.   
  2552.                     il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]  
  2553.                     if (specializedConstructor != null)  
  2554.                     {  
  2555.                         il.Emit(OpCodes.Pop);  
  2556.                         if (item.MemberType.IsValueType)  
  2557.                         {  
  2558.                             int localIndex = il.DeclareLocal(item.MemberType).LocalIndex;  
  2559.                             LoadLocalAddress(il, localIndex);  
  2560.                             il.Emit(OpCodes.Initobj, item.MemberType);  
  2561.                             LoadLocal(il, localIndex);  
  2562.                         }  
  2563.                         else  
  2564.                         {  
  2565.                             il.Emit(OpCodes.Ldnull);  
  2566.                         }  
  2567.                     }  
  2568.                     else  
  2569.                     {  
  2570.                         il.Emit(OpCodes.Pop); // stack is now [target][target]  
  2571.                         il.Emit(OpCodes.Pop); // stack is now [target]  
  2572.                     }  
  2573.   
  2574.                     if (first && returnNullIfFirstMissing)  
  2575.                     {  
  2576.                         il.Emit(OpCodes.Pop);  
  2577.                         il.Emit(OpCodes.Ldnull); // stack is now [null]  
  2578.                         il.Emit(OpCodes.Stloc_1);  
  2579.                         il.Emit(OpCodes.Br, allDone);  
  2580.                     }  
  2581.   
  2582.                     il.MarkLabel(finishLabel);  
  2583.                 }  
  2584.                 first = false;  
  2585.                 index += 1;  
  2586.             }  
  2587.             if (type.IsValueType)  
  2588.             {  
  2589.                 il.Emit(OpCodes.Pop);  
  2590.             }  
  2591.             else  
  2592.             {  
  2593.                 if (specializedConstructor != null)  
  2594.                 {  
  2595.                     il.Emit(OpCodes.Newobj, specializedConstructor);  
  2596.                 }  
  2597.                 il.Emit(OpCodes.Stloc_1); // stack is empty  
  2598.             }  
  2599.             il.MarkLabel(allDone);  
  2600.             il.BeginCatchBlock(typeof(Exception)); // stack is Exception  
  2601.             il.Emit(OpCodes.Ldloc_0); // stack is Exception, index  
  2602.             il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader  
  2603.             il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);  
  2604.             il.EndExceptionBlock();  
  2605.   
  2606.             il.Emit(OpCodes.Ldloc_1); // stack is [rval]  
  2607.             if (type.IsValueType)  
  2608.             {  
  2609.                 il.Emit(OpCodes.Box, type);  
  2610.             }  
  2611.             il.Emit(OpCodes.Ret);  
  2612.   
  2613.             return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));  
  2614.         }  
  2615.         static MethodInfo GetOperator(Type from, Type to)  
  2616.         {  
  2617.             if (to == nullreturn null;  
  2618.             MethodInfo[] fromMethods, toMethods;  
  2619.             return ResolveOperator(fromMethods = from.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")  
  2620.                 ?? ResolveOperator(toMethods = to.GetMethods(BindingFlags.Static | BindingFlags.Public), from, to, "op_Implicit")  
  2621.                 ?? ResolveOperator(fromMethods, from, to, "op_Explicit")  
  2622.                 ?? ResolveOperator(toMethods, from, to, "op_Explicit");  
  2623.   
  2624.         }  
  2625.         static MethodInfo ResolveOperator(MethodInfo[] methods, Type from, Type to, string name)  
  2626.         {  
  2627.             for (int i = 0; i < methods.Length; i++)  
  2628.             {  
  2629.                 if (methods[i].Name != name || methods[i].ReturnType != to) continue;  
  2630.                 var args = methods[i].GetParameters();  
  2631.                 if (args.Length != 1 || args[0].ParameterType != from) continue;  
  2632.                 return methods[i];  
  2633.             }  
  2634.             return null;  
  2635.         }  
  2636.   
  2637.         private static void LoadLocal(ILGenerator il, int index)  
  2638.         {  
  2639.             if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");  
  2640.             switch (index)  
  2641.             {  
  2642.                 case 0: il.Emit(OpCodes.Ldloc_0); break;  
  2643.                 case 1: il.Emit(OpCodes.Ldloc_1); break;  
  2644.                 case 2: il.Emit(OpCodes.Ldloc_2); break;  
  2645.                 case 3: il.Emit(OpCodes.Ldloc_3); break;  
  2646.                 default:  
  2647.                     if (index <= 255)  
  2648.                     {  
  2649.                         il.Emit(OpCodes.Ldloc_S, (byte)index);  
  2650.                     }  
  2651.                     else  
  2652.                     {  
  2653.                         il.Emit(OpCodes.Ldloc, (short)index);  
  2654.                     }  
  2655.                     break;  
  2656.             }  
  2657.         }  
  2658.         private static void StoreLocal(ILGenerator il, int index)  
  2659.         {  
  2660.             if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");  
  2661.             switch (index)  
  2662.             {  
  2663.                 case 0: il.Emit(OpCodes.Stloc_0); break;  
  2664.                 case 1: il.Emit(OpCodes.Stloc_1); break;  
  2665.                 case 2: il.Emit(OpCodes.Stloc_2); break;  
  2666.                 case 3: il.Emit(OpCodes.Stloc_3); break;  
  2667.                 default:  
  2668.                     if (index <= 255)  
  2669.                     {  
  2670.                         il.Emit(OpCodes.Stloc_S, (byte)index);  
  2671.                     }  
  2672.                     else  
  2673.                     {  
  2674.                         il.Emit(OpCodes.Stloc, (short)index);  
  2675.                     }  
  2676.                     break;  
  2677.             }  
  2678.         }  
  2679.         private static void LoadLocalAddress(ILGenerator il, int index)  
  2680.         {  
  2681.             if (index < 0 || index >= short.MaxValue) throw new ArgumentNullException("index");  
  2682.   
  2683.             if (index <= 255)  
  2684.             {  
  2685.                 il.Emit(OpCodes.Ldloca_S, (byte)index);  
  2686.             }  
  2687.             else  
  2688.             {  
  2689.                 il.Emit(OpCodes.Ldloca, (short)index);  
  2690.             }  
  2691.         }  
  2692.         /// <summary>  
  2693.         /// Throws a data exception, only used internally  
  2694.         /// </summary>  
  2695.         /// <param name="ex"></param>  
  2696.         /// <param name="index"></param>  
  2697.         /// <param name="reader"></param>  
  2698.         public static void ThrowDataException(Exception ex, int index, IDataReader reader)  
  2699.         {  
  2700.             Exception toThrow;  
  2701.             try  
  2702.             {  
  2703.                 string name = "(n/a)", value = "(n/a)";  
  2704.                 if (reader != null && index >= 0 && index < reader.FieldCount)  
  2705.                 {  
  2706.                     name = reader.GetName(index);  
  2707.                     object val = reader.GetValue(index);  
  2708.                     if (val == null || val is DBNull)  
  2709.                     {  
  2710.                         value = "<null>";  
  2711.                     }  
  2712.                     else  
  2713.                     {  
  2714.                         value = Convert.ToString(val) + " - " + Type.GetTypeCode(val.GetType());  
  2715.                     }  
  2716.                 }  
  2717.                 toThrow = new DataException(string.Format("Error parsing column {0} ({1}={2})", index, name, value), ex);  
  2718.             }  
  2719.             catch  
  2720.             { // throw the **original** exception, wrapped as DataException  
  2721.                 toThrow = new DataException(ex.Message, ex);  
  2722.             }  
  2723.             throw toThrow;  
  2724.         }  
  2725.         private static void EmitInt32(ILGenerator il, int value)  
  2726.         {  
  2727.             switch (value)  
  2728.             {  
  2729.                 case -1: il.Emit(OpCodes.Ldc_I4_M1); break;  
  2730.                 case 0: il.Emit(OpCodes.Ldc_I4_0); break;  
  2731.                 case 1: il.Emit(OpCodes.Ldc_I4_1); break;  
  2732.                 case 2: il.Emit(OpCodes.Ldc_I4_2); break;  
  2733.                 case 3: il.Emit(OpCodes.Ldc_I4_3); break;  
  2734.                 case 4: il.Emit(OpCodes.Ldc_I4_4); break;  
  2735.                 case 5: il.Emit(OpCodes.Ldc_I4_5); break;  
  2736.                 case 6: il.Emit(OpCodes.Ldc_I4_6); break;  
  2737.                 case 7: il.Emit(OpCodes.Ldc_I4_7); break;  
  2738.                 case 8: il.Emit(OpCodes.Ldc_I4_8); break;  
  2739.                 default:  
  2740.                     if (value >= -128 && value <= 127)  
  2741.                     {  
  2742.                         il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);  
  2743.                     }  
  2744.                     else  
  2745.                     {  
  2746.                         il.Emit(OpCodes.Ldc_I4, value);  
  2747.                     }  
  2748.                     break;  
  2749.             }  
  2750.         }  
  2751.   
  2752.   
  2753.         /// <summary>  
  2754.         /// How should connection strings be compared for equivalence? Defaults to StringComparer.Ordinal.  
  2755.         /// Providing a custom implementation can be useful for allowing multi-tenancy databases with identical  
  2756.         /// schema to share startegies. Note that usual equivalence rules apply: any equivalent connection strings  
  2757.         /// <b>MUST</b> yield the same hash-code.  
  2758.         /// </summary>  
  2759.         public static IEqualityComparer<string> ConnectionStringComparer  
  2760.         {  
  2761.             get { return connectionStringComparer; }  
  2762.             set { connectionStringComparer = value ?? StringComparer.Ordinal; }  
  2763.         }  
  2764.         private static IEqualityComparer<string> connectionStringComparer = StringComparer.Ordinal;  
  2765.   
  2766.         /// <summary>  
  2767.         /// The grid reader provides interfaces for reading multiple result sets from a Dapper query   
  2768.         /// </summary>  
  2769.         public partial class GridReader : IDisposable  
  2770.         {  
  2771.             private IDataReader reader;  
  2772.             private IDbCommand command;  
  2773.             private Identity identity;  
  2774.   
  2775.             internal GridReader(IDbCommand command, IDataReader reader, Identity identity)  
  2776.             {  
  2777.                 this.command = command;  
  2778.                 this.reader = reader;  
  2779.                 this.identity = identity;  
  2780.             }  
  2781.  
  2782. #if !CSHARP30  
  2783.   
  2784.             /// <summary>  
  2785.             /// Read the next grid of results, returned as a dynamic object  
  2786.             /// </summary>  
  2787.             public IEnumerable<dynamic> Read(bool buffered = true)  
  2788.             {  
  2789.                 return Read<DapperRow>(buffered);  
  2790.             }  
  2791. #endif  
  2792.  
  2793. #if CSHARP30  
  2794.             /// <summary>  
  2795.             /// Read the next grid of results  
  2796.             /// </summary>  
  2797.             public IEnumerable<T> Read<T>()  
  2798.             {  
  2799.                 return Read<T>(true);  
  2800.             }  
  2801. #endif  
  2802.             /// <summary>  
  2803.             /// Read the next grid of results  
  2804.             /// </summary>  
  2805. #if CSHARP30  
  2806.             public IEnumerable<T> Read<T>(bool buffered)  
  2807. #else  
  2808.             public IEnumerable<T> Read<T>(bool buffered = true)  
  2809. #endif  
  2810.             {  
  2811.                 if (reader == nullthrow new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");  
  2812.                 if (consumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");  
  2813.                 var typedIdentity = identity.ForGrid(typeof(T), gridIndex);  
  2814.                 CacheInfo cache = GetCacheInfo(typedIdentity);  
  2815.                 var deserializer = cache.Deserializer;  
  2816.   
  2817.                 int hash = GetColumnHash(reader);  
  2818.                 if (deserializer.Func == null || deserializer.Hash != hash)  
  2819.                 {  
  2820.                     deserializer = new DeserializerState(hash, GetDeserializer(typeof(T), reader, 0, -1, false));  
  2821.                     cache.Deserializer = deserializer;  
  2822.                 }  
  2823.                 consumed = true;  
  2824.                 var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity);  
  2825.                 return buffered ? result.ToList() : result;  
  2826.             }  
  2827.   
  2828.             private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(object func, string splitOn)  
  2829.             {  
  2830.                 var identity = this.identity.ForGrid(typeof(TReturn), new Type[] {   
  2831.                     typeof(TFirst),   
  2832.                     typeof(TSecond),  
  2833.                     typeof(TThird),  
  2834.                     typeof(TFourth),  
  2835.                     typeof(TFifth),  
  2836.                     typeof(TSixth),  
  2837.                     typeof(TSeventh)  
  2838.                 }, gridIndex);  
  2839.                 try  
  2840.                 {  
  2841.                     foreach (var r in SqlMapper.MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(nullnull, func, nullnull, splitOn, nullnull, reader, identity))  
  2842.                     {  
  2843.                         yield return r;  
  2844.                     }  
  2845.                 }  
  2846.                 finally  
  2847.                 {  
  2848.                     NextResult();  
  2849.                 }  
  2850.             }  
  2851.  
  2852. #if CSHARP30  
  2853.             /// <summary>  
  2854.             /// Read multiple objects from a single recordset on the grid  
  2855.             /// </summary>  
  2856.             public IEnumerable<TReturn> Read<TFirst, TSecond, TReturn>(Func<TFirst, TSecond, TReturn> func, string splitOn)  
  2857.             {  
  2858.                 return Read<TFirst, TSecond, TReturn>(func, splitOn, true);  
  2859.             }  
  2860. #endif  
  2861.             /// <summary>  
  2862.             /// Read multiple objects from a single recordset on the grid  
  2863.             /// </summary>  
  2864. #if CSHARP30  
  2865.             public IEnumerable<TReturn> Read<TFirst, TSecond, TReturn>(Func<TFirst, TSecond, TReturn> func, string splitOn, bool buffered)  
  2866. #else  
  2867.             public IEnumerable<TReturn> Read<TFirst, TSecond, TReturn>(Func<TFirst, TSecond, TReturn> func, string splitOn = "id"bool buffered = true)  
  2868. #endif  
  2869.             {  
  2870.                 var result = MultiReadInternal<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(func, splitOn);  
  2871.                 return buffered ? result.ToList() : result;  
  2872.             }  
  2873.  
  2874. #if CSHARP30  
  2875.             /// <summary>  
  2876.             /// Read multiple objects from a single recordset on the grid  
  2877.             /// </summary>  
  2878.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TReturn>(Func<TFirst, TSecond, TThird, TReturn> func, string splitOn)  
  2879.             {  
  2880.                 return Read<TFirst, TSecond, TThird, TReturn>(func, splitOn, true);  
  2881.             }  
  2882. #endif  
  2883.             /// <summary>  
  2884.             /// Read multiple objects from a single recordset on the grid  
  2885.             /// </summary>  
  2886. #if CSHARP30  
  2887.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TReturn>(Func<TFirst, TSecond, TThird, TReturn> func, string splitOn, bool buffered)  
  2888. #else  
  2889.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TReturn>(Func<TFirst, TSecond, TThird, TReturn> func, string splitOn = "id"bool buffered = true)  
  2890. #endif  
  2891.             {  
  2892.                 var result = MultiReadInternal<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(func, splitOn);  
  2893.                 return buffered ? result.ToList() : result;  
  2894.             }  
  2895.  
  2896. #if CSHARP30  
  2897.             /// <summary>  
  2898.             /// Read multiple objects from a single record set on the grid  
  2899.             /// </summary>  
  2900.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TReturn> func, string splitOn)  
  2901.             {  
  2902.                 return Read<TFirst, TSecond, TThird, TFourth, TReturn>(func, splitOn, true);  
  2903.             }  
  2904. #endif  
  2905.   
  2906.             /// <summary>  
  2907.             /// Read multiple objects from a single record set on the grid  
  2908.             /// </summary>  
  2909. #if CSHARP30  
  2910.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TReturn> func, string splitOn, bool buffered)  
  2911. #else  
  2912.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TReturn> func, string splitOn = "id"bool buffered = true)  
  2913. #endif  
  2914.             {  
  2915.                 var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(func, splitOn);  
  2916.                 return buffered ? result.ToList() : result;  
  2917.             }  
  2918.  
  2919.  
  2920.  
  2921. #if !CSHARP30  
  2922.             /// <summary>  
  2923.             /// Read multiple objects from a single record set on the grid  
  2924.             /// </summary>  
  2925.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> func, string splitOn = "id"bool buffered = true)  
  2926.             {  
  2927.                 var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(func, splitOn);  
  2928.                 return buffered ? result.ToList() : result;  
  2929.             }  
  2930.             /// <summary>  
  2931.             /// Read multiple objects from a single record set on the grid  
  2932.             /// </summary>  
  2933.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> func, string splitOn = "id"bool buffered = true)  
  2934.             {  
  2935.                 var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(func, splitOn);  
  2936.                 return buffered ? result.ToList() : result;  
  2937.             }  
  2938.             /// <summary>  
  2939.             /// Read multiple objects from a single record set on the grid  
  2940.             /// </summary>  
  2941.             public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> func, string splitOn = "id"bool buffered = true)  
  2942.             {  
  2943.                 var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(func, splitOn);  
  2944.                 return buffered ? result.ToList() : result;  
  2945.             }  
  2946. #endif  
  2947.   
  2948.             private IEnumerable<T> ReadDeferred<T>(int index, Func<IDataReader, object> deserializer, Identity typedIdentity)  
  2949.             {  
  2950.                 try  
  2951.                 {  
  2952.                     while (index == gridIndex && reader.Read())  
  2953.                     {  
  2954.                         yield return (T)deserializer(reader);  
  2955.                     }  
  2956.                 }  
  2957.                 finally // finally so that First etc progresses things even when multiple rows  
  2958.                 {  
  2959.                     if (index == gridIndex)  
  2960.                     {  
  2961.                         NextResult();  
  2962.                     }  
  2963.                 }  
  2964.             }  
  2965.             private int gridIndex, readCount;  
  2966.             private bool consumed;  
  2967.             private void NextResult()  
  2968.             {  
  2969.                 if (reader.NextResult())  
  2970.                 {  
  2971.                     readCount++;  
  2972.                     gridIndex++;  
  2973.                     consumed = false;  
  2974.                 }  
  2975.                 else  
  2976.                 {  
  2977.                     // happy path; close the reader cleanly - no  
  2978.                     // need for "Cancel" etc  
  2979.                     reader.Dispose();  
  2980.                     reader = null;  
  2981.   
  2982.                     Dispose();  
  2983.                 }  
  2984.   
  2985.             }  
  2986.             /// <summary>  
  2987.             /// Dispose the grid, closing and disposing both the underlying reader and command.  
  2988.             /// </summary>  
  2989.             public void Dispose()  
  2990.             {  
  2991.                 if (reader != null)  
  2992.                 {  
  2993.                     if (!reader.IsClosed && command != null) command.Cancel();  
  2994.                     reader.Dispose();  
  2995.                     reader = null;  
  2996.                 }  
  2997.                 if (command != null)  
  2998.                 {  
  2999.                     command.Dispose();  
  3000.                     command = null;  
  3001.                 }  
  3002.             }  
  3003.         }  
  3004.     }  
  3005.   
  3006.     /// <summary>  
  3007.     /// A bag of parameters that can be passed to the Dapper Query and Execute methods  
  3008.     /// </summary>  
  3009.     partial class DynamicParameters : SqlMapper.IDynamicParameters  
  3010.     {  
  3011.         internal const DbType EnumerableMultiParameter = (DbType)(-1);  
  3012.         static Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>();  
  3013.   
  3014.         Dictionary<string, ParamInfo> parameters = new Dictionary<string, ParamInfo>();  
  3015.         List<object> templates;  
  3016.   
  3017.         partial class ParamInfo  
  3018.         {  
  3019.             public string Name { getset; }  
  3020.             public object Value { getset; }  
  3021.             public ParameterDirection ParameterDirection { getset; }  
  3022.             public DbType? DbType { getset; }  
  3023.             public int? Size { getset; }  
  3024.             public IDbDataParameter AttachedParam { getset; }  
  3025.         }  
  3026.   
  3027.         /// <summary>  
  3028.         /// construct a dynamic parameter bag  
  3029.         /// </summary>  
  3030.         public DynamicParameters()  
  3031.         {  
  3032.             RemoveUnused = true;  
  3033.         }  
  3034.   
  3035.         /// <summary>  
  3036.         /// construct a dynamic parameter bag  
  3037.         /// </summary>  
  3038.         /// <param name="template">can be an anonymous type or a DynamicParameters bag</param>  
  3039.         public DynamicParameters(object template)  
  3040.         {  
  3041.             RemoveUnused = true;  
  3042.             AddDynamicParams(template);  
  3043.         }  
  3044.   
  3045.         /// <summary>  
  3046.         /// Append a whole object full of params to the dynamic  
  3047.         /// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic  
  3048.         /// </summary>  
  3049.         /// <param name="param"></param>  
  3050.         public void AddDynamicParams(  
  3051. #if CSHARP30  
  3052. object param  
  3053. #else  
  3054. dynamic param  
  3055. #endif  
  3056. )  
  3057.         {  
  3058.             var obj = param as object;  
  3059.             if (obj != null)  
  3060.             {  
  3061.                 var subDynamic = obj as DynamicParameters;  
  3062.                 if (subDynamic == null)  
  3063.                 {  
  3064.                     var dictionary = obj as IEnumerable<KeyValuePair<stringobject>>;  
  3065.                     if (dictionary == null)  
  3066.                     {  
  3067.                         templates = templates ?? new List<object>();  
  3068.                         templates.Add(obj);  
  3069.                     }  
  3070.                     else  
  3071.                     {  
  3072.                         foreach (var kvp in dictionary)  
  3073.                         {  
  3074. #if CSHARP30  
  3075.                             Add(kvp.Key, kvp.Value, nullnullnull);  
  3076. #else  
  3077.                             Add(kvp.Key, kvp.Value);  
  3078. #endif  
  3079.                         }  
  3080.                     }  
  3081.                 }  
  3082.                 else  
  3083.                 {  
  3084.                     if (subDynamic.parameters != null)  
  3085.                     {  
  3086.                         foreach (var kvp in subDynamic.parameters)  
  3087.                         {  
  3088.                             parameters.Add(kvp.Key, kvp.Value);  
  3089.                         }  
  3090.                     }  
  3091.   
  3092.                     if (subDynamic.templates != null)  
  3093.                     {  
  3094.                         templates = templates ?? new List<object>();  
  3095.                         foreach (var t in subDynamic.templates)  
  3096.                         {  
  3097.                             templates.Add(t);  
  3098.                         }  
  3099.                     }  
  3100.                 }  
  3101.             }  
  3102.         }  
  3103.   
  3104.         /// <summary>  
  3105.         /// Add a parameter to this dynamic parameter list  
  3106.         /// </summary>  
  3107.         /// <param name="name"></param>  
  3108.         /// <param name="value"></param>  
  3109.         /// <param name="dbType"></param>  
  3110.         /// <param name="direction"></param>  
  3111.         /// <param name="size"></param>  
  3112.         public void Add(  
  3113. #if CSHARP30  
  3114. string name, object value, DbType? dbType, ParameterDirection? direction, int? size  
  3115. #else  
  3116. string name, object value = null, DbType? dbType = null, ParameterDirection? direction = nullint? size = null  
  3117. #endif  
  3118. )  
  3119.         {  
  3120.             parameters[Clean(name)] = new ParamInfo() { Name = name, Value = value, ParameterDirection = direction ?? ParameterDirection.Input, DbType = dbType, Size = size };  
  3121.         }  
  3122.   
  3123.         static string Clean(string name)  
  3124.         {  
  3125.             if (!string.IsNullOrEmpty(name))  
  3126.             {  
  3127.                 switch (name[0])  
  3128.                 {  
  3129.                     case '@':  
  3130.                     case ':':  
  3131.                     case '?':  
  3132.                         return name.Substring(1);  
  3133.                 }  
  3134.             }  
  3135.             return name;  
  3136.         }  
  3137.   
  3138.         void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)  
  3139.         {  
  3140.             AddParameters(command, identity);  
  3141.         }  
  3142.   
  3143.         /// <summary>  
  3144.         /// If true, the command-text is inspected and only values that are clearly used are included on the connection  
  3145.         /// </summary>  
  3146.         public bool RemoveUnused { getset; }  
  3147.   
  3148.         /// <summary>  
  3149.         /// Add all the parameters needed to the command just before it executes  
  3150.         /// </summary>  
  3151.         /// <param name="command">The raw command prior to execution</param>  
  3152.         /// <param name="identity">Information about the query</param>  
  3153.         protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)  
  3154.         {  
  3155.             if (templates != null)  
  3156.             {  
  3157.                 foreach (var template in templates)  
  3158.                 {  
  3159.                     var newIdent = identity.ForDynamicParameters(template.GetType());  
  3160.                     Action<IDbCommand, object> appender;  
  3161.   
  3162.                     lock (paramReaderCache)  
  3163.                     {  
  3164.                         if (!paramReaderCache.TryGetValue(newIdent, out appender))  
  3165.                         {  
  3166.                             appender = SqlMapper.CreateParamInfoGenerator(newIdent, true, RemoveUnused);  
  3167.                             paramReaderCache[newIdent] = appender;  
  3168.                         }  
  3169.                     }  
  3170.   
  3171.                     appender(command, template);  
  3172.                 }  
  3173.             }  
  3174.   
  3175.             foreach (var param in parameters.Values)  
  3176.             {  
  3177.                 var dbType = param.DbType;  
  3178.                 var val = param.Value;  
  3179.                 string name = Clean(param.Name);  
  3180.   
  3181.                 if (dbType == null && val != null) dbType = SqlMapper.LookupDbType(val.GetType(), name);  
  3182.   
  3183.                 if (dbType == DynamicParameters.EnumerableMultiParameter)  
  3184.                 {  
  3185. #pragma warning disable 612, 618  
  3186.                     SqlMapper.PackListParameters(command, name, val);  
  3187. #pragma warning restore 612, 618  
  3188.                 }  
  3189.                 else  
  3190.                 {  
  3191.   
  3192.                     bool add = !command.Parameters.Contains(name);  
  3193.                     IDbDataParameter p;  
  3194.                     if (add)  
  3195.                     {  
  3196.                         p = command.CreateParameter();  
  3197.                         p.ParameterName = name;  
  3198.                     }  
  3199.                     else  
  3200.                     {  
  3201.                         p = (IDbDataParameter)command.Parameters[name];  
  3202.                     }  
  3203.   
  3204.                     p.Value = val ?? DBNull.Value;  
  3205.                     p.Direction = param.ParameterDirection;  
  3206.                     var s = val as string;  
  3207.                     if (s != null)  
  3208.                     {  
  3209.                         if (s.Length <= 4000)  
  3210.                         {  
  3211.                             p.Size = 4000;  
  3212.                         }  
  3213.                     }  
  3214.                     if (param.Size != null)  
  3215.                     {  
  3216.                         p.Size = param.Size.Value;  
  3217.                     }  
  3218.                     if (dbType != null)  
  3219.                     {  
  3220.                         p.DbType = dbType.Value;  
  3221.                     }  
  3222.                     if (add)  
  3223.                     {  
  3224.                         command.Parameters.Add(p);  
  3225.                     }  
  3226.                     param.AttachedParam = p;  
  3227.                 }  
  3228.   
  3229.             }  
  3230.         }  
  3231.   
  3232.         /// <summary>  
  3233.         /// All the names of the param in the bag, use Get to yank them out  
  3234.         /// </summary>  
  3235.         public IEnumerable<string> ParameterNames  
  3236.         {  
  3237.             get  
  3238.             {  
  3239.                 return parameters.Select(p => p.Key);  
  3240.             }  
  3241.         }  
  3242.   
  3243.   
  3244.         /// <summary>  
  3245.         /// Get the value of a parameter  
  3246.         /// </summary>  
  3247.         /// <typeparam name="T"></typeparam>  
  3248.         /// <param name="name"></param>  
  3249.         /// <returns>The value, note DBNull.Value is not returned, instead the value is returned as null</returns>  
  3250.         public T Get<T>(string name)  
  3251.         {  
  3252.             var val = parameters[Clean(name)].AttachedParam.Value;  
  3253.             if (val == DBNull.Value)  
  3254.             {  
  3255.                 if (default(T) != null)  
  3256.                 {  
  3257.                     throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!");  
  3258.                 }  
  3259.                 return default(T);  
  3260.             }  
  3261.             return (T)val;  
  3262.         }  
  3263.     }  
  3264.   
  3265.     /// <summary>  
  3266.     /// 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  
  3267.     /// </summary>  
  3268.     sealed partial class DbString : Dapper.SqlMapper.ICustomQueryParameter  
  3269.     {  
  3270.         /// <summary>  
  3271.         /// Create a new DbString  
  3272.         /// </summary>  
  3273.         public DbString() { Length = -1; }  
  3274.         /// <summary>  
  3275.         /// Ansi vs Unicode   
  3276.         /// </summary>  
  3277.         public bool IsAnsi { getset; }  
  3278.         /// <summary>  
  3279.         /// Fixed length   
  3280.         /// </summary>  
  3281.         public bool IsFixedLength { getset; }  
  3282.         /// <summary>  
  3283.         /// Length of the string -1 for max  
  3284.         /// </summary>  
  3285.         public int Length { getset; }  
  3286.         /// <summary>  
  3287.         /// The value of the string  
  3288.         /// </summary>  
  3289.         public string Value { getset; }  
  3290.         /// <summary>  
  3291.         /// Add the parameter to the command... internal use only  
  3292.         /// </summary>  
  3293.         /// <param name="command"></param>  
  3294.         /// <param name="name"></param>  
  3295.         public void AddParameter(IDbCommand command, string name)  
  3296.         {  
  3297.             if (IsFixedLength && Length == -1)  
  3298.             {  
  3299.                 throw new InvalidOperationException("If specifying IsFixedLength,  a Length must also be specified");  
  3300.             }  
  3301.             var param = command.CreateParameter();  
  3302.             param.ParameterName = name;  
  3303.             param.Value = (object)Value ?? DBNull.Value;  
  3304.             if (Length == -1 && Value != null && Value.Length <= 4000)  
  3305.             {  
  3306.                 param.Size = 4000;  
  3307.             }  
  3308.             else  
  3309.             {  
  3310.                 param.Size = Length;  
  3311.             }  
  3312.             param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String);  
  3313.             command.Parameters.Add(param);  
  3314.         }  
  3315.     }  
  3316.   
  3317.     /// <summary>  
  3318.     /// Handles variances in features per DBMS  
  3319.     /// </summary>  
  3320.     partial class FeatureSupport  
  3321.     {  
  3322.         /// <summary>  
  3323.         /// Dictionary of supported features index by connection type name  
  3324.         /// </summary>  
  3325.         private static readonly Dictionary<string, FeatureSupport> FeatureList = new Dictionary<string, FeatureSupport>(StringComparer.InvariantCultureIgnoreCase) {  
  3326.                 {"sqlserverconnection"new FeatureSupport { Arrays = false}},  
  3327.                 {"npgsqlconnection"new FeatureSupport {Arrays = true}}  
  3328.         };  
  3329.   
  3330.         /// <summary>  
  3331.         /// Gets the featureset based on the passed connection  
  3332.         /// </summary>  
  3333.         public static FeatureSupport Get(IDbConnection connection)  
  3334.         {  
  3335.             string name = connection.GetType().Name;  
  3336.             FeatureSupport features;  
  3337.             return FeatureList.TryGetValue(name, out features) ? features : FeatureList.Values.First();  
  3338.         }  
  3339.   
  3340.         /// <summary>  
  3341.         /// True if the db supports array columns e.g. Postgresql  
  3342.         /// </summary>  
  3343.         public bool Arrays { getset; }  
  3344.     }  
  3345.   
  3346.     /// <summary>  
  3347.     /// Represents simple memeber map for one of target parameter or property or field to source DataReader column  
  3348.     /// </summary>  
  3349.     sealed partial class SimpleMemberMap : SqlMapper.IMemberMap  
  3350.     {  
  3351.         private readonly string _columnName;  
  3352.         private readonly PropertyInfo _property;  
  3353.         private readonly FieldInfo _field;  
  3354.         private readonly ParameterInfo _parameter;  
  3355.   
  3356.         /// <summary>  
  3357.         /// Creates instance for simple property mapping  
  3358.         /// </summary>  
  3359.         /// <param name="columnName">DataReader column name</param>  
  3360.         /// <param name="property">Target property</param>  
  3361.         public SimpleMemberMap(string columnName, PropertyInfo property)  
  3362.         {  
  3363.             if (columnName == null)  
  3364.                 throw new ArgumentNullException("columnName");  
  3365.   
  3366.             if (property == null)  
  3367.                 throw new ArgumentNullException("property");  
  3368.   
  3369.             _columnName = columnName;  
  3370.             _property = property;  
  3371.         }  
  3372.   
  3373.         /// <summary>  
  3374.         /// Creates instance for simple field mapping  
  3375.         /// </summary>  
  3376.         /// <param name="columnName">DataReader column name</param>  
  3377.         /// <param name="field">Target property</param>  
  3378.         public SimpleMemberMap(string columnName, FieldInfo field)  
  3379.         {  
  3380.             if (columnName == null)  
  3381.                 throw new ArgumentNullException("columnName");  
  3382.   
  3383.             if (field == null)  
  3384.                 throw new ArgumentNullException("field");  
  3385.   
  3386.             _columnName = columnName;  
  3387.             _field = field;  
  3388.         }  
  3389.   
  3390.         /// <summary>  
  3391.         /// Creates instance for simple constructor parameter mapping  
  3392.         /// </summary>  
  3393.         /// <param name="columnName">DataReader column name</param>  
  3394.         /// <param name="parameter">Target constructor parameter</param>  
  3395.         public SimpleMemberMap(string columnName, ParameterInfo parameter)  
  3396.         {  
  3397.             if (columnName == null)  
  3398.                 throw new ArgumentNullException("columnName");  
  3399.   
  3400.             if (parameter == null)  
  3401.                 throw new ArgumentNullException("parameter");  
  3402.   
  3403.             _columnName = columnName;  
  3404.             _parameter = parameter;  
  3405.         }  
  3406.   
  3407.         /// <summary>  
  3408.         /// DataReader column name  
  3409.         /// </summary>  
  3410.         public string ColumnName  
  3411.         {  
  3412.             get { return _columnName; }  
  3413.         }  
  3414.   
  3415.         /// <summary>  
  3416.         /// Target member type  
  3417.         /// </summary>  
  3418.         public Type MemberType  
  3419.         {  
  3420.             get  
  3421.             {  
  3422.                 if (_field != null)  
  3423.                     return _field.FieldType;  
  3424.   
  3425.                 if (_property != null)  
  3426.                     return _property.PropertyType;  
  3427.   
  3428.                 if (_parameter != null)  
  3429.                     return _parameter.ParameterType;  
  3430.   
  3431.                 return null;  
  3432.             }  
  3433.         }  
  3434.   
  3435.         /// <summary>  
  3436.         /// Target property  
  3437.         /// </summary>  
  3438.         public PropertyInfo Property  
  3439.         {  
  3440.             get { return _property; }  
  3441.         }  
  3442.   
  3443.         /// <summary>  
  3444.         /// Target field  
  3445.         /// </summary>  
  3446.         public FieldInfo Field  
  3447.         {  
  3448.             get { return _field; }  
  3449.         }  
  3450.   
  3451.         /// <summary>  
  3452.         /// Target constructor parameter  
  3453.         /// </summary>  
  3454.         public ParameterInfo Parameter  
  3455.         {  
  3456.             get { return _parameter; }  
  3457.         }  
  3458.     }  
  3459.   
  3460.     /// <summary>  
  3461.     /// Represents default type mapping strategy used by Dapper  
  3462.     /// </summary>  
  3463.     sealed partial class DefaultTypeMap : SqlMapper.ITypeMap  
  3464.     {  
  3465.         private readonly List<FieldInfo> _fields;  
  3466.         private readonly List<PropertyInfo> _properties;  
  3467.         private readonly Type _type;  
  3468.   
  3469.         /// <summary>  
  3470.         /// Creates default type map  
  3471.         /// </summary>  
  3472.         /// <param name="type">Entity type</param>  
  3473.         public DefaultTypeMap(Type type)  
  3474.         {  
  3475.             if (type == null)  
  3476.                 throw new ArgumentNullException("type");  
  3477.   
  3478.             _fields = GetSettableFields(type);  
  3479.             _properties = GetSettableProps(type);  
  3480.             _type = type;  
  3481.         }  
  3482.   
  3483.         internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type)  
  3484.         {  
  3485.             return propertyInfo.DeclaringType == type ?  
  3486.                 propertyInfo.GetSetMethod(true) :  
  3487.                 propertyInfo.DeclaringType.GetProperty(propertyInfo.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetSetMethod(true);  
  3488.         }  
  3489.   
  3490.         internal static List<PropertyInfo> GetSettableProps(Type t)  
  3491.         {  
  3492.             return t  
  3493.                   .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)  
  3494.                   .Where(p => GetPropertySetter(p, t) != null)  
  3495.                   .ToList();  
  3496.         }  
  3497.   
  3498.         internal static List<FieldInfo> GetSettableFields(Type t)  
  3499.         {  
  3500.             return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList();  
  3501.         }  
  3502.   
  3503.         /// <summary>  
  3504.         /// Finds best constructor  
  3505.         /// </summary>  
  3506.         /// <param name="names">DataReader column names</param>  
  3507.         /// <param name="types">DataReader column types</param>  
  3508.         /// <returns>Matching constructor or default one</returns>  
  3509.         public ConstructorInfo FindConstructor(string[] names, Type[] types)  
  3510.         {  
  3511.             var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);  
  3512.             foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))  
  3513.             {  
  3514.                 ParameterInfo[] ctorParameters = ctor.GetParameters();  
  3515.                 if (ctorParameters.Length == 0)  
  3516.                     return ctor;  
  3517.   
  3518.                 if (ctorParameters.Length != types.Length)  
  3519.                     continue;  
  3520.   
  3521.                 int i = 0;  
  3522.                 for (; i < ctorParameters.Length; i++)  
  3523.                 {  
  3524.                     if (!String.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase))  
  3525.                         break;  
  3526.                     if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary)  
  3527.                         continue;  
  3528.                     var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;  
  3529.                     if (unboxedType != types[i]  
  3530.                         && !(unboxedType.IsEnum && Enum.GetUnderlyingType(unboxedType) == types[i])  
  3531.                         && !(unboxedType == typeof(char) && types[i] == typeof(string)))  
  3532.                         break;  
  3533.                 }  
  3534.   
  3535.                 if (i == ctorParameters.Length)  
  3536.                     return ctor;  
  3537.             }  
  3538.   
  3539.             return null;  
  3540.         }  
  3541.   
  3542.         /// <summary>  
  3543.         /// Gets mapping for constructor parameter  
  3544.         /// </summary>  
  3545.         /// <param name="constructor">Constructor to resolve</param>  
  3546.         /// <param name="columnName">DataReader column name</param>  
  3547.         /// <returns>Mapping implementation</returns>  
  3548.         public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)  
  3549.         {  
  3550.             var parameters = constructor.GetParameters();  
  3551.   
  3552.             return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)));  
  3553.         }  
  3554.   
  3555.         /// <summary>  
  3556.         /// Gets member mapping for column  
  3557.         /// </summary>  
  3558.         /// <param name="columnName">DataReader column name</param>  
  3559.         /// <returns>Mapping implementation</returns>  
  3560.         public SqlMapper.IMemberMap GetMember(string columnName)  
  3561.         {  
  3562.             var property = _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))  
  3563.                ?? _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));  
  3564.   
  3565.             if (property != null)  
  3566.                 return new SimpleMemberMap(columnName, property);  
  3567.   
  3568.             var field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))  
  3569.                ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));  
  3570.   
  3571.             if (field != null)  
  3572.                 return new SimpleMemberMap(columnName, field);  
  3573.   
  3574.             return null;  
  3575.         }  
  3576.     }  
  3577.   
  3578.     /// <summary>  
  3579.     /// Implements custom property mapping by user provided criteria (usually presence of some custom attribute with column to member mapping)  
  3580.     /// </summary>  
  3581.     sealed partial class CustomPropertyTypeMap : SqlMapper.ITypeMap  
  3582.     {  
  3583.         private readonly Type _type;  
  3584.         private readonly Func<Type, string, PropertyInfo> _propertySelector;  
  3585.   
  3586.         /// <summary>  
  3587.         /// Creates custom property mapping  
  3588.         /// </summary>  
  3589.         /// <param name="type">Target entity type</param>  
  3590.         /// <param name="propertySelector">Property selector based on target type and DataReader column name</param>  
  3591.         public CustomPropertyTypeMap(Type type, Func<Type, string, PropertyInfo> propertySelector)  
  3592.         {  
  3593.             if (type == null)  
  3594.                 throw new ArgumentNullException("type");  
  3595.   
  3596.             if (propertySelector == null)  
  3597.                 throw new ArgumentNullException("propertySelector");  
  3598.   
  3599.             _type = type;  
  3600.             _propertySelector = propertySelector;  
  3601.         }  
  3602.   
  3603.         /// <summary>  
  3604.         /// Always returns default constructor  
  3605.         /// </summary>  
  3606.         /// <param name="names">DataReader column names</param>  
  3607.         /// <param name="types">DataReader column types</param>  
  3608.         /// <returns>Default constructor</returns>  
  3609.         public ConstructorInfo FindConstructor(string[] names, Type[] types)  
  3610.         {  
  3611.             return _type.GetConstructor(new Type[0]);  
  3612.         }  
  3613.   
  3614.         /// <summary>  
  3615.         /// Not impelmeneted as far as default constructor used for all cases  
  3616.         /// </summary>  
  3617.         /// <param name="constructor"></param>  
  3618.         /// <param name="columnName"></param>  
  3619.         /// <returns></returns>  
  3620.         public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)  
  3621.         {  
  3622.             throw new NotSupportedException();  
  3623.         }  
  3624.   
  3625.         /// <summary>  
  3626.         /// Returns property based on selector strategy  
  3627.         /// </summary>  
  3628.         /// <param name="columnName">DataReader column name</param>  
  3629.         /// <returns>Poperty member map</returns>  
  3630.         public SqlMapper.IMemberMap GetMember(string columnName)  
  3631.         {  
  3632.             var prop = _propertySelector(_type, columnName);  
  3633.             return prop != null ? new SimpleMemberMap(columnName, prop) : null;  
  3634.         }  
  3635.     }  
  3636.   
  3637.     // Define DAPPER_MAKE_PRIVATE if you reference Dapper by source  
  3638.     // and you like to make the Dapper types private (in order to avoid  
  3639.     // conflicts with other projects that also reference Dapper by source)  
  3640. #if !DAPPER_MAKE_PRIVATE  
  3641.   
  3642.     public partial class SqlMapper  
  3643.     {  
  3644.     }  
  3645.   
  3646.     public partial class DynamicParameters  
  3647.     {  
  3648.   
  3649.     }  
  3650.   
  3651.     public partial class DbString  
  3652.     {  
  3653.   
  3654.     }  
  3655.   
  3656.     public partial class SimpleMemberMap  
  3657.     {  
  3658.   
  3659.     }  
  3660.   
  3661.     public partial class DefaultTypeMap  
  3662.     {  
  3663.   
  3664.     }  
  3665.   
  3666.     public partial class CustomPropertyTypeMap  
  3667.     {  
  3668.   
  3669.     }  
  3670.   
  3671.     public partial class FeatureSupport  
  3672.     {  
  3673.   
  3674.     }  
  3675.  
  3676. #endif  
  3677.   
  3678. }  


Nunits测试代码

1、对数据操作部分的测试

[csharp] view plaincopy
  1. using System;  
  2. using System.Linq;  
  3. using NUnit.Framework;  
  4. using System.Data.SqlClient;  
  5. using Dapper;  
  6. using System.Collections;  
  7. using System.Collections.Generic;  
  8.   
  9. namespace DapperTest.DapperExtensions  
  10. {  
  11.     [Serializable]  
  12.     public class AutoCreate  
  13.     {  
  14.         public int ID { getset; }  
  15.         public string Name { getset; }  
  16.     }  
  17.     [TestFixture]  
  18.     public class DataOperateTest  
  19.     {  
  20.         private static string connectionStr = @"Server=W-PC\DEMO; Database=ByteartRetail; Integrated Security=True; MultipleActiveResultSets=True;";  
  21.         SqlConnection conn;  
  22.         [TestFixtureSetUp]  
  23.         public void InitSqlConnection()  
  24.         {  
  25.             DapperPocoInfo dpi = typeof(AutoCreate).GetPocoInfo();  
  26.             dpi.AddStringColumnMap("Name");  
  27.             dpi.TableName = "Tbl_AutoCreate";  
  28.   
  29.             conn = new SqlConnection(connectionStr);  
  30.             conn.Open();  
  31.         }  
  32.   
  33.         [TestFixtureTearDown]  
  34.         public void CloseConnection()  
  35.         {  
  36.             conn.Close();  
  37.         }  
  38.         [Test]  
  39.         public void IUQDTypedTest()  
  40.         {  
  41.             AutoCreate poco = new AutoCreate();  
  42.             poco.Name = DateTime.Now.ToString("yyMMddHHmmssfff");  
  43.             bool success = conn.Insert<AutoCreate>(poco);  
  44.             Assert.AreEqual(success, true);  
  45.             Assert.AreNotEqual(0, poco.ID);  
  46.   
  47.             poco.Name = "UU" + DateTime.Now.ToString("yyMMddHHmmssfff");  
  48.             success = conn.Update<AutoCreate>(poco);  
  49.             Assert.AreEqual(success, true);  
  50.   
  51.             AutoCreate pocoQuery = conn.QueryByKey<AutoCreate>(new { ID = poco.ID });  
  52.             Assert.AreEqual(poco.Name, pocoQuery.Name);  
  53.   
  54.             success = conn.DeleteByKey<AutoCreate>(new { ID = poco.ID });  
  55.             Assert.AreEqual(true, success);  
  56.   
  57.             pocoQuery = conn.QueryByKey<AutoCreate>(poco);  
  58.             Assert.IsNull(pocoQuery);  
  59.         }  
  60.   
  61.         [Test]  
  62.         public void IUQDDynamicTest()  
  63.         {  
  64.             //AutoCreate tmp = new AutoCreate();  
  65.             string name = DateTime.Now.ToString("yyMMddHHmmssfff");  
  66.             var poco = new { ID = 0, Name = name };  
  67.             MsSqlServerAdapter adapter = (MsSqlServerAdapter)conn.GetSqlAdapter();  
  68.             bool success = adapter.Insert<AutoCreate>(conn, poco);  
  69.             Assert.AreEqual(success, true);  
  70.             //Assert.AreNotEqual(0, poco.ID);  
  71.             int id;  
  72.             //id = adapter.GetLastInsertID<AutoCreate>(conn);  
  73.             id = (int)conn.GetLastInsertIndentityID<AutoCreate>();  
  74.             Assert.Greater(id, 0);  
  75.   
  76.             AutoCreate pocoQuery = conn.QueryByKey<AutoCreate>(new { ID = id });  
  77.             Assert.AreEqual(poco.Name, pocoQuery.Name);//测试插入的数据是否正确  
  78.   
  79.             name = "UU" + DateTime.Now.ToString("yyMMddHHmmssfff");  
  80.             //success = adapter.UpdateByKey<AutoCreate>(conn, new { ID = id, Name = name });  
  81.             success = conn.Update<AutoCreate>(new { ID = id, Name = name });  
  82.             Assert.AreEqual(success, true);  
  83.   
  84.             pocoQuery = conn.QueryByKey<AutoCreate>(new { ID = id });  
  85.             Assert.AreEqual(name, pocoQuery.Name);//测试插入的数据是否正确  
  86.   
  87.             success = conn.DeleteByKey<AutoCreate>(new { ID = id });  
  88.             Assert.AreEqual(true, success);  
  89.   
  90.             pocoQuery = conn.QueryByKey<AutoCreate>(new { ID = id });  
  91.             Assert.IsNull(pocoQuery);  
  92.         }  
  93.     }  
  94. }  

 

2、对POCO扩展部分的测试

[csharp] view plaincopy
  1. using System;  
  2.   
  3. namespace DapperTest  
  4. {  
  5.     [Serializable]  
  6.     public class TestEndWithIDModel  
  7.     {  
  8.         public byte ID { getset; }  
  9.         public string Name { getset; }  
  10.     }  
  11.     [Serializable]  
  12.     public class TestNotEndWithIDModel  
  13.     {  
  14.         public int OrderNum { getset; }  
  15.         public string OrderName { getset; }  
  16.     }  
  17. }  


对ID结尾的主键测试

[csharp] view plaincopy
  1. using System.Linq;  
  2. using NUnit.Framework;  
  3. using Dapper;  
  4.   
  5. namespace DapperTest.PocoInfo  
  6. {  
  7.     [TestFixture]  
  8.     public class PocoInfoEndWithIDKeyTest  
  9.     {  
  10.         DapperPocoInfo dpi;  
  11.         [SetUp]  
  12.         public void GetDapperPocoInfo()  
  13.         {  
  14.             dpi = typeof(TestEndWithIDModel).GetPocoInfo();  
  15.             //dpi.AddKeyMap("ID");//此行代码注销以下测试也是正确的  
  16.         }  
  17.         [Test]  
  18.         public void TestModelKeyOnlyOneTest()  
  19.         {  
  20.             Assert.AreEqual(1, dpi.KeyProperties.Count());  
  21.             Assert.AreEqual("ID", dpi.KeyProperties.First().Name);  
  22.             Assert.AreEqual(true, dpi.IsUnWriteKey());  
  23.         }  
  24.     }  
  25. }  


非ID结尾的主键测试

[csharp] view plaincopy
  1. using System.Linq;  
  2. using NUnit.Framework;  
  3. using Dapper;  
  4.   
  5. namespace DapperTest.PocoInfo  
  6. {  
  7.     [TestFixture]  
  8.     public class PocoInfoNotEndWithIDKeyTest  
  9.     {  
  10.         DapperPocoInfo dpi;  
  11.         [SetUp]  
  12.         public void GetDapperPocoInfo()  
  13.         {  
  14.             dpi = typeof(TestNotEndWithIDModel).GetPocoInfo();  
  15.             dpi.AddKeyMap("OrderNum");  
  16.         }  
  17.         [Test]  
  18.         public void TestModelKeyOnlyOneTest()  
  19.         {  
  20.             Assert.AreEqual(1, dpi.KeyProperties.Count());  
  21.             Assert.AreEqual("OrderNum", dpi.KeyProperties.First().Name);  
  22.             Assert.AreEqual(true, dpi.IsUnWriteKey());  
  23.         }  
  24.     }  
  25. }  


字符串映射测试

[csharp] view plaincopy
    1. using System.Collections.Generic;  
    2. using NUnit.Framework;  
    3. using Dapper;  
    4. using System.Data;  
    5.   
    6. namespace DapperTest.PocoInfo  
    7. {  
    8.     [TestFixture]  
    9.     public class PocoInfoStringColumnMapTest  
    10.     {  
    11.         KeyValuePair<DbType, int>? kvp;  
    12.         [SetUp]  
    13.         public void GetBasicInfo()  
    14.         {  
    15.             DapperPocoInfo dpi = typeof(TestEndWithIDModel).GetPocoInfo();  
    16.             dpi.AddStringColumnMap("Name");  
    17.             kvp = dpi.GetStringColumnMap("Name");  
    18.         }  
    19.         [Test]  
    20.         public void TestModelMappedStringColumnMapTest()  
    21.         {  
    22.             Assert.NotNull(kvp);  
    23.             Assert.AreEqual(DbType.AnsiString, kvp.Value.Key);  
    24.             Assert.AreEqual(50, kvp.Value.Value);  
    25.         }  
    26.     }  
    27. }
posted on 2013-12-08 00:30  davidkam  阅读(15651)  评论(1编辑  收藏  举报