123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Threading.Tasks;
-
- namespace RDH.Data
- {
- public static partial class SqlMapper
- {
-
- private static Dictionary<Type, TypeMapperBase> customTypeMaps = new Dictionary<Type, TypeMapperBase>();
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T">mapToType</typeparam>
- /// <param name="fromType"></param>
- /// <param name="mapDeMapHander"> </param>
- public static void RegisterMapperProvider<T>(DbType fromType, Func<object, T> mapDeMapHander)
- {
- var mapTo = typeof(T);
- if (typeMap.ContainsKey(mapTo))
- {
- typeMap[mapTo] = fromType;
- }
- else
- {
- typeMap.Add(mapTo, fromType);
- }
-
- customTypeMaps.Add(mapTo, new TypeMapper<T>
- {
- DbType = fromType,
- MethodName = mapDeMapHander.Method.Name,
- DeMapHanderR = mapDeMapHander,
- ProviderType = mapDeMapHander.Method.ReflectedType
-
- });
- }
-
- static Link<Type, Action<IDbCommand, bool>> bindByNameCache;
- static Action<IDbCommand, bool> GetBindByName(Type commandType)
- {
- if (commandType == null) return null; // GIGO
- Action<IDbCommand, bool> action;
- if (Link<Type, Action<IDbCommand, bool>>.TryGet(bindByNameCache, commandType, out action))
- {
- return action;
- }
- var prop = commandType.GetProperty("BindByName", BindingFlags.Public | BindingFlags.Instance);
- action = null;
- ParameterInfo[] indexers;
- MethodInfo setter;
- if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)
- && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)
- && (setter = prop.GetSetMethod()) != null
- )
- {
- var method = new DynamicMethod(commandType.Name + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });
- var il = method.GetILGenerator();
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Castclass, commandType);
- il.Emit(OpCodes.Ldarg_1);
- il.EmitCall(OpCodes.Callvirt, setter, null);
- il.Emit(OpCodes.Ret);
- action = (Action<IDbCommand, bool>)method.CreateDelegate(typeof(Action<IDbCommand, bool>));
- }
- // cache it
- Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);
- return action;
- }
- /// <summary>
- /// This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),
- /// and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**
- /// equality. The type is fully thread-safe.
- /// </summary>
- class Link<TKey, TValue> where TKey : class
- {
- public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value)
- {
- while (link != null)
- {
- if ((object)key == (object)link.Key)
- {
- value = link.Value;
- return true;
- }
- link = link.Tail;
- }
- value = default(TValue);
- return false;
- }
- public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue value)
- {
- bool tryAgain;
- do
- {
- var snapshot = Interlocked.CompareExchange(ref head, null, null);
- TValue found;
- if (TryGet(snapshot, key, out found))
- { // existing match; report the existing value instead
- value = found;
- return false;
- }
- var newNode = new Link<TKey, TValue>(key, value, snapshot);
- // did somebody move our cheese?
- tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot;
- } while (tryAgain);
- return true;
- }
- private Link(TKey key, TValue value, Link<TKey, TValue> tail)
- {
- Key = key;
- Value = value;
- Tail = tail;
- }
- public TKey Key { get; private set; }
- public TValue Value { get; private set; }
- public Link<TKey, TValue> Tail { get; private set; }
- }
-
-
- public static event EventHandler QueryCachePurged;
- private static void OnQueryCachePurged()
- {
- var handler = QueryCachePurged;
- if (handler != null) handler(null, EventArgs.Empty);
- }
- #if CSHARP30
- private static readonly Dictionary<Identity, CacheInfo> _queryCache = new Dictionary<Identity, CacheInfo>();
- // note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of
- // ReaderWriterLockSlim etc; a simple lock is faster
- private static void SetQueryCache(Identity key, CacheInfo value)
- {
- lock (_queryCache) { _queryCache[key] = value; }
- }
- private static bool TryGetQueryCache(Identity key, out CacheInfo value)
- {
- lock (_queryCache) { return _queryCache.TryGetValue(key, out value); }
- }
- public static void PurgeQueryCache()
- {
- lock (_queryCache)
- {
- _queryCache.Clear();
- }
- OnQueryCachePurged();
- }
- #else
- static readonly System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo> _queryCache = new System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo>();
- private static void SetQueryCache(Identity key, CacheInfo value)
- {
- if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)
- {
- CollectCacheGarbage();
- }
- _queryCache[key] = value;
- }
-
- private static void CollectCacheGarbage()
- {
- try
- {
- foreach (var pair in _queryCache)
- {
- if (pair.Value.GetHitCount() <= COLLECT_HIT_COUNT_MIN)
- {
- CacheInfo cache;
- _queryCache.TryRemove(pair.Key, out cache);
- }
- }
- }
-
- finally
- {
- Interlocked.Exchange(ref collect, 0);
- }
- }
-
- private const int COLLECT_PER_ITEMS = 1000, COLLECT_HIT_COUNT_MIN = 0;
- private static int collect;
- private static bool TryGetQueryCache(Identity key, out CacheInfo value)
- {
- if (_queryCache.TryGetValue(key, out value))
- {
- value.RecordHit();
- return true;
- }
- value = null;
- return false;
- }
-
- public static void PurgeQueryCache()
- {
- _queryCache.Clear();
- OnQueryCachePurged();
- }
-
- public static int GetCachedSQLCount()
- {
- return _queryCache.Count;
- }
-
-
- public static IEnumerable<Tuple<string, string, int>> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)
- {
- var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount()));
- if (ignoreHitCountAbove < int.MaxValue) data = data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove);
- return data;
- }
-
- public static IEnumerable<Tuple<int, int>> GetHashCollissions()
- {
- var counts = new Dictionary<int, int>();
- foreach (var key in _queryCache.Keys)
- {
- int count;
- if (!counts.TryGetValue(key.hashCode, out count))
- {
- counts.Add(key.hashCode, 1);
- }
- else
- {
- counts[key.hashCode] = count + 1;
- }
- }
- return from pair in counts
- where pair.Value > 1
- select Tuple.Create(pair.Key, pair.Value);
-
- }
- #endif
-
-
- static readonly Dictionary<Type, DbType> typeMap;
-
- static SqlMapper()
- {
-
- typeMap = new Dictionary<Type, DbType>();
- typeMap[typeof(byte)] = DbType.Byte;
- typeMap[typeof(sbyte)] = DbType.SByte;
- typeMap[typeof(short)] = DbType.Int16;
- typeMap[typeof(ushort)] = DbType.UInt16;
- typeMap[typeof(int)] = DbType.Int32;
- typeMap[typeof(uint)] = DbType.UInt32;
- typeMap[typeof(long)] = DbType.Int64;
- typeMap[typeof(ulong)] = DbType.UInt64;
- typeMap[typeof(float)] = DbType.Single;
- typeMap[typeof(double)] = DbType.Double;
- typeMap[typeof(decimal)] = DbType.Decimal;
- typeMap[typeof(bool)] = DbType.Boolean;
- typeMap[typeof(string)] = DbType.String;
- typeMap[typeof(char)] = DbType.StringFixedLength;
- typeMap[typeof(Guid)] = DbType.String;
- typeMap[typeof(DateTime)] = DbType.DateTime;
- typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
- typeMap[typeof(byte[])] = DbType.Binary;
- typeMap[typeof(byte?)] = DbType.Byte;
- typeMap[typeof(sbyte?)] = DbType.SByte;
- typeMap[typeof(short?)] = DbType.Int16;
- typeMap[typeof(ushort?)] = DbType.UInt16;
- typeMap[typeof(int?)] = DbType.Int32;
- typeMap[typeof(uint?)] = DbType.UInt32;
- typeMap[typeof(long?)] = DbType.Int64;
- typeMap[typeof(ulong?)] = DbType.UInt64;
- typeMap[typeof(float?)] = DbType.Single;
- typeMap[typeof(double?)] = DbType.Double;
- typeMap[typeof(decimal?)] = DbType.Decimal;
- typeMap[typeof(bool?)] = DbType.Boolean;
- typeMap[typeof(char?)] = DbType.StringFixedLength;
- typeMap[typeof(Guid?)] = DbType.Guid;
- typeMap[typeof(DateTime?)] = DbType.DateTime;
- typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
- //typeMap[typeof(System.Data.Linq.Binary)] = DbType.Binary;
-
- //by zxb reg map types types
- SqlMapper.RegisterMapperProvider<bool>(System.Data.DbType.AnsiStringFixedLength, TypeMapProvider.ReadBoolean);
- SqlMapper.RegisterMapperProvider<bool?>(System.Data.DbType.AnsiStringFixedLength, TypeMapProvider.ReadNullableBoolean);
- SqlMapper.RegisterMapperProvider<char>(System.Data.DbType.StringFixedLength, TypeMapProvider.ReadChar);
- SqlMapper.RegisterMapperProvider<char?>(System.Data.DbType.StringFixedLength, TypeMapProvider.ReadNullableChar);
- SqlMapper.RegisterMapperProvider<Guid>(System.Data.DbType.String, TypeMapProvider.ReadGuid);
- SqlMapper.RegisterMapperProvider<Guid?>(System.Data.DbType.String, TypeMapProvider.ReadNullableGuid);
- SqlMapper.RegisterMapperProvider<decimal>(System.Data.DbType.Decimal, TypeMapProvider.ReadDecimal);
- SqlMapper.RegisterMapperProvider<decimal?>(System.Data.DbType.Decimal, TypeMapProvider.ReadNullableDecimal);
- SqlMapper.RegisterMapperProvider<int>(System.Data.DbType.Decimal, TypeMapProvider.ReadInt);
- SqlMapper.RegisterMapperProvider<int?>(System.Data.DbType.Decimal, TypeMapProvider.ReadNullableInt);
- SqlMapper.RegisterMapperProvider<double>(System.Data.DbType.Double, TypeMapProvider.ReadDouble);
- SqlMapper.RegisterMapperProvider<double?>(System.Data.DbType.Double, TypeMapProvider.ReadNullableDouble);
-
- }
-
- private static DbType LookupDbType(Type type, string name)
- {
- DbType dbType;
- var nullUnderlyingType = Nullable.GetUnderlyingType(type);
- if (nullUnderlyingType != null) type = nullUnderlyingType;
- if (type.IsEnum)
- {
- type = Enum.GetUnderlyingType(type);
- }
- if (typeMap.TryGetValue(type, out dbType))
- {
- return dbType;
- }
- if (typeof(IEnumerable).IsAssignableFrom(type))
- {
- // use xml to denote its a list, hacky but will work on any DB
- return DbType.Xml;
- }
-
-
- throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));
- }
-
-
- #if CSHARP30
- /// <summary>
- /// Execute parameterized SQL
- /// </summary>
- /// <returns>Number of rows affected</returns>
- public static int Execute(this IDbConnection cnn, string sql, object param)
- {
- return Execute(cnn, sql, param, null, null, null);
- }
- /// <summary>
- /// Executes a query, returning the data typed as per T
- /// </summary>
- /// <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>
- /// <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
- /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
- /// </returns>
- public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param)
- {
- return Query<T>(cnn, sql, param, null, true, null, null);
- }
-
- #endif
- /// <summary>
- /// Execute parameterized SQL
- /// </summary>
- /// <returns>Number of rows affected</returns>
- public static int Execute(
- #if CSHARP30
- this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- IEnumerable multiExec = (object)param as IEnumerable;
- Identity identity;
- CacheInfo info = null;
- if (multiExec != null && !(multiExec is string))
- {
- bool isFirst = true;
- int total = 0;
- using (var cmd = SetupCommand(cnn, transaction, sql, null, null, commandTimeout, commandType))
- {
-
- string masterSql = null;
- foreach (var obj in multiExec)
- {
- if (isFirst)
- {
- masterSql = cmd.CommandText;
- isFirst = false;
- identity = new Identity(sql, cmd.CommandType, cnn, null, obj.GetType(), null);
- info = GetCacheInfo(identity);
- }
- else
- {
- cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
- cmd.Parameters.Clear(); // current code is Add-tastic
- }
- info.ParamReader(cmd, obj);
- total += cmd.ExecuteNonQuery();
- }
- }
- return total;
- }
-
- // nice and simple
- identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);
- info = GetCacheInfo(identity);
- Int32 i = ExecuteCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);
- return i;
- }
- #if !CSHARP30
- /// <summary>
- /// Return a list of dynamic objects, reader is closed after the call
- /// </summary>
- public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
- {
- return Query<FastExpando>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
- }
- #endif
-
- /// <summary>
- /// Executes a query, returning the data typed as per T
- /// </summary>
- /// <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>
- /// <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
- /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
- /// </returns>
- public static IEnumerable<T> Query<T>(
- #if CSHARP30
- this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- var data = QueryInternal<T>(cnn, sql, param as object, transaction, commandTimeout, commandType);
- return buffered ? data.ToList() : data;
- }
-
- /// <summary>
- /// Execute a command that returns multiple result sets, and access each in turn
- /// </summary>
- public static GridReader QueryMultiple(
- #if CSHARP30
- this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- Identity identity = new Identity(sql, commandType, cnn, typeof(GridReader), (object)param == null ? null : ((object)param).GetType(), null);
- CacheInfo info = GetCacheInfo(identity);
-
- IDbCommand cmd = null;
- IDataReader reader = null;
- try
- {
- cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);
- reader = cmd.ExecuteReader();
- return new GridReader(cmd, reader, identity);
- }
- catch
- {
- if (reader != null) reader.Dispose();
- if (cmd != null) cmd.Dispose();
- throw;
- }
- }
-
- /// <summary>
- /// Return a typed list of objects, reader is closed after the call
- /// </summary>
- private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
- {
- var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
- var info = GetCacheInfo(identity);
- if (cnn.State != ConnectionState.Open) { cnn.Open(); }
-
- using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType))
- {
- using (var reader = cmd.ExecuteReader())
- {
- Func<Func<IDataReader, object>> cacheDeserializer = () =>
- {
- info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
- SetQueryCache(identity, info);
- return info.Deserializer;
- };
-
- if (info.Deserializer == null)
- {
- cacheDeserializer();
- }
-
- var deserializer = info.Deserializer;
-
- while (reader.Read())
- {
- object next;
- try
- {
- next = deserializer(reader);
- }
- catch (DataException)
- {
- // give it another shot, in case the underlying schema changed
- deserializer = cacheDeserializer();
- next = deserializer(reader);
- }
- yield return (T)next;
- }
-
- }
- }
- }
-
- /// <summary>
- /// Maps a query to objects
- /// </summary>
- /// <typeparam name="T">The return type</typeparam>
- /// <typeparam name="U"></typeparam>
- /// <param name="cnn"></param>
- /// <param name="sql"></param>
- /// <param name="map"></param>
- /// <param name="param"></param>
- /// <param name="transaction"></param>
- /// <param name="buffered"></param>
- /// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
- /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
- /// <returns></returns>
- public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(
- #if CSHARP30
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- return MultiMap<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
-
- public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(
- #if CSHARP30
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- return MultiMap<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
-
- public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(
- #if CSHARP30
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- return MultiMap<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
-
- public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(
- #if CSHARP30
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth,TFifth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
-
- public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(
- #if CSHARP30
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType
- #else
- this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null
- #endif
- )
- {
- return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
-
- #if !CSHARP30
- 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 = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
- {
- return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
- }
- #endif
-
- static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(
- this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType)
- {
- var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param, transaction, splitOn, commandTimeout, commandType, null, null);
- return buffered ? results.ToList() : results;
- }
-
-
- internal 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)
- {
- 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) });
- CacheInfo cinfo = GetCacheInfo(identity);
-
- IDbCommand ownedCommand = null;
- IDataReader ownedReader = null;
-
- try
- {
- if (reader == null)
- {
- ownedCommand = SetupCommand(cnn, transaction, sql, cinfo.ParamReader, (object)param, commandTimeout, commandType);
- ownedReader = ownedCommand.ExecuteReader();
- reader = ownedReader;
- }
- Func<IDataReader, object> deserializer = null;
- Func<IDataReader, object>[] otherDeserializers = null;
-
- Action cacheDeserializers = () =>
- {
- var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) }, splitOn, reader);
- deserializer = cinfo.Deserializer = deserializers[0];
- otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray();
- SetQueryCache(identity, cinfo);
- };
-
- if ((deserializer = cinfo.Deserializer) == null || (otherDeserializers = cinfo.OtherDeserializers) == null)
- {
- cacheDeserializers();
- }
-
- Func<IDataReader, TReturn> mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(deserializer, otherDeserializers, map);
-
- if (mapIt != null)
- {
- while (reader.Read())
- {
- TReturn next;
- try
- {
- next = mapIt(reader);
- }
- catch (DataException)
- {
- cacheDeserializers();
- mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(deserializer, otherDeserializers, map);
- next = mapIt(reader);
- }
- yield return next;
- }
- }
- }
- finally
- {
- try
- {
- if (ownedReader != null)
- {
- ownedReader.Dispose();
- }
- }
- finally
- {
- if (ownedCommand != null)
- {
- ownedCommand.Dispose();
- }
- }
- }
- }
-
- private static Func<IDataReader, TReturn> GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Func<IDataReader, object> deserializer, Func<IDataReader, object>[] otherDeserializers, object map)
- {
- switch (otherDeserializers.Length)
- {
- case 1:
- return r => ((Func<TFirst, TSecond, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r));
- case 2:
- return r => ((Func<TFirst, TSecond, TThird, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r));
- case 3:
- return r => ((Func<TFirst, TSecond, TThird, TFourth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r));
- #if !CSHARP30
- case 4:
- 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));
- #endif
- case 5:
- 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));
-
- case 6:
- 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));
-
-
- default:
- throw new NotSupportedException();
- }
- }
-
- private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)
- {
- int current = 0;
- var splits = splitOn.Split(',').Select(s => s.Trim()).ToArray();
- var splitIndex = 0;
-
- Func<Type, int> nextSplit = type =>
- {
- var currentSplit = splits[splitIndex];
- if (splits.Length > splitIndex + 1)
- {
- splitIndex++;
- }
-
- bool skipFirst = false;
- int startingPos = current + 1;
- // if our current type has the split, skip the first time you see it.
- if (type != typeof(Object))
- {
- var props = GetSettableProps(type);
- var fields = GetSettableFields(type);
-
- foreach (var name in props.Select(p => p.Name).Concat(fields.Select(f => f.Name)))
- {
- if (string.Equals(name, currentSplit, StringComparison.OrdinalIgnoreCase))
- {
- skipFirst = true;
- startingPos = current;
- break;
- }
- }
-
- }
-
- int pos;
- for (pos = startingPos; pos < reader.FieldCount; pos++)
- {
- // some people like ID some id ... assuming case insensitive splits for now
- if (splitOn == "*")
- {
- break;
- }
- if (string.Equals(reader.GetName(pos), currentSplit, StringComparison.OrdinalIgnoreCase))
- {
- if (skipFirst)
- {
- skipFirst = false;
- }
- else
- {
- break;
- }
- }
- }
- current = pos;
- return pos;
- };
-
- var deserializers = new List<Func<IDataReader, object>>();
- int split = 0;
- bool first = true;
- foreach (var type in types)
- {
- if (type != typeof(DontMap))
- {
- int next = nextSplit(type);
- deserializers.Add(GetDeserializer(type, reader, split, next - split, /* returnNullIfFirstMissing: */ !first));
- first = false;
- split = next;
- }
- }
-
- return deserializers.ToArray();
- }
-
- internal static CacheInfo GetCacheInfo(Identity identity)
- {
- CacheInfo info;
- if (!TryGetQueryCache(identity, out info))
- {
- info = new CacheInfo();
- if (identity.parametersType != null)
- {
- if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))
- {
- info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd, identity); };
- }
- else
- {
- info.ParamReader = CreateParamInfoGenerator(identity);
- }
- }
- SetQueryCache(identity, info);
- }
- return info;
- }
-
- internal static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
- {
- #if !CSHARP30
- // dynamic is passed in as Object ... by c# design
- if (type == typeof(object)
- || type == typeof(FastExpando))
- {
- return GetDynamicDeserializer(reader, startBound, length, returnNullIfFirstMissing);
- }
- #endif
-
- if (type.IsClass && type != typeof(string)
- && type != typeof(byte[]))
- {
- return GetClassDeserializer2(type, reader, startBound, length, returnNullIfFirstMissing);
- }
- return GetStructDeserializer(type, startBound);
-
- }
- #if !CSHARP30
- private class FastExpando : System.Dynamic.DynamicObject, IDictionary<string, object>
- {
- IDictionary<string, object> data;
-
- public static FastExpando Attach(IDictionary<string, object> data)
- {
- return new FastExpando { data = data };
- }
-
- public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
- {
- data[binder.Name] = value;
- return true;
- }
-
- public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
- {
- return data.TryGetValue(binder.Name, out result);
- }
-
- #region IDictionary<string,object> Members
-
- void IDictionary<string, object>.Add(string key, object value)
- {
- throw new NotImplementedException();
- }
-
- bool IDictionary<string, object>.ContainsKey(string key)
- {
- return data.ContainsKey(key);
- }
-
- ICollection<string> IDictionary<string, object>.Keys
- {
- get { return data.Keys; }
- }
-
- bool IDictionary<string, object>.Remove(string key)
- {
- throw new NotImplementedException();
- }
-
- bool IDictionary<string, object>.TryGetValue(string key, out object value)
- {
- return data.TryGetValue(key, out value);
- }
-
- ICollection<object> IDictionary<string, object>.Values
- {
- get { return data.Values; }
- }
-
- object IDictionary<string, object>.this[string key]
- {
- get
- {
- return data[key];
- }
- set
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region ICollection<KeyValuePair<string,object>> Members
-
- void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
- {
- throw new NotImplementedException();
- }
-
- void ICollection<KeyValuePair<string, object>>.Clear()
- {
- throw new NotImplementedException();
- }
-
- bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
- {
- return data.Contains(item);
- }
-
- void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
- {
- data.CopyTo(array, arrayIndex);
- }
-
- int ICollection<KeyValuePair<string, object>>.Count
- {
- get { return data.Count; }
- }
-
- bool ICollection<KeyValuePair<string, object>>.IsReadOnly
- {
- get { return true; }
- }
-
- bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
- {
- throw new NotImplementedException();
- }
-
- #endregion
-
- #region IEnumerable<KeyValuePair<string,object>> Members
-
- IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
- {
- return data.GetEnumerator();
- }
-
- #endregion
-
- #region IEnumerable Members
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return data.GetEnumerator();
- }
-
- #endregion
- }
-
-
- private static Func<IDataReader, object> GetDynamicDeserializer(IDataRecord reader, int startBound, int length, bool returnNullIfFirstMissing)
- {
- var fieldCount = reader.FieldCount;
- if (length == -1)
- {
- length = fieldCount - startBound;
- }
-
- if (fieldCount <= startBound)
- {
- throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");
- }
-
- return
- r =>
- {
- IDictionary<string, object> row = new Dictionary<string, object>(length);
- for (var i = startBound; i < startBound + length; i++)
- {
- var tmp = r.GetValue(i);
- tmp = tmp == DBNull.Value ? null : tmp;
- row[r.GetName(i)] = tmp;
- if (returnNullIfFirstMissing && i == startBound && tmp == null)
- {
- return null;
- }
- }
- //we know this is an object so it will not box
- return FastExpando.Attach(row);
- };
- }
- #endif
-
- [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
- [Obsolete("This method is for internal usage only", true)]
- public static void PackListParameters(IDbCommand command, string namePrefix, object value)
- {
- // initially we tried TVP, however it performs quite poorly.
- // keep in mind SQL support up to 2000 params easily in sp_executesql, needing more is rare
-
- var list = value as IEnumerable;
- var count = 0;
-
- if (list != null)
- {
- bool isString = value is IEnumerable<string>;
- foreach (var item in list)
- {
- count++;
- var listParam = command.CreateParameter();
- listParam.ParameterName = namePrefix + count;
- listParam.Value = item ?? DBNull.Value;
- if (isString)
- {
- listParam.Size = 4000;
- if (item != null && ((string)item).Length > 4000)
- {
- listParam.Size = -1;
- }
- }
- command.Parameters.Add(listParam);
- }
-
- if (count == 0)
- {
- command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), "(SELECT NULL WHERE 1 = 0)");
- }
- else
- {
- command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), match =>
- {
- var grp = match.Value;
- var sb = new StringBuilder("(").Append(grp).Append(1);
- for (int i = 2; i <= count; i++)
- {
- sb.Append(',').Append(grp).Append(i);
- }
- return sb.Append(')').ToString();
- });
- }
- }
-
- }
-
- private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyInfo> parameters, string sql)
- {
- return parameters.Where(p => Regex.IsMatch(sql, "[@:]" + p.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline));
- }
-
- public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity)
- {
- Type type = identity.parametersType;
- bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;
-
- var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);
-
- var il = dm.GetILGenerator();
-
- il.DeclareLocal(type); // 0
- bool haveInt32Arg1 = false;
- il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]
- il.Emit(OpCodes.Unbox_Any, type); // stack is now [typed-param]
- il.Emit(OpCodes.Stloc_0);// stack is now empty
-
- il.Emit(OpCodes.Ldarg_0); // stack is now [command]
- il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); // stack is now [parameters]
-
- IEnumerable<PropertyInfo> props = type.GetProperties().OrderBy(p => p.Name);
- if (filterParams)
- {
- props = FilterParameters(props, identity.sql);
- }
- foreach (var prop in props)
- {
- if (filterParams)
- {
- if (identity.sql.IndexOf("@" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0
- && identity.sql.IndexOf(":" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0)
- { // can't see the parameter in the text (even in a comment, etc) - burn it with fire
- continue;
- }
- }
- if (prop.PropertyType == typeof(DbString))
- {
- il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
- il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [dbstring]
- il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [dbstring] [command]
- il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [dbstring] [command] [name]
- il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters]
- continue;
- }
- DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
- if (dbType == DbType.Xml)
- {
- // this actually represents special handling for list types;
- il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
- il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
- il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
- il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
- if (prop.PropertyType.IsValueType)
- {
- il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
- }
- il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]
- continue;
- }
- il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters]
-
- il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]
- il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter]
-
- il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
- il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [parameter] [parameter] [name]
- il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
-
- il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
- EmitInt32(il, (int)dbType);// stack is now [parameters] [parameters] [parameter] [parameter] [db-type]
-
- il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
-
- il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
- EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [parameters] [parameter] [parameter] [dir]
- il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
-
- il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
- il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [parameters] [parameter] [parameter] [typed-param]
- il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [parameters] [parameter] [parameter] [typed-value]
- bool checkForNull = true;
- if (prop.PropertyType.IsValueType)
- {
- il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [parameters] [parameter] [parameter] [boxed-value]
- if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
- { // struct but not Nullable<T>; boxed value cannot be null
- checkForNull = false;
- }
- }
- if (checkForNull)
- {
- if (dbType == DbType.String && !haveInt32Arg1)
- {
- il.DeclareLocal(typeof(int));
- haveInt32Arg1 = true;
- }
- // relative stack: [boxed value]
- il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]
- Label notNull = il.DefineLabel();
- Label? allDone = dbType == DbType.String ? il.DefineLabel() : (Label?)null;
- il.Emit(OpCodes.Brtrue_S, notNull);
- // relative stack [boxed value = null]
- il.Emit(OpCodes.Pop); // relative stack empty
- il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); // relative stack [DBNull]
- if (dbType == DbType.String)
- {
- EmitInt32(il, 0);
- il.Emit(OpCodes.Stloc_1);
- }
- if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);
- il.MarkLabel(notNull);
- if (prop.PropertyType == typeof(string))
- {
- il.Emit(OpCodes.Dup); // [string] [string]
- il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); // [string] [length]
- EmitInt32(il, 4000); // [string] [length] [4000]
- il.Emit(OpCodes.Cgt); // [string] [0 or 1]
- Label isLong = il.DefineLabel(), lenDone = il.DefineLabel();
- il.Emit(OpCodes.Brtrue_S, isLong);
- EmitInt32(il, 4000); // [string] [4000]
- il.Emit(OpCodes.Br_S, lenDone);
- il.MarkLabel(isLong);
- EmitInt32(il, -1); // [string] [-1]
- il.MarkLabel(lenDone);
- il.Emit(OpCodes.Stloc_1); // [string]
- }
- //if (prop.PropertyType == typeof(System.Data.Linq.Binary))
- //{
- // il.EmitCall(OpCodes.Callvirt, typeof(System.Data.Linq.Binary).GetMethod("ToArray", BindingFlags.Public | BindingFlags.Instance), null);
- //}
- if (allDone != null) il.MarkLabel(allDone.Value);
- // relative stack [boxed value or DBNull]
- }
- il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
-
- if (prop.PropertyType == typeof(string))
- {
- var endOfSize = il.DefineLabel();
- // don't set if 0
- il.Emit(OpCodes.Ldloc_1); // [parameters] [parameters] [parameter] [size]
- il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [parameters] [parameter]
-
- il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
- il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [parameters] [parameter] [parameter] [size]
- il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
-
- il.MarkLabel(endOfSize);
- }
-
- il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]
- il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care
- }
- // stack is currently [command]
- il.Emit(OpCodes.Pop); // stack is now empty
- il.Emit(OpCodes.Ret);
- return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>));
- }
-
- private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType)
- {
- var cmd = cnn.CreateCommand();
- var bindByName = GetBindByName(cmd.GetType());
- if (bindByName != null) bindByName(cmd, true);
- cmd.Transaction = transaction;
- cmd.CommandText = sql;
- if (commandTimeout.HasValue)
- cmd.CommandTimeout = commandTimeout.Value;
- if (commandType.HasValue)
- cmd.CommandType = commandType.Value;
- if (paramReader != null)
- {
- paramReader(cmd, obj);
- }
- return cmd;
- }
-
-
- private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType)
- {
- using (var cmd = SetupCommand(cnn, tranaction, sql, paramReader, obj, commandTimeout, commandType))
- {
- return cmd.ExecuteNonQuery();
- }
- }
-
- private static Func<IDataReader, object> GetStructDeserializer(Type type, int index)
- {
- // no point using special per-type handling here; it boils down to the same, plus not all are supported anyway (see: SqlDataReader.GetChar - not supported!)
- #pragma warning disable 618
-
- if (customTypeMaps.ContainsKey(type))
- {
- return r => customTypeMaps[type].DeMapHander(r.GetValue(index));
- }
-
-
- #pragma warning restore 618
- return r =>
- {
- var val = r.GetValue(index);
- return val is DBNull ? null : val;
- };
- }
-
- static readonly MethodInfo
- enumParse = typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }),
- getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public)
- .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int))
- .Select(p => p.GetGetMethod()).First();
-
- class PropInfo
- {
- public string Name { get; set; }
- public MethodInfo Setter { get; set; }
- public Type Type { get; set; }
- }
-
- static List<PropInfo> GetSettableProps(Type t)
- {
- return t
- .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
- .Select(p => new PropInfo
- {
- Name = p.Name,
- Setter = p.DeclaringType == t ? p.GetSetMethod(true) : p.DeclaringType.GetProperty(p.Name).GetSetMethod(true),
- Type = p.PropertyType
- })
- .Where(info => info.Setter != null)
- .ToList();
- }
-
- static List<FieldInfo> GetSettableFields(Type t)
- {
- return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList();
- }
-
-
- private static Func<IDataReader, object> GetClassDeserializer2(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
- {
- var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
-
- if (length == -1)
- {
- length = reader.FieldCount - startBound;
- }
-
- if (reader.FieldCount <= startBound)
- {
- throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");
- }
-
- var names = new Dictionary<string, int>();
-
- for (int i = startBound; i < startBound + length; i++)
- {
- names.Add(reader.GetName(i), i);
- }
-
- var setters = (
- from n in names
- let prop = properties.FirstOrDefault(p => string.Equals(p.Name, n.Key, StringComparison.Ordinal)) // property case sensitive first
- ?? properties.FirstOrDefault(p => string.Equals(p.Name, n.Key, StringComparison.OrdinalIgnoreCase)) // property case insensitive second
- select new { Name = n, Property = prop }
- ).ToList();
-
- return (r) =>
- {
- var instance = Activator.CreateInstance(type);
- bool create = false;
- foreach (var setter in setters)
- {
- if (r.IsDBNull(setter.Name.Value) || setter.Property == null)
- { continue; }
- create = true;
- var value = r.GetValue(setter.Name.Value);
- object converted = value;
- if (!setter.Property.PropertyType.IsAssignableFrom(value.GetType()))
- {
- if (customTypeMaps.ContainsKey(setter.Property.PropertyType))
- {
- converted = customTypeMaps[setter.Property.PropertyType].DeMapHander(value);
- }
- }
- setter.Property.SetValue(instance, converted);
- }
-
- return create ? instance : null;
- };
-
- }
-
-
-
- public static Func<IDataReader, object> GetClassDeserializer(
- #if CSHARP30
- Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing
- #else
- Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false
- #endif
- )
- {
-
- var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), type, new[] { typeof(IDataReader) }, true);
-
- var il = dm.GetILGenerator();
- il.DeclareLocal(typeof(int));
- il.DeclareLocal(type);
- bool haveEnumLocal = false;
- il.Emit(OpCodes.Ldc_I4_0);
- il.Emit(OpCodes.Stloc_0);
- var properties = GetSettableProps(type);
- var fields = GetSettableFields(type);
- if (length == -1)
- {
- length = reader.FieldCount - startBound;
- }
-
- if (reader.FieldCount <= startBound)
- {
- throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");
- }
-
- var names = new List<string>();
-
- for (int i = startBound; i < startBound + length; i++)
- {
- names.Add(reader.GetName(i));
- }
-
- var setters = (
- from n in names
- let prop = properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // property case sensitive first
- ?? properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase)) // property case insensitive second
- let field = prop != null ? null : (fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) // field case sensitive third
- ?? fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase))) // field case insensitive fourth
- select new { Name = n, Property = prop, Field = field }
- ).ToList();
-
- int index = startBound;
-
- il.BeginExceptionBlock();
- // stack is empty
- il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); // stack is now [target]
- bool first = true;
- var allDone = il.DefineLabel();
- foreach (var item in setters)
- {
- if (item.Property != null || item.Field != null)
- {
- il.Emit(OpCodes.Dup); // stack is now [target][target]
- Label isDbNullLabel = il.DefineLabel();
- Label finishLabel = il.DefineLabel();
-
- il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
- EmitInt32(il, index); // stack is now [target][target][reader][index]
- il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
- il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]
- il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]
-
-
- Type memberType = item.Property != null ? item.Property.Type : item.Field.FieldType;
-
- if (customTypeMaps.ContainsKey(memberType))
- {
- il.EmitCall(OpCodes.Call, customTypeMaps[memberType].ProviderType.GetMethod(
- customTypeMaps[memberType].MethodName, BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
-
- }
- else
- {
- il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
- il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
- il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]
-
- // unbox nullable enums as the primitive, i.e. byte etc
-
- var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
- var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;
-
- if (unboxType.IsEnum)
- {
- if (!haveEnumLocal)
- {
- il.DeclareLocal(typeof(string));
- haveEnumLocal = true;
- }
-
- Label isNotString = il.DefineLabel();
- il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
- il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null]
- il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null]
- il.Emit(OpCodes.Stloc_2); // stack is now [target][target][value-as-object][string or null]
- il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object]
-
- il.Emit(OpCodes.Pop); // stack is now [target][target]
-
-
- il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
- il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
- il.Emit(OpCodes.Ldloc_2); // stack is now [target][target][enum-type][string]
- il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
- il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]
-
- il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
-
- if (nullUnderlyingType != null)
- {
- il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
- }
- if (item.Property != null)
- {
- il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target]
- }
- else
- {
- il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
- }
- il.Emit(OpCodes.Br_S, finishLabel);
-
-
- il.MarkLabel(isNotString);
- }
- //if (memberType == typeof(System.Data.Linq.Binary))
- //{
- // il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
- // il.Emit(OpCodes.Newobj, typeof(System.Data.Linq.Binary).GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
- //}
- //else
- {
- il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
-
- }
- if (nullUnderlyingType != null && nullUnderlyingType.IsEnum)
- {
- il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
- }
- }
- if (item.Property != null)
- {
- il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target]
- }
- else
- {
- il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
- }
-
- il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]
-
- il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
-
- il.Emit(OpCodes.Pop); // stack is now [target][target]
- il.Emit(OpCodes.Pop); // stack is now [target]
-
- if (first && returnNullIfFirstMissing)
- {
- il.Emit(OpCodes.Pop);
- il.Emit(OpCodes.Ldnull); // stack is now [null]
- il.Emit(OpCodes.Stloc_1);
- il.Emit(OpCodes.Br, allDone);
- }
-
- il.MarkLabel(finishLabel);
- }
- first = false;
- index += 1;
- }
- il.Emit(OpCodes.Stloc_1); // stack is empty
- il.MarkLabel(allDone);
- il.BeginCatchBlock(typeof(Exception)); // stack is Exception
- il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
- il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
- il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);
- il.Emit(OpCodes.Ldnull);
- il.Emit(OpCodes.Stloc_1); // to make it verifiable
- il.EndExceptionBlock();
-
- il.Emit(OpCodes.Ldloc_1); // stack is empty
- il.Emit(OpCodes.Ret);
-
- return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
- }
- public static void ThrowDataException(Exception ex, int index, IDataReader reader)
- {
- string name = "(n/a)", value = "(n/a)";
- if (reader != null && index >= 0 && index < reader.FieldCount)
- {
- name = reader.GetName(index);
- object val = reader.GetValue(index);
- if (val == null || val is DBNull)
- {
- value = "<null>";
- }
- else
- {
- value = Convert.ToString(val) + " - " + Type.GetTypeCode(val.GetType());
- }
- }
- throw new DataException(string.Format("Error parsing column {0} ({1}={2})", index, name, value), ex);
- }
- private static void EmitInt32(ILGenerator il, int value)
- {
- switch (value)
- {
- case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
- case 0: il.Emit(OpCodes.Ldc_I4_0); break;
- case 1: il.Emit(OpCodes.Ldc_I4_1); break;
- case 2: il.Emit(OpCodes.Ldc_I4_2); break;
- case 3: il.Emit(OpCodes.Ldc_I4_3); break;
- case 4: il.Emit(OpCodes.Ldc_I4_4); break;
- case 5: il.Emit(OpCodes.Ldc_I4_5); break;
- case 6: il.Emit(OpCodes.Ldc_I4_6); break;
- case 7: il.Emit(OpCodes.Ldc_I4_7); break;
- case 8: il.Emit(OpCodes.Ldc_I4_8); break;
- default:
- if (value >= -128 && value <= 127)
- {
- il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
- }
- else
- {
- il.Emit(OpCodes.Ldc_I4, value);
- }
- break;
- }
- }
-
-
- }
- }
|