oracle.exe 1648,Dapper完善兼容Oracle,执行存储过程,并返回结果集

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 * 增加Oracle存储过程支持9 * 李科笠 2015年10月13日 17:43:5410 */

11 usingSystem;12 usingSystem.Collections;13 usingSystem.Collections.Generic;14 usingSystem.ComponentModel;15 usingSystem.Data;16 usingSystem.Linq;17 usingSystem.Reflection;18 usingSystem.Reflection.Emit;19 usingSystem.Text;20 usingSystem.Threading;21 usingSystem.Text.RegularExpressions;22 usingOracle.DataAccess.Client;23

24 namespaceDapper25 {26 public static partial classSqlMapper27 {28 public interfaceIDynamicParameters29 {30 voidAddParameters(IDbCommand command, Identity identity);31 }32 static Link>bindByNameCache;33 static ActionGetBindByName(Type commandType)34 {35 if (commandType == null) return null; //GIGO

36 Actionaction;37 if (Link>.TryGet(bindByNameCache, commandType, outaction))38 {39 returnaction;40 }41 var prop = commandType.GetProperty("BindByName", BindingFlags.Public |BindingFlags.Instance);42 action = null;43 ParameterInfo[] indexers;44 MethodInfo setter;45 if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)46 && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)47 && (setter = prop.GetSetMethod()) != null

48 )49 {50 var method = new DynamicMethod(commandType.Name + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });51 var il =method.GetILGenerator();52 il.Emit(OpCodes.Ldarg_0);53 il.Emit(OpCodes.Castclass, commandType);54 il.Emit(OpCodes.Ldarg_1);55 il.EmitCall(OpCodes.Callvirt, setter, null);56 il.Emit(OpCodes.Ret);57 action = (Action)method.CreateDelegate(typeof(Action));58 }59 //cache it

60 Link>.TryAdd(ref bindByNameCache, commandType, refaction);61 returnaction;62 }63 ///

64 ///This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),65 ///and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**66 ///equality. The type is fully thread-safe.67 ///

68 class Link where TKey : class

69 {70 public static bool TryGet(Link link, TKey key, outTValue value)71 {72 while (link != null)73 {74 if ((object)key == (object)link.Key)75 {76 value =link.Value;77 return true;78 }79 link =link.Tail;80 }81 value = default(TValue);82 return false;83 }84 public static bool TryAdd(ref Link head, TKey key, refTValue value)85 {86 booltryAgain;87 do

88 {89 var snapshot = Interlocked.CompareExchange(ref head, null, null);90 TValue found;91 if (TryGet(snapshot, key, outfound))92 { //existing match; report the existing value instead

93 value =found;94 return false;95 }96 var newNode = new Link(key, value, snapshot);97 //did somebody move our cheese?

98 tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) !=snapshot;99 } while(tryAgain);100 return true;101 }102 private Link(TKey key, TValue value, Linktail)103 {104 Key =key;105 Value =value;106 Tail =tail;107 }108 public TKey Key { get; private set; }109 public TValue Value { get; private set; }110 public Link Tail { get; private set; }111 }112 classCacheInfo113 {114 public Func Deserializer { get; set; }115 public Func[] OtherDeserializers { get; set; }116 public Action ParamReader { get; set; }117 private inthitCount;118 public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }119 public void RecordHit() { Interlocked.Increment(refhitCount); }120 }121

122 public static eventEventHandler QueryCachePurged;123 private static voidOnQueryCachePurged()124 {125 var handler =QueryCachePurged;126 if (handler != null) handler(null, EventArgs.Empty);127 }128 #if CSHARP30

129 private static readonly Dictionary _queryCache = new Dictionary();130 //note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of131 //ReaderWriterLockSlim etc; a simple lock is faster

132 private static voidSetQueryCache(Identity key, CacheInfo value)133 {134 lock (_queryCache) { _queryCache[key] =value; }135 }136 private static bool TryGetQueryCache(Identity key, outCacheInfo value)137 {138 lock (_queryCache) { return _queryCache.TryGetValue(key, outvalue); }139 }140 public static voidPurgeQueryCache()141 {142 lock(_queryCache)143 {144 _queryCache.Clear();145 }146 OnQueryCachePurged();147 }148 #else

149 static readonly System.Collections.Concurrent.ConcurrentDictionary _queryCache = new System.Collections.Concurrent.ConcurrentDictionary();150 private static voidSetQueryCache(Identity key, CacheInfo value)151 {152 if (Interlocked.Increment(ref collect) ==COLLECT_PER_ITEMS)153 {154 CollectCacheGarbage();155 }156 _queryCache[key] =value;157 }158

159 private static voidCollectCacheGarbage()160 {161 try

162 {163 foreach (var pair in_queryCache)164 {165 if (pair.Value.GetHitCount() <=COLLECT_HIT_COUNT_MIN)166 {167 CacheInfo cache;168 _queryCache.TryRemove(pair.Key, outcache);169 }170 }171 }172

173 finally

174 {175 Interlocked.Exchange(ref collect, 0);176 }177 }178

179 private const int COLLECT_PER_ITEMS = 1000, COLLECT_HIT_COUNT_MIN = 0;180 private static intcollect;181 private static bool TryGetQueryCache(Identity key, outCacheInfo value)182 {183 if (_queryCache.TryGetValue(key, outvalue))184 {185 value.RecordHit();186 return true;187 }188 value = null;189 return false;190 }191

192 public static voidPurgeQueryCache()193 {194 _queryCache.Clear();195 OnQueryCachePurged();196 }197

198 public static intGetCachedSQLCount()199 {200 return_queryCache.Count;201 }202

203

204 public static IEnumerable> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)205 {206 var data = _queryCache.Select(pair =>Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount()));207 if (ignoreHitCountAbove < int.MaxValue) data = data.Where(tuple => tuple.Item3 <=ignoreHitCountAbove);208 returndata;209 }210

211 public static IEnumerable>GetHashCollissions()212 {213 var counts = new Dictionary();214 foreach (var key in_queryCache.Keys)215 {216 intcount;217 if (!counts.TryGetValue(key.hashCode, outcount))218 {219 counts.Add(key.hashCode, 1);220 }221 else

222 {223 counts[key.hashCode] = count + 1;224 }225 }226 return from pair incounts227 where pair.Value > 1

228 selectTuple.Create(pair.Key, pair.Value);229

230 }231 #endif

232

233

234 static readonly DictionarytypeMap;235

236 staticSqlMapper()237 {238 typeMap = new Dictionary();239 typeMap[typeof(byte)] =DbType.Byte;240 typeMap[typeof(sbyte)] =DbType.SByte;241 typeMap[typeof(short)] =DbType.Int16;242 typeMap[typeof(ushort)] =DbType.UInt16;243 typeMap[typeof(int)] =DbType.Int32;244 typeMap[typeof(uint)] =DbType.UInt32;245 typeMap[typeof(long)] =DbType.Int64;246 typeMap[typeof(ulong)] =DbType.UInt64;247 typeMap[typeof(float)] =DbType.Single;248 typeMap[typeof(double)] =DbType.Double;249 typeMap[typeof(decimal)] =DbType.Decimal;250 typeMap[typeof(bool)] =DbType.Boolean;251 typeMap[typeof(string)] =DbType.String;252 typeMap[typeof(char)] =DbType.StringFixedLength;253 typeMap[typeof(Guid)] =DbType.Guid;254 typeMap[typeof(DateTime)] =DbType.DateTime;255 typeMap[typeof(DateTimeOffset)] =DbType.DateTimeOffset;256 typeMap[typeof(byte[])] =DbType.Binary;257 typeMap[typeof(byte?)] =DbType.Byte;258 typeMap[typeof(sbyte?)] =DbType.SByte;259 typeMap[typeof(short?)] =DbType.Int16;260 typeMap[typeof(ushort?)] =DbType.UInt16;261 typeMap[typeof(int?)] =DbType.Int32;262 typeMap[typeof(uint?)] =DbType.UInt32;263 typeMap[typeof(long?)] =DbType.Int64;264 typeMap[typeof(ulong?)] =DbType.UInt64;265 typeMap[typeof(float?)] =DbType.Single;266 typeMap[typeof(double?)] =DbType.Double;267 typeMap[typeof(decimal?)] =DbType.Decimal;268 typeMap[typeof(bool?)] =DbType.Boolean;269 typeMap[typeof(char?)] =DbType.StringFixedLength;270 typeMap[typeof(Guid?)] =DbType.Guid;271 typeMap[typeof(DateTime?)] =DbType.DateTime;272 typeMap[typeof(DateTimeOffset?)] =DbType.DateTimeOffset;273 typeMap[typeof(System.Data.Linq.Binary)] =DbType.Binary;274 }275

276 private static DbType LookupDbType(Type type, stringname)277 {278 DbType dbType;279 var nullUnderlyingType =Nullable.GetUnderlyingType(type);280 if (nullUnderlyingType != null) type =nullUnderlyingType;281 if(type.IsEnum)282 {283 type =Enum.GetUnderlyingType(type);284 }285 if (typeMap.TryGetValue(type, outdbType))286 {287 returndbType;288 }289 if (typeof(IEnumerable).IsAssignableFrom(type))290 {291 //use xml to denote its a list, hacky but will work on any DB

292 returnDbType.Xml;293 }294

295

296 throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));297 }298

299 public class Identity : IEquatable

300 {301 internal Identity ForGrid(Type primaryType, intgridIndex)302 {303 return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);304 }305

306 internal Identity ForGrid(Type primaryType, Type[] otherTypes, intgridIndex)307 {308 return newIdentity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);309 }310

311 publicIdentity ForDynamicParameters(Type type)312 {313 return new Identity(sql, commandType, connectionString, this.type, type, null, -1);314 }315

316 internal Identity(string sql, CommandType?commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)317 : this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)318 { }319 private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, intgridIndex)320 {321 this.sql =sql;322 this.commandType =commandType;323 this.connectionString =connectionString;324 this.type =type;325 this.parametersType =parametersType;326 this.gridIndex =gridIndex;327 unchecked

328 {329 hashCode = 17; //we *know* we are using this in a dictionary, so pre-compute this

330 hashCode = hashCode * 23 +commandType.GetHashCode();331 hashCode = hashCode * 23 +gridIndex.GetHashCode();332 hashCode = hashCode * 23 + (sql == null ? 0: sql.GetHashCode());333 hashCode = hashCode * 23 + (type == null ? 0: type.GetHashCode());334 if (otherTypes != null)335 {336 foreach (var t inotherTypes)337 {338 hashCode = hashCode * 23 + (t == null ? 0: t.GetHashCode());339 }340 }341 hashCode = hashCode * 23 + (connectionString == null ? 0: connectionString.GetHashCode());342 hashCode = hashCode * 23 + (parametersType == null ? 0: parametersType.GetHashCode());343 }344 }345 public override bool Equals(objectobj)346 {347 return Equals(obj asIdentity);348 }349 public readonly stringsql;350 public readonly CommandType?commandType;351 public readonly inthashCode, gridIndex;352 private readonlyType type;353 public readonly stringconnectionString;354 public readonlyType parametersType;355 public override intGetHashCode()356 {357 returnhashCode;358 }359 public boolEquals(Identity other)360 {361 return

362 other != null &&

363 gridIndex == other.gridIndex &&

364 type == other.type &&

365 sql == other.sql &&

366 commandType == other.commandType &&

367 connectionString == other.connectionString &&

368 parametersType ==other.parametersType;369 }370 }371

372 #if CSHARP30

373 ///

374 ///Execute parameterized SQL375 ///

376 /// Number of rows affected

377 public static int Execute(this IDbConnection cnn, string sql, objectparam)378 {379 return Execute(cnn, sql, param, null, null, null);380 }381 ///

382 ///Executes a query, returning the data typed as per T383 ///

384 /// 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 newget new object

385 /// 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 is386 ///created per row, and a direct column-name===member-name mapping is assumed (case insensitive).387 ///

388 public static IEnumerable Query(this IDbConnection cnn, string sql, objectparam)389 {390 return Query(cnn, sql, param, null, true, null, null);391 }392

393 #endif

394 ///

395 ///Execute parameterized SQL396 ///

397 /// Number of rows affected

398 public static intExecute(399 #if CSHARP30

400 this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType?commandType401 #else

402 this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null

403 #endif

404 )405 {406 IEnumerable multiExec = (object)param asIEnumerable;407 Identity identity;408 CacheInfo info = null;409 if (multiExec != null && !(multiExec is string))410 {411 bool isFirst = true;412 int total = 0;413 using (var cmd = SetupCommand(cnn, transaction, sql, null, null, commandTimeout, commandType))414 {415

416 string masterSql = null;417 foreach (var obj inmultiExec)418 {419 if(isFirst)420 {421 masterSql =cmd.CommandText;422 isFirst = false;423 identity = new Identity(sql, cmd.CommandType, cnn, null, obj.GetType(), null);424 info =GetCacheInfo(identity);425 }426 else

427 {428 cmd.CommandText = masterSql; //because we do magic replaces on "in" etc

429 cmd.Parameters.Clear(); //current code is Add-tastic

430 }431 info.ParamReader(cmd, obj);432 total +=cmd.ExecuteNonQuery();433 }434 }435 returntotal;436 }437

438 //nice and simple

439 identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);440 info =GetCacheInfo(identity);441 return ExecuteCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);442 }443 #if !CSHARP30

444 ///

445 ///Return a list of dynamic objects, reader is closed after the call446 ///

447 public static IEnumerable Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)448 {449 return Query(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);450 }451 #endif

452

453 ///

454 ///Executes a query, returning the data typed as per T455 ///

456 /// 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 newget new object

457 /// 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 is458 ///created per row, and a direct column-name===member-name mapping is assumed (case insensitive).459 ///

460 public static IEnumerable Query(461 #if CSHARP30

462 this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType?commandType463 #else

464 this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null

465 #endif

466 )467 {468 var data = QueryInternal(cnn, sql, param as object, transaction, commandTimeout, commandType);469 return buffered ?data.ToList() : data;470 }471

472 ///

473 ///Execute a command that returns multiple result sets, and access each in turn474 ///

475 public staticGridReader QueryMultiple(476 #if CSHARP30

477 this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType?commandType478 #else

479 this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null

480 #endif

481 )482 {483 Identity identity = new Identity(sql, commandType, cnn, typeof(GridReader), (object)param == null ? null : ((object)param).GetType(), null);484 CacheInfo info =GetCacheInfo(identity);485

486 IDbCommand cmd = null;487 IDataReader reader = null;488 try

489 {490 cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);491 reader =cmd.ExecuteReader();492 return newGridReader(cmd, reader, identity);493 }494 catch

495 {496 if (reader != null) reader.Dispose();497 if (cmd != null) cmd.Dispose();498 throw;499 }500 }501

502 ///

503 ///Return a typed list of objects, reader is closed after the call504 ///

505 private static IEnumerable QueryInternal(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType?commandType)506 {507 var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);508 var info =GetCacheInfo(identity);509

510 using (var cmd =SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType))511 {512 using (var reader =cmd.ExecuteReader())513 {514 Func> cacheDeserializer = () =>

515 {516 info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);517 SetQueryCache(identity, info);518 returninfo.Deserializer;519 };520

521 if (info.Deserializer == null)522 {523 cacheDeserializer();524 }525

526 var deserializer =info.Deserializer;527

528 while(reader.Read())529 {530 objectnext;531 try

532 {533 next =deserializer(reader);534 }535 catch(DataException)536 {537 //give it another shot, in case the underlying schema changed

538 deserializer =cacheDeserializer();539 next =deserializer(reader);540 }541 yield return(T)next;542 }543

544 }545 }546 }547

548 ///

549 ///Maps a query to objects550 ///

551 /// The return type

552 ///

553 ///

554 ///

555 ///

556 ///

557 ///

558 ///

559 /// The Field we should split and read the second object from (default: id)

560 /// Number of seconds before command execution timeout

561 ///

562 public static IEnumerable Query(563 #if CSHARP30

564 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType?commandType565 #else

566 this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null

567 #endif

568 )569 {570 return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);571 }572

573 public static IEnumerable Query(574 #if CSHARP30

575 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType?commandType576 #else

577 this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null

578 #endif

579 )580 {581 return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);582 }583

584 public static IEnumerable Query(585 #if CSHARP30

586 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType?commandType587 #else

588 this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null

589 #endif

590 )591 {592 return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);593 }594 #if !CSHARP30

595 public static IEnumerable Query(this IDbConnection cnn, string sql, Func map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)596 {597 return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);598 }599 #endif

600 classDontMap { }601 static IEnumerable MultiMap(602 this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType?commandType)603 {604 var results = MultiMapImpl(cnn, sql, map, param, transaction, splitOn, commandTimeout, commandType, null, null);605 return buffered ?results.ToList() : results;606 }607

608

609 static IEnumerable MultiMapImpl(this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType?commandType, IDataReader reader, Identity identity)610 {611 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) });612 CacheInfo cinfo =GetCacheInfo(identity);613

614 IDbCommand ownedCommand = null;615 IDataReader ownedReader = null;616

617 try

618 {619 if (reader == null)620 {621 ownedCommand = SetupCommand(cnn, transaction, sql, cinfo.ParamReader, (object)param, commandTimeout, commandType);622 ownedReader =ownedCommand.ExecuteReader();623 reader =ownedReader;624 }625 Func deserializer = null;626 Func[] otherDeserializers = null;627

628 Action cacheDeserializers = () =>

629 {630 var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) }, splitOn, reader);631 deserializer = cinfo.Deserializer = deserializers[0];632 otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray();633 SetQueryCache(identity, cinfo);634 };635

636 if ((deserializer = cinfo.Deserializer) == null || (otherDeserializers = cinfo.OtherDeserializers) == null)637 {638 cacheDeserializers();639 }640

641 Func mapIt = GenerateMapper(deserializer, otherDeserializers, map);642

643 if (mapIt != null)644 {645 while(reader.Read())646 {647 TReturn next;648 try

649 {650 next =mapIt(reader);651 }652 catch(DataException)653 {654 cacheDeserializers();655 mapIt = GenerateMapper(deserializer, otherDeserializers, map);656 next =mapIt(reader);657 }658 yield returnnext;659 }660 }661 }662 finally

663 {664 try

665 {666 if (ownedReader != null)667 {668 ownedReader.Dispose();669 }670 }671 finally

672 {673 if (ownedCommand != null)674 {675 ownedCommand.Dispose();676 }677 }678 }679 }680

681 private static Func GenerateMapper(Func deserializer, Func[] otherDeserializers, objectmap)682 {683 switch(otherDeserializers.Length)684 {685 case 1:686 return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r));687 case 2:688 return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r));689 case 3:690 return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r));691 #if !CSHARP30

692 case 4:693 return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r));694 #endif

695 default:696 throw newNotSupportedException();697 }698 }699

700 private static Func[] GenerateDeserializers(Type[] types, stringsplitOn, IDataReader reader)701 {702 int current = 0;703 var splits = splitOn.Split(',').ToArray();704 var splitIndex = 0;705

706 Func nextSplit = type =>

707 {708 var currentSplit =splits[splitIndex];709 if (splits.Length > splitIndex + 1)710 {711 splitIndex++;712 }713

714 bool skipFirst = false;715 int startingPos = current + 1;716 //if our current type has the split, skip the first time you see it.

717 if (type != typeof(Object))718 {719 var props =GetSettableProps(type);720 var fields =GetSettableFields(type);721

722 foreach (var name in props.Select(p => p.Name).Concat(fields.Select(f =>f.Name)))723 {724 if (string.Equals(name, currentSplit, StringComparison.OrdinalIgnoreCase))725 {726 skipFirst = true;727 startingPos =current;728 break;729 }730 }731

732 }733

734 intpos;735 for (pos = startingPos; pos < reader.FieldCount; pos++)736 {737 //some people like ID some id ... assuming case insensitive splits for now

738 if (splitOn == "*")739 {740 break;741 }742 if (string.Equals(reader.GetName(pos), currentSplit, StringComparison.OrdinalIgnoreCase))743 {744 if(skipFirst)745 {746 skipFirst = false;747 }748 else

749 {750 break;751 }752 }753 }754 current =pos;755 returnpos;756 };757

758 var deserializers = new List>();759 int split = 0;760 bool first = true;761 foreach (var type intypes)762 {763 if (type != typeof(DontMap))764 {765 int next =nextSplit(type);766 deserializers.Add(GetDeserializer(type, reader, split, next - split, /*returnNullIfFirstMissing:*/ !first));767 first = false;768 split =next;769 }770 }771

772 returndeserializers.ToArray();773 }774

775 private staticCacheInfo GetCacheInfo(Identity identity)776 {777 CacheInfo info;778 if (!TryGetQueryCache(identity, outinfo))779 {780 info = newCacheInfo();781 if (identity.parametersType != null)782 {783 if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))784 {785 info.ParamReader = (cmd, obj) => { (obj asIDynamicParameters).AddParameters(cmd, identity); };786 }787 else

788 {789 info.ParamReader =CreateParamInfoGenerator(identity);790 }791 }792 SetQueryCache(identity, info);793 }794 returninfo;795 }796

797 private static Func GetDeserializer(Type type, IDataReader reader, int startBound, int length, boolreturnNullIfFirstMissing)798 {799 #if !CSHARP30

800 //dynamic is passed in as Object ... by c# design

801 if (type == typeof(object)802 || type == typeof(FastExpando))803 {804 returnGetDynamicDeserializer(reader, startBound, length, returnNullIfFirstMissing);805 }806 #endif

807

808 if (type.IsClass && type != typeof(string) && type != typeof(byte[]) && type != typeof(System.Data.Linq.Binary))809 {810 returnGetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);811 }812 returnGetStructDeserializer(type, startBound);813

814 }815 #if !CSHARP30

816 private class FastExpando : System.Dynamic.DynamicObject, IDictionary

817 {818 IDictionarydata;819

820 public static FastExpando Attach(IDictionarydata)821 {822 return new FastExpando { data =data };823 }824

825 public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, objectvalue)826 {827 data[binder.Name] =value;828 return true;829 }830

831 public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out objectresult)832 {833 return data.TryGetValue(binder.Name, outresult);834 }835

836 #region IDictionary Members

837

838 void IDictionary.Add(string key, objectvalue)839 {840 throw newNotImplementedException();841 }842

843 bool IDictionary.ContainsKey(stringkey)844 {845 returndata.ContainsKey(key);846 }847

848 ICollection IDictionary.Keys849 {850 get { returndata.Keys; }851 }852

853 bool IDictionary.Remove(stringkey)854 {855 throw newNotImplementedException();856 }857

858 bool IDictionary.TryGetValue(string key, out objectvalue)859 {860 return data.TryGetValue(key, outvalue);861 }862

863 ICollection IDictionary.Values864 {865 get { returndata.Values; }866 }867

868 object IDictionary.this[stringkey]869 {870 get

871 {872 returndata[key];873 }874 set

875 {876 throw newNotImplementedException();877 }878 }879

880 #endregion

881

882 #region ICollection> Members

883

884 void ICollection>.Add(KeyValuePairitem)885 {886 throw newNotImplementedException();887 }888

889 void ICollection>.Clear()890 {891 throw newNotImplementedException();892 }893

894 bool ICollection>.Contains(KeyValuePairitem)895 {896 returndata.Contains(item);897 }898

899 void ICollection>.CopyTo(KeyValuePair[] array, intarrayIndex)900 {901 data.CopyTo(array, arrayIndex);902 }903

904 int ICollection>.Count905 {906 get { returndata.Count; }907 }908

909 bool ICollection>.IsReadOnly910 {911 get { return true; }912 }913

914 bool ICollection>.Remove(KeyValuePairitem)915 {916 throw newNotImplementedException();917 }918

919 #endregion

920

921 #region IEnumerable> Members

922

923 IEnumerator> IEnumerable>.GetEnumerator()924 {925 returndata.GetEnumerator();926 }927

928 #endregion

929

930 #region IEnumerable Members

931

932 IEnumerator IEnumerable.GetEnumerator()933 {934 returndata.GetEnumerator();935 }936

937 #endregion

938 }939

940

941 private static Func GetDynamicDeserializer(IDataRecord reader, int startBound, int length, boolreturnNullIfFirstMissing)942 {943 var fieldCount =reader.FieldCount;944 if (length == -1)945 {946 length = fieldCount -startBound;947 }948

949 if (fieldCount <=startBound)950 {951 throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");952 }953

954 return

955 r =>

956 {957 IDictionary row = new Dictionary(length);958 for (var i = startBound; i < startBound + length; i++)959 {960 var tmp =r.GetValue(i);961 tmp = tmp == DBNull.Value ? null: tmp;962 row[r.GetName(i)] =tmp;963 if (returnNullIfFirstMissing && i == startBound && tmp == null)964 {965 return null;966 }967 }968 //we know this is an object so it will not box

969 returnFastExpando.Attach(row);970 };971 }972 #endif

973 [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]974 [Obsolete("This method is for internal usage only", false)]975 public static char ReadChar(objectvalue)976 {977 if (value == null || value is DBNull) throw new ArgumentNullException("value");978 string s = value as string;979 if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected", "value");980 return s[0];981 }982 [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]983 [Obsolete("This method is for internal usage only", false)]984 public static char? ReadNullableChar(objectvalue)985 {986 if (value == null || value is DBNull) return null;987 string s = value as string;988 if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected", "value");989 return s[0];990 }991 [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]992 [Obsolete("This method is for internal usage only", true)]993 public static void PackListParameters(IDbCommand command, string namePrefix, objectvalue)994 {995 //initially we tried TVP, however it performs quite poorly.996 //keep in mind SQL support up to 2000 params easily in sp_executesql, needing more is rare

997

998 var list = value asIEnumerable;999 var count = 0;1000

1001 if (list != null)1002 {1003 bool isString = value is IEnumerable;1004 foreach (var item inlist)1005 {1006 count++;1007 var listParam =command.CreateParameter();1008 listParam.ParameterName = namePrefix +count;1009 listParam.Value = item ??DBNull.Value;1010 if(isString)1011 {1012 listParam.Size = 4000;1013 if (item != null && ((string)item).Length > 4000)1014 {1015 listParam.Size = -1;1016 }1017 }1018 command.Parameters.Add(listParam);1019 }1020

1021 if (count == 0)1022 {1023 command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), "(SELECT NULL WHERE 1 = 0)");1024 }1025 else

1026 {1027 command.CommandText = Regex.Replace(command.CommandText, @"[?@:]" + Regex.Escape(namePrefix), match =>

1028 {1029 var grp =match.Value;1030 var sb = new StringBuilder("(").Append(grp).Append(1);1031 for (int i = 2; i <= count; i++)1032 {1033 sb.Append(',').Append(grp).Append(i);1034 }1035 return sb.Append(')').ToString();1036 });1037 }1038 }1039

1040 }1041

1042 private static IEnumerable FilterParameters(IEnumerable parameters, stringsql)1043 {1044 return parameters.Where(p => Regex.IsMatch(sql, "[@:]" + p.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase |RegexOptions.Multiline));1045 }1046

1047 public static ActionCreateParamInfoGenerator(Identity identity)1048 {1049 Type type =identity.parametersType;1050 bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) ==CommandType.Text;1051

1052 var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);1053

1054 var il =dm.GetILGenerator();1055

1056 il.DeclareLocal(type); //0

1057 bool haveInt32Arg1 = false;1058 il.Emit(OpCodes.Ldarg_1); //stack is now [untyped-param]

1059 il.Emit(OpCodes.Unbox_Any, type); //stack is now [typed-param]

1060 il.Emit(OpCodes.Stloc_0);//stack is now empty

1061

1062 il.Emit(OpCodes.Ldarg_0); //stack is now [command]

1063 il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); //stack is now [parameters]

1064

1065 IEnumerable props = type.GetProperties().OrderBy(p =>p.Name);1066 if(filterParams)1067 {1068 props =FilterParameters(props, identity.sql);1069 }1070 foreach (var prop inprops)1071 {1072 if(filterParams)1073 {1074 if (identity.sql.IndexOf("@" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0

1075 && identity.sql.IndexOf(":" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0)1076 { //can't see the parameter in the text (even in a comment, etc) - burn it with fire

1077 continue;1078 }1079 }1080 if (prop.PropertyType == typeof(DbString))1081 {1082 il.Emit(OpCodes.Ldloc_0); //stack is now [parameters] [typed-param]

1083 il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); //stack is [parameters] [dbstring]

1084 il.Emit(OpCodes.Ldarg_0); //stack is now [parameters] [dbstring] [command]

1085 il.Emit(OpCodes.Ldstr, prop.Name); //stack is now [parameters] [dbstring] [command] [name]

1086 il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); //stack is now [parameters]

1087 continue;1088 }1089 DbType dbType =LookupDbType(prop.PropertyType, prop.Name);1090 if (dbType ==DbType.Xml)1091 {1092 //this actually represents special handling for list types;

1093 il.Emit(OpCodes.Ldarg_0); //stack is now [parameters] [command]

1094 il.Emit(OpCodes.Ldstr, prop.Name); //stack is now [parameters] [command] [name]

1095 il.Emit(OpCodes.Ldloc_0); //stack is now [parameters] [command] [name] [typed-param]

1096 il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); //stack is [parameters] [command] [name] [typed-value]

1097 if(prop.PropertyType.IsValueType)1098 {1099 il.Emit(OpCodes.Box, prop.PropertyType); //stack is [parameters] [command] [name] [boxed-value]

1100 }1101 il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); //stack is [parameters]

1102 continue;1103 }1104 il.Emit(OpCodes.Dup); //stack is now [parameters] [parameters]

1105

1106 il.Emit(OpCodes.Ldarg_0); //stack is now [parameters] [parameters] [command]

1107 il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);//stack is now [parameters] [parameters] [parameter]

1108

1109 il.Emit(OpCodes.Dup);//stack is now [parameters] [parameters] [parameter] [parameter]

1110 il.Emit(OpCodes.Ldstr, prop.Name); //stack is now [parameters] [parameters] [parameter] [parameter] [name]

1111 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);//stack is now [parameters] [parameters] [parameter]

1112

1113 il.Emit(OpCodes.Dup);//stack is now [parameters] [parameters] [parameter] [parameter]

1114 EmitInt32(il, (int)dbType);//stack is now [parameters] [parameters] [parameter] [parameter] [db-type]

1115

1116 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);//stack is now [parameters] [parameters] [parameter]

1117

1118 il.Emit(OpCodes.Dup);//stack is now [parameters] [parameters] [parameter] [parameter]

1119 EmitInt32(il, (int)ParameterDirection.Input);//stack is now [parameters] [parameters] [parameter] [parameter] [dir]

1120 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);//stack is now [parameters] [parameters] [parameter]

1121

1122 il.Emit(OpCodes.Dup);//stack is now [parameters] [parameters] [parameter] [parameter]

1123 il.Emit(OpCodes.Ldloc_0); //stack is now [parameters] [parameters] [parameter] [parameter] [typed-param]

1124 il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); //stack is [parameters] [parameters] [parameter] [parameter] [typed-value]

1125 bool checkForNull = true;1126 if(prop.PropertyType.IsValueType)1127 {1128 il.Emit(OpCodes.Box, prop.PropertyType); //stack is [parameters] [parameters] [parameter] [parameter] [boxed-value]

1129 if (Nullable.GetUnderlyingType(prop.PropertyType) == null)1130 { //struct but not Nullable; boxed value cannot be null

1131 checkForNull = false;1132 }1133 }1134 if(checkForNull)1135 {1136 if (dbType == DbType.String && !haveInt32Arg1)1137 {1138 il.DeclareLocal(typeof(int));1139 haveInt32Arg1 = true;1140 }1141 //relative stack: [boxed value]

1142 il.Emit(OpCodes.Dup);//relative stack: [boxed value] [boxed value]

1143 Label notNull =il.DefineLabel();1144 Label? allDone = dbType == DbType.String ? il.DefineLabel() : (Label?)null;1145 il.Emit(OpCodes.Brtrue_S, notNull);1146 //relative stack [boxed value = null]

1147 il.Emit(OpCodes.Pop); //relative stack empty

1148 il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); //relative stack [DBNull]

1149 if (dbType ==DbType.String)1150 {1151 EmitInt32(il, 0);1152 il.Emit(OpCodes.Stloc_1);1153 }1154 if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);1155 il.MarkLabel(notNull);1156 if (prop.PropertyType == typeof(string))1157 {1158 il.Emit(OpCodes.Dup); //[string] [string]

1159 il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); //[string] [length]

1160 EmitInt32(il, 4000); //[string] [length] [4000]

1161 il.Emit(OpCodes.Cgt); //[string] [0 or 1]

1162 Label isLong = il.DefineLabel(), lenDone =il.DefineLabel();1163 il.Emit(OpCodes.Brtrue_S, isLong);1164 EmitInt32(il, 4000); //[string] [4000]

1165 il.Emit(OpCodes.Br_S, lenDone);1166 il.MarkLabel(isLong);1167 EmitInt32(il, -1); //[string] [-1]

1168 il.MarkLabel(lenDone);1169 il.Emit(OpCodes.Stloc_1); //[string]

1170 }1171 if (prop.PropertyType == typeof(System.Data.Linq.Binary))1172 {1173 il.EmitCall(OpCodes.Callvirt, typeof(System.Data.Linq.Binary).GetMethod("ToArray", BindingFlags.Public | BindingFlags.Instance), null);1174 }1175 if (allDone != null) il.MarkLabel(allDone.Value);1176 //relative stack [boxed value or DBNull]

1177 }1178 il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);//stack is now [parameters] [parameters] [parameter]

1179

1180 if (prop.PropertyType == typeof(string))1181 {1182 var endOfSize =il.DefineLabel();1183 //don't set if 0

1184 il.Emit(OpCodes.Ldloc_1); //[parameters] [parameters] [parameter] [size]

1185 il.Emit(OpCodes.Brfalse_S, endOfSize); //[parameters] [parameters] [parameter]

1186

1187 il.Emit(OpCodes.Dup);//stack is now [parameters] [parameters] [parameter] [parameter]

1188 il.Emit(OpCodes.Ldloc_1); //stack is now [parameters] [parameters] [parameter] [parameter] [size]

1189 il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null);//stack is now [parameters] [parameters] [parameter]

1190

1191 il.MarkLabel(endOfSize);1192 }1193

1194 il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); //stack is now [parameters]

1195 il.Emit(OpCodes.Pop); //IList.Add returns the new index (int); we don't care

1196 }1197 //stack is currently [command]

1198 il.Emit(OpCodes.Pop); //stack is now empty

1199 il.Emit(OpCodes.Ret);1200 return (Action)dm.CreateDelegate(typeof(Action));1201 }1202

1203 private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action paramReader, object obj, int? commandTimeout, CommandType?commandType)1204 {1205 var cmd =cnn.CreateCommand();1206 var bindByName =GetBindByName(cmd.GetType());1207 if (bindByName != null) bindByName(cmd, true);1208 cmd.Transaction =transaction;1209 cmd.CommandText =sql;1210 if(commandTimeout.HasValue)1211 cmd.CommandTimeout =commandTimeout.Value;1212 if(commandType.HasValue)1213 cmd.CommandType =commandType.Value;1214 if (paramReader != null)1215 {1216 paramReader(cmd, obj);1217 }1218 returncmd;1219 }1220

1221

1222 private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, string sql, Action paramReader, object obj, int? commandTimeout, CommandType?commandType)1223 {1224 using (var cmd =SetupCommand(cnn, tranaction, sql, paramReader, obj, commandTimeout, commandType))1225 {1226 returncmd.ExecuteNonQuery();1227 }1228 }1229

1230 private static Func GetStructDeserializer(Type type, intindex)1231 {1232 //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!)

1233 #pragma warning disable 618

1234 if (type == typeof(char))1235 { //this *does* need special handling, though

1236 return r =>SqlMapper.ReadChar(r.GetValue(index));1237 }1238 if (type == typeof(char?))1239 {1240 return r =>SqlMapper.ReadNullableChar(r.GetValue(index));1241 }1242 if (type == typeof(System.Data.Linq.Binary))1243 {1244 return r => new System.Data.Linq.Binary((byte[])r.GetValue(index));1245 }1246 #pragma warning restore 618

1247 return r =>

1248 {1249 var val =r.GetValue(index);1250 return val is DBNull ? null: Convert.ChangeType(val, type);1251 };1252 }1253

1254 static readonlyMethodInfo1255 enumParse = typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }),1256 getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance |BindingFlags.Public)1257 .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int))1258 .Select(p =>p.GetGetMethod()).First();1259

1260 classPropInfo1261 {1262 public string Name { get; set; }1263 public MethodInfo Setter { get; set; }1264 public Type Type { get; set; }1265 }1266

1267 static ListGetSettableProps(Type t)1268 {1269 returnt1270 .GetProperties(BindingFlags.Public | BindingFlags.NonPublic |BindingFlags.Instance)1271 .Select(p => newPropInfo1272 {1273 Name =p.Name,1274 Setter = p.DeclaringType == t ? p.GetSetMethod(true) : p.DeclaringType.GetProperty(p.Name).GetSetMethod(true),1275 Type =p.PropertyType1276 })1277 .Where(info => info.Setter != null)1278 .ToList();1279 }1280

1281 static ListGetSettableFields(Type t)1282 {1283 return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic |BindingFlags.Instance).ToList();1284 }1285

1286 public static FuncGetClassDeserializer(1287 #if CSHARP30

1288 Type type, IDataReader reader, int startBound, int length, boolreturnNullIfFirstMissing1289 #else

1290 Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false

1291 #endif

1292 )1293 {1294 var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), type, new[] { typeof(IDataReader) }, true);1295

1296 var il =dm.GetILGenerator();1297 il.DeclareLocal(typeof(int));1298 il.DeclareLocal(type);1299 bool haveEnumLocal = false;1300 il.Emit(OpCodes.Ldc_I4_0);1301 il.Emit(OpCodes.Stloc_0);1302 var properties =GetSettableProps(type);1303 var fields =GetSettableFields(type);1304 if (length == -1)1305 {1306 length = reader.FieldCount -startBound;1307 }1308

1309 if (reader.FieldCount <=startBound)1310 {1311 throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");1312 }1313

1314 var names = new List();1315

1316 for (int i = startBound; i < startBound + length; i++)1317 {1318 names.Add(reader.GetName(i));1319 }1320

1321 var setters =(1322 from n innames1323 let prop = properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) //property case sensitive first

1324 ?? properties.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase)) //property case insensitive second

1325 let field = prop != null ? null : (fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.Ordinal)) //field case sensitive third

1326 ?? fields.FirstOrDefault(p => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase))) //field case insensitive fourth

1327 select new { Name = n, Property = prop, Field =field }1328 ).ToList();1329

1330 int index =startBound;1331

1332 il.BeginExceptionBlock();1333 //stack is empty

1334 il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); //stack is now [target]

1335 bool first = true;1336 var allDone =il.DefineLabel();1337 foreach (var item insetters)1338 {1339 if (item.Property != null || item.Field != null)1340 {1341 il.Emit(OpCodes.Dup); //stack is now [target][target]

1342 Label isDbNullLabel =il.DefineLabel();1343 Label finishLabel =il.DefineLabel();1344

1345 il.Emit(OpCodes.Ldarg_0); //stack is now [target][target][reader]

1346 EmitInt32(il, index); //stack is now [target][target][reader][index]

1347 il.Emit(OpCodes.Dup);//stack is now [target][target][reader][index][index]

1348 il.Emit(OpCodes.Stloc_0);//stack is now [target][target][reader][index]

1349 il.Emit(OpCodes.Callvirt, getItem); //stack is now [target][target][value-as-object]

1350

1351

1352 Type memberType = item.Property != null ?item.Property.Type : item.Field.FieldType;1353

1354 if (memberType == typeof(char) || memberType == typeof(char?))1355 {1356 il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(1357 memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); //stack is now [target][target][typed-value]

1358 }1359 else

1360 {1361 il.Emit(OpCodes.Dup); //stack is now [target][target][value][value]

1362 il.Emit(OpCodes.Isinst, typeof(DBNull)); //stack is now [target][target][value-as-object][DBNull or null]

1363 il.Emit(OpCodes.Brtrue_S, isDbNullLabel); //stack is now [target][target][value-as-object]1364

1365 //unbox nullable enums as the primitive, i.e. byte etc

1366

1367 var nullUnderlyingType =Nullable.GetUnderlyingType(memberType);1368 var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ?nullUnderlyingType : memberType;1369

1370 if(unboxType.IsEnum)1371 {1372 if (!haveEnumLocal)1373 {1374 il.DeclareLocal(typeof(string));1375 haveEnumLocal = true;1376 }1377

1378 Label isNotString =il.DefineLabel();1379 il.Emit(OpCodes.Dup); //stack is now [target][target][value][value]

1380 il.Emit(OpCodes.Isinst, typeof(string)); //stack is now [target][target][value-as-object][string or null]

1381 il.Emit(OpCodes.Dup);//stack is now [target][target][value-as-object][string or null][string or null]

1382 il.Emit(OpCodes.Stloc_2); //stack is now [target][target][value-as-object][string or null]

1383 il.Emit(OpCodes.Brfalse_S, isNotString); //stack is now [target][target][value-as-object]

1384

1385 il.Emit(OpCodes.Pop); //stack is now [target][target]

1386

1387

1388 il.Emit(OpCodes.Ldtoken, unboxType); //stack is now [target][target][enum-type-token]

1389 il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);//stack is now [target][target][enum-type]

1390 il.Emit(OpCodes.Ldloc_2); //stack is now [target][target][enum-type][string]

1391 il.Emit(OpCodes.Ldc_I4_1); //stack is now [target][target][enum-type][string][true]

1392 il.EmitCall(OpCodes.Call, enumParse, null); //stack is now [target][target][enum-as-object]

1393

1394 il.Emit(OpCodes.Unbox_Any, unboxType); //stack is now [target][target][typed-value]

1395

1396 if (nullUnderlyingType != null)1397 {1398 il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));1399 }1400 if (item.Property != null)1401 {1402 il.Emit(OpCodes.Callvirt, item.Property.Setter); //stack is now [target]

1403 }1404 else

1405 {1406 il.Emit(OpCodes.Stfld, item.Field); //stack is now [target]

1407 }1408 il.Emit(OpCodes.Br_S, finishLabel);1409

1410

1411 il.MarkLabel(isNotString);1412 }1413 if (memberType == typeof(System.Data.Linq.Binary))1414 {1415 il.Emit(OpCodes.Unbox_Any, typeof(byte[])); //stack is now [target][target][byte-array]

1416 il.Emit(OpCodes.Newobj, typeof(System.Data.Linq.Binary).GetConstructor(new Type[] { typeof(byte[]) }));//stack is now [target][target][binary]

1417 }1418 else

1419 {1420 il.Emit(OpCodes.Unbox_Any, unboxType); //stack is now [target][target][typed-value]

1421 }1422 if (nullUnderlyingType != null &&nullUnderlyingType.IsEnum)1423 {1424 il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));1425 }1426 }1427 if (item.Property != null)1428 {1429 il.Emit(OpCodes.Callvirt, item.Property.Setter); //stack is now [target]

1430 }1431 else

1432 {1433 il.Emit(OpCodes.Stfld, item.Field); //stack is now [target]

1434 }1435

1436 il.Emit(OpCodes.Br_S, finishLabel); //stack is now [target]

1437

1438 il.MarkLabel(isDbNullLabel); //incoming stack: [target][target][value]

1439

1440 il.Emit(OpCodes.Pop); //stack is now [target][target]

1441 il.Emit(OpCodes.Pop); //stack is now [target]

1442

1443 if (first &&returnNullIfFirstMissing)1444 {1445 il.Emit(OpCodes.Pop);1446 il.Emit(OpCodes.Ldnull); //stack is now [null]

1447 il.Emit(OpCodes.Stloc_1);1448 il.Emit(OpCodes.Br, allDone);1449 }1450

1451 il.MarkLabel(finishLabel);1452 }1453 first = false;1454 index += 1;1455 }1456 il.Emit(OpCodes.Stloc_1); //stack is empty

1457 il.MarkLabel(allDone);1458 il.BeginCatchBlock(typeof(Exception)); //stack is Exception

1459 il.Emit(OpCodes.Ldloc_0); //stack is Exception, index

1460 il.Emit(OpCodes.Ldarg_0); //stack is Exception, index, reader

1461 il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);1462 il.Emit(OpCodes.Ldnull);1463 il.Emit(OpCodes.Stloc_1); //to make it verifiable

1464 il.EndExceptionBlock();1465

1466 il.Emit(OpCodes.Ldloc_1); //stack is empty

1467 il.Emit(OpCodes.Ret);1468

1469 return (Func)dm.CreateDelegate(typeof(Func));1470 }1471 public static void ThrowDataException(Exception ex, intindex, IDataReader reader)1472 {1473 string name = "(n/a)", value = "(n/a)";1474 if (reader != null && index >= 0 && index

1483 {1484 value = Convert.ToString(val) + "-" +Type.GetTypeCode(val.GetType());1485 }1486 }1487 throw new DataException(string.Format("Error parsing column {0} ({1}={2})", index, name, value), ex);1488 }1489 private static void EmitInt32(ILGenerator il, intvalue)1490 {1491 switch(value)1492 {1493 case -1: il.Emit(OpCodes.Ldc_I4_M1); break;1494 case 0: il.Emit(OpCodes.Ldc_I4_0); break;1495 case 1: il.Emit(OpCodes.Ldc_I4_1); break;1496 case 2: il.Emit(OpCodes.Ldc_I4_2); break;1497 case 3: il.Emit(OpCodes.Ldc_I4_3); break;1498 case 4: il.Emit(OpCodes.Ldc_I4_4); break;1499 case 5: il.Emit(OpCodes.Ldc_I4_5); break;1500 case 6: il.Emit(OpCodes.Ldc_I4_6); break;1501 case 7: il.Emit(OpCodes.Ldc_I4_7); break;1502 case 8: il.Emit(OpCodes.Ldc_I4_8); break;1503 default:1504 if (value >= -128 && value <= 127)1505 {1506 il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);1507 }1508 else

1509 {1510 il.Emit(OpCodes.Ldc_I4, value);1511 }1512 break;1513 }1514 }1515

1516 public classGridReader : IDisposable1517 {1518 privateIDataReader reader;1519 privateIDbCommand command;1520 privateIdentity identity;1521

1522 internalGridReader(IDbCommand command, IDataReader reader, Identity identity)1523 {1524 this.command =command;1525 this.reader =reader;1526 this.identity =identity;1527 }1528 ///

1529 ///Read the next grid of results1530 ///

1531 public IEnumerable Read()1532 {1533 if (reader == null) throw newObjectDisposedException(GetType().Name);1534 if (consumed) throw new InvalidOperationException("Each grid can only be iterated once");1535 var typedIdentity = identity.ForGrid(typeof(T), gridIndex);1536 CacheInfo cache =GetCacheInfo(typedIdentity);1537 var deserializer =cache.Deserializer;1538

1539 Func> deserializerGenerator = () =>

1540 {1541 deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);1542 cache.Deserializer =deserializer;1543 returndeserializer;1544 };1545

1546 if (deserializer == null)1547 {1548 deserializer =deserializerGenerator();1549 }1550 consumed = true;1551 return ReadDeferred(gridIndex, deserializer, typedIdentity, deserializerGenerator);1552 }1553

1554 private IEnumerable MultiReadInternal(object func, stringsplitOn)1555 {1556

1557 var identity = this.identity.ForGrid(typeof(TReturn), newType[] {1558 typeof(TFirst),1559 typeof(TSecond),1560 typeof(TThird),1561 typeof(TFourth),1562 typeof(TFifth)1563 }, gridIndex);1564 try

1565 {1566 foreach (var r in SqlMapper.MultiMapImpl(null, null, func, null, null, splitOn, null, null, reader, identity))1567 {1568 yield returnr;1569 }1570 }1571 finally

1572 {1573 NextResult();1574 }1575 }1576

1577 #if CSHARP30

1578 public IEnumerable Read(Func func, stringsplitOn)1579 #else

1580 public IEnumerable Read(Func func, string splitOn = "id")1581 #endif

1582 {1583 return MultiReadInternal(func, splitOn);1584 }1585

1586 #if CSHARP30

1587 public IEnumerable Read(Func func, stringsplitOn)1588 #else

1589 public IEnumerable Read(Func func, string splitOn = "id")1590 #endif

1591 {1592 return MultiReadInternal(func, splitOn);1593 }1594

1595 #if CSHARP30

1596 public IEnumerable Read(Func func, stringsplitOn)1597 #else

1598 public IEnumerable Read(Func func, string splitOn = "id")1599 #endif

1600 {1601 return MultiReadInternal(func, splitOn);1602 }1603

1604 #if !CSHARP30

1605 public IEnumerable Read(Func func, string splitOn = "id")1606 {1607 return MultiReadInternal(func, splitOn);1608 }1609 #endif

1610

1611 private IEnumerable ReadDeferred(int index, Func deserializer, Identity typedIdentity, Func>deserializerGenerator)1612 {1613 try

1614 {1615 while (index == gridIndex &&reader.Read())1616 {1617 objectnext;1618 try

1619 {1620 next =deserializer(reader);1621 }1622 catch(DataException)1623 {1624 deserializer =deserializerGenerator();1625 next =deserializer(reader);1626 }1627 yield return(T)next;1628 }1629 }1630 finally //finally so that First etc progresses things even when multiple rows

1631 {1632 if (index ==gridIndex)1633 {1634 NextResult();1635 }1636 }1637 }1638 private intgridIndex;1639 private boolconsumed;1640 private voidNextResult()1641 {1642 if(reader.NextResult())1643 {1644 gridIndex++;1645 consumed = false;1646 }1647 else

1648 {1649 Dispose();1650 }1651

1652 }1653 public voidDispose()1654 {1655 if (reader != null)1656 {1657 reader.Dispose();1658 reader = null;1659 }1660 if (command != null)1661 {1662 command.Dispose();1663 command = null;1664 }1665 }1666 }1667 }1668

1669 public classDynamicParameters : SqlMapper.IDynamicParameters1670 {1671 static Dictionary> paramReaderCache = new Dictionary>();1672

1673 Dictionary parameters = new Dictionary();1674 Listtemplates;1675

1676 classParamInfo1677 {1678 public string Name { get; set; }1679 public object Value { get; set; }1680 public ParameterDirection ParameterDirection { get; set; }1681 public DbType? DbType { get; set; }1682 public int? Size { get; set; }1683 public IDbDataParameter AttachedParam { get; set; }1684 }1685

1686 publicDynamicParameters() { }1687 public DynamicParameters(objecttemplate)1688 {1689 if (template != null)1690 {1691 AddDynamicParams(template);1692 }1693 }1694

1695 ///

1696 ///Append a whole object full of params to the dynamic1697 ///EG: AddParams(new {A = 1, B = 2}) // will add property A and B to the dynamic1698 ///

1699 ///

1700 public voidAddDynamicParams(1701 #if CSHARP30

1702 objectparam1703 #else

1704 dynamicparam1705 #endif

1706 )1707 {1708 object obj = param as object;1709

1710 if (obj != null)1711 {1712 templates = templates ?? new List();1713 templates.Add(obj);1714 }1715 }1716

1717

1718 public voidAdd(1719 #if CSHARP30

1720 string name, object value, DbType? dbType, ParameterDirection? direction, int?size1721 #else

1722 string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null

1723 #endif

1724 )1725 {1726 parameters[Clean(name)] = new ParamInfo() { Name = name, Value = value, ParameterDirection = direction ?? ParameterDirection.Input, DbType = dbType, Size =size };1727 }1728

1729 static string Clean(stringname)1730 {1731 if (!string.IsNullOrEmpty(name))1732 {1733 switch (name[0])1734 {1735 case '@':1736 case ':':1737 case '?':1738 return name.Substring(1);1739 }1740 }1741 returnname;1742 }1743

1744 voidSqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)1745 {1746 if (templates != null)1747 {1748 foreach (var template intemplates)1749 {1750 var newIdent =identity.ForDynamicParameters(template.GetType());1751 Actionappender;1752

1753 lock(paramReaderCache)1754 {1755 if (!paramReaderCache.TryGetValue(newIdent, outappender))1756 {1757 appender =SqlMapper.CreateParamInfoGenerator(newIdent);1758 paramReaderCache[newIdent] =appender;1759 }1760 }1761

1762 appender(command, template);1763 }1764 }1765

1766 foreach (var param inparameters.Values)1767 {1768 var p =command.CreateParameter();1769 var val =param.Value;1770 p.ParameterName =param.Name;1771 p.Value = val ??DBNull.Value;1772 p.Direction =param.ParameterDirection;1773 var s = val as string;1774 if (s != null)1775 {1776 if (s.Length <= 4000)1777 {1778 p.Size = 4000;1779 }1780 }1781 if (param.Size != null)1782 {1783 p.Size =param.Size.Value;1784 }1785 if (param.DbType != null)1786 {1787 p.DbType =param.DbType.Value;1788 }1789 command.Parameters.Add(p);1790 param.AttachedParam =p;1791 }1792 }1793

1794 public T Get(stringname)1795 {1796 var val =parameters[Clean(name)].AttachedParam.Value;1797 if (val ==DBNull.Value)1798 {1799 if (default(T) != null)1800 {1801 throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!");1802 }1803 return default(T);1804 }1805 return(T)val;1806 }1807 }1808

1809 public classOracleDynamicParameters : SqlMapper.IDynamicParameters1810 {1811 private readonly DynamicParameters dynamicParameters = newDynamicParameters();1812

1813 private readonly List oracleParameters = new List();1814

1815 public void Add(string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null)1816 {1817 dynamicParameters.Add(name, value, dbType, direction, size);1818 }1819

1820 public void Add(stringname, OracleDbType oracleDbType, ParameterDirection direction)1821 {1822 var oracleParameter = newOracleParameter(name, oracleDbType, direction);1823 oracleParameters.Add(oracleParameter);1824 }1825

1826 public voidAddParameters(IDbCommand command, SqlMapper.Identity identity)1827 {1828 ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);1829

1830 var oracleCommand = command asOracleCommand;1831

1832 if (oracleCommand != null)1833 {1834 oracleCommand.Parameters.AddRange(oracleParameters.ToArray());1835 }1836 }1837 }1838

1839 public sealed classDbString1840 {1841 public DbString() { Length = -1; }1842 public bool IsAnsi { get; set; }1843 public bool IsFixedLength { get; set; }1844 public int Length { get; set; }1845 public string Value { get; set; }1846 public void AddParameter(IDbCommand command, stringname)1847 {1848 if (IsFixedLength && Length == -1)1849 {1850 throw new InvalidOperationException("If specifying IsFixedLength, a Length must also be specified");1851 }1852 var param =command.CreateParameter();1853 param.ParameterName =name;1854 param.Value = (object)Value ??DBNull.Value;1855 if (Length == -1 && Value != null && Value.Length <= 4000)1856 {1857 param.Size = 4000;1858 }1859 else

1860 {1861 param.Size =Length;1862 }1863 param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ?DbType.StringFixedLength : DbType.String);1864 command.Parameters.Add(param);1865 }1866 }1867 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值