dapper,给力分享新的ORM => Dapper

最近一直很痛苦,想选一个好点的ORM来做项目,实在没遇到好的。昨天忽然的看到Dapper,立刻迷恋上了。这个ORM实在太强大了,可惜资料少的可怜。就发点资料来介绍吧。官方资料点击这里
Dapper是一个轻型的ORM类。代码就一个SqlMapper.cs文件。文件见下。编译后就40K的一个很小的Dll.(估计其他的ORM要哭了。)
Dapper很快,有多快。实验下就知道了。官方给了点测试包,想玩的时候就去测试下。Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。(更多的ORM泪奔了,好多ORM弱爆了。)
Dapper支持什么数据库。Dapper支持Mysql,SqlLite,Mssql2000,Mssql2005,Oracle等一系列的数据库,当然如果你知道原理也可以让它支持Mongo db.(有的ORM泪奔了)
Dapper的r支持多表并联的对象。支持一对多 多对多的关系。并且没侵入性,想用就用,不想用就不用。无XML无属性。代码以前怎么写现在还怎么写。(更更更更多的ORM泪奔了,看到其他的ORM又是XML又是属性,Map来Map去,设计来设计去,缓存来缓存去,然后还是那么的慢。)
Dapper原理通过Emit反射IDataReader的序列队列,来快速的得到和产生对象。性能实在高高高。(更更更更更更更多的ORM泪奔了)
Dapper支持net2.0,3.0,3.5,4.0。不过就是要配置下。如果不知道如何配置查看我博客里的在2.0下使用3.5就可以了。(更更更更更更更更更更更多的ORM泪奔了)
Dapper的语法是这样的。语法十分简单。并且无须迁就数据库的设计。(我X,无敌了)
public static readonly string sqlconnectionString = "Data Source=xxx;Initial Catalog=Express;User ID=sa;Password=123"; public static readonly string mysqlconnectionString = @"server=xxx;database=dddd;uid=xxx;pwd=123;charset='gbk'"; public static SqlConnection SqlConnection() { var connection = new SqlConnection(sqlconnectionString); connection.Open(); return connection; } public static MySqlConnection MySqlConnection() { var connection = new MySqlConnection(mysqlconnectionString); connection.Open(); return connection; }
调用方法
SqlConnection connection = Program.SqlConnection(); for (int i = 0; i < 100000; i++) { //connection.Execute("INSERT INTO dog (Age,Name,Weight) VALUES (@age,@name,@Weight)", // new { @age = i,@name = Guid.NewGuid().ToString(), @Weight = i }); } var d = connection.Query("select * from dog where id = 1", null).Single(); var dd = connection.Query("select * from dog where id < 10", null).ToList();
试用感觉
30W条数据,取其中的一个对象,和第一页前15条数据,耗时0.0906879秒。这个速度超过Datable。
官方的测试代码以及数据

Performance of SELECT mapping over 500 iterations - POCO serialization

Method Duration Remarks
Hand coded (using a SqlDataReader) 47ms
Dapper ExecuteMapperQuery 49ms
PetaPoco 52ms Can be faster
BLToolkit 80ms
SubSonic CodingHorror 107ms
NHibernate SQL 104ms
Linq 2 SQL ExecuteQuery 181ms
Entity framework ExecuteStoreQuery 631ms

Performance of SELECT mapping over 500 iterations - dynamic serialization

Method Duration Remarks
Dapper ExecuteMapperQuery (dynamic) 48ms
Massive 52ms
Simple.Data 95ms

Performance of SELECT mapping over 500 iterations - typical usage

Method Duration Remarks
Linq 2 SQL CompiledQuery 81ms Not super typical involves complex code
NHibernate HQL 118ms
Linq 2 SQL 559ms
Entity framework 859ms
SubSonic ActiveRecord.SingleOrDefault 3619ms
Performance benchmarks are available here: http://code.google.com/p/dapper-dot-net/source/browse/Tests/PerformanceTests.cs , Feel free to submit patches that include other ORMs - when running benchmarks, be sure to compile in Release and not attach a debugger (ctrl F5)
内存控制 非常好。
结论不管你用不用,我反正是用它了。
我编译好了一份发上来了。点击下载。代码如下有点长
/* License: http://www.apache.org/licenses/LICENSE-2.0 Home page: http://code.google.com/p/dapper-dot-net/ Note: to build _disibledevent=> action; if (Link>.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)method.CreateDelegate(typeof(Action)); } // cache it Link>.TryAdd(ref bindByNameCache, commandType, ref action); return action; } /// /// 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 _disibledevent=> head, TKey key, ref TValue value) { bool tryAgain; do { var snapshot = Interlocked.CompareExchange(ref head, null, null); TValue found; if (TryGet(snapshot, key, out found)) { // existing match; report the existing value instead value = found; return false; } var newNode = new Link(key, value, snapshot); // did somebody move our cheese? tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot; } while (tryAgain); return true; } private Link(TKey key, TValue value, Link tail) { Key = key; Value = value; Tail = tail; } public TKey Key { get; private set; } public TValue Value { get; private set; } public Link Tail { get; private set; } } class CacheInfo { public Func Deserializer { get; set; } public Func[] OtherDeserializers { get; set; } public Action ParamReader { get; set; } private int hitCount; public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); } public void RecordHit() { Interlocked.Increment(ref hitCount); } } public static event EventHandler QueryCachePurged; private static void _disibledevent=> _queryCache = new Dictionary(); // note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of // ReaderWriterLockSlim etc; a simple lock is faster private static void SetQueryCache(Identity key, CacheInfo value) { lock (_queryCache) { _queryCache[key] = value; } } private static bool TryGetQueryCache(Identity key, out CacheInfo value) { lock (_queryCache) { return _queryCache.TryGetValue(key, out value); } } public static void PurgeQueryCache() { lock (_queryCache) { _queryCache.Clear(); } _disibledevent=>(); 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(); _disibledevent=> 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> GetHashCollissions() { var counts = new Dictionary(); foreach(var key in _queryCache.Keys) { int count; if(!counts.TryGetValue(key.hashCode, out count)) { counts.Add(key.hashCode, 1); } else { counts[key.hashCode] = count + 1; } } return from pair in counts where pair.Value > 1 select Tuple.Create(pair.Key, pair.Value); } #endif static readonly Dictionary typeMap; static SqlMapper() { typeMap = new Dictionary(); typeMap[typeof(byte)] = DbType.Byte; typeMap[typeof(sbyte)] = DbType.SByte; typeMap[typeof(short)] = DbType.Int16; typeMap[typeof(ushort)] = DbType.UInt16; typeMap[typeof(int)] = DbType.Int32; typeMap[typeof(uint)] = DbType.UInt32; typeMap[typeof(long)] = DbType.Int64; typeMap[typeof(ulong)] = DbType.UInt64; typeMap[typeof(float)] = DbType.Single; typeMap[typeof(double)] = DbType.Double; typeMap[typeof(decimal)] = DbType.Decimal; typeMap[typeof(bool)] = DbType.Boolean; typeMap[typeof(string)] = DbType.String; typeMap[typeof(char)] = DbType.StringFixedLength; typeMap[typeof(Guid)] = DbType.Guid; typeMap[typeof(DateTime)] = DbType.DateTime; typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset; typeMap[typeof(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; } 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 _disibledevent=> /// Execute parameterized SQL /// /// Number of rows affected public static int Execute(this IDbConnection cnn, string sql, object param) { return Execute(cnn, sql, param, null, null, null); } /// /// Executes a query, returning the data typed as per T /// /// 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 get new object /// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive). /// public static IEnumerable Query(this IDbConnection cnn, string sql, object param) { return Query(cnn, sql, param, null, true, null, null); } #endif /// /// Execute parameterized SQL /// /// Number of rows affected public static int Execute( #if CSHARP30 this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType #else this IDbConnection cnn, string sql, 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 _disibledevent=> /// Return a list of dynamic objects, reader is closed after the call /// public static IEnumerable Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) { return Query(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType); } #endif /// /// Executes a query, returning the data typed as per T /// /// 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 get new object /// A sequence of data of the supplied type; if a basic type (int, string, etc) is queried then the data from the first column in assumed, otherwise an instance is /// created per row, and a direct column-name===member-name mapping is assumed (case insensitive). /// public static IEnumerable Query( #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(cnn, sql, param as object, transaction, commandTimeout, commandType); return buffered ? data.ToList() : data; } /// /// Execute a command that returns multiple result sets, and access each in turn /// 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; } } /// /// Return a typed list of objects, reader is closed after the call /// private static IEnumerable QueryInternal(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); using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType)) { using (var reader = cmd.ExecuteReader()) { Func> 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; } } } } /// /// Maps a query to objects /// /// The return type /// ///
///
///
///
///
///
/// The Field we should split and read the second object from (default: id)
/// Number of seconds before command execution timeout
/// public static IEnumerable Query( #if CSHARP30 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType #else 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 #endif ) { return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType); } public static IEnumerable Query( #if CSHARP30 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType #else 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 #endif ) { return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType); } public static IEnumerable Query( #if CSHARP30 this IDbConnection cnn, string sql, Func map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType #else 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 #endif ) { return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType); } #if !CSHARP30 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) { return MultiMap(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType); } #endif class DontMap { } static IEnumerable MultiMap( this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType) { var results = MultiMapImpl(cnn, sql, map, param, transaction, splitOn, commandTimeout, commandType, null, null); return buffered ? results.ToList() : results; } static IEnumerable MultiMapImpl(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) }); 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 deserializer = null; Func[] otherDeserializers = null; Action cacheDeserializers = () => { var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth)}, 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 mapIt = GenerateMapper(deserializer, otherDeserializers, map); if (mapIt != null) { while (reader.Read()) { TReturn next; try { next = mapIt(reader); } catch (DataException) { cacheDeserializers(); mapIt = GenerateMapper(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 GenerateMapper(Func deserializer, Func[] otherDeserializers, object map) { switch(otherDeserializers.Length) { case 1: return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r)); case 2: return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r)); case 3: return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r)); #if !CSHARP30 case 4: return r => ((Func)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r)); #endif default: throw new NotSupportedException(); } } private static Func[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader) { int current = 0; var splits = splitOn.Split(',').ToArray(); var splitIndex = 0; Func 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>(); 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(); } private 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; } private static Func 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[]) && type != typeof(System.Data.Linq.Binary)) { return GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing); } return GetStructDeserializer(type, startBound); } #if !CSHARP30 private class FastExpando : System.Dynamic.DynamicObject, IDictionary { IDictionary data; public static FastExpando Attach(IDictionary 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 Members void IDictionary.Add(string key, object value) { throw new NotImplementedException(); } bool IDictionary.ContainsKey(string key) { return data.ContainsKey(key); } ICollection IDictionary.Keys { get { return data.Keys; } } bool IDictionary.Remove(string key) { throw new NotImplementedException(); } bool IDictionary.TryGetValue(string key, out object value) { return data.TryGetValue(key, out value); } ICollection IDictionary.Values { get { return data.Values; } } object IDictionary.this[string key] { get { return data[key]; } set { throw new NotImplementedException(); } } #endregion #region ICollection> Members void ICollection>.Add(KeyValuePair item) { throw new NotImplementedException(); } void ICollection>.Clear() { throw new NotImplementedException(); } bool ICollection>.Contains(KeyValuePair item) { return data.Contains(item); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { data.CopyTo(array, arrayIndex); } int ICollection>.Count { get { return data.Count; } } bool ICollection>.IsReadOnly { get { return true; } } bool ICollection>.Remove(KeyValuePair item) { throw new NotImplementedException(); } #endregion #region IEnumerable> Members IEnumerator> IEnumerable>.GetEnumerator() { return data.GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return data.GetEnumerator(); } #endregion } private static Func 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 row = new Dictionary(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 _disibledevent=>; 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 FilterParameters(IEnumerable parameters, string sql) { return parameters.Where(p => Regex.IsMatch(sql, "[@:]" + p.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline)); } public static Action 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 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; 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)dm.CreateDelegate(typeof(Action)); } private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action 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 paramReader, object obj, int? commandTimeout, CommandType? commandType) { using (var cmd = SetupCommand(cnn, tranaction, sql, paramReader, obj, commandTimeout, commandType)) { return cmd.ExecuteNonQuery(); } } private static Func 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 (type == typeof(char)) { // this *does* need special handling, though return r => SqlMapper.ReadChar(r.GetValue(index)); } if (type == typeof(char?)) { return r => SqlMapper.ReadNullableChar(r.GetValue(index)); } if (type == typeof(System.Data.Linq.Binary)) { return r => new System.Data.Linq.Binary((byte[])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 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 GetSettableFields(Type t) { return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList(); } public static Func 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(); 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 (memberType == typeof(char) || memberType == typeof(char?)) { il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod( memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", 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)dm.CreateDelegate(typeof(Func)); } 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 = ""; } 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; } } public class GridReader : IDisposable { private IDataReader reader; private IDbCommand command; private Identity identity; internal GridReader(IDbCommand command, IDataReader reader, Identity identity) { this.command = command; this.reader = reader; this.identity = identity; } /// /// Read the next grid of results /// public IEnumerable Read() { if (reader == null) throw new ObjectDisposedException(GetType().Name); if (consumed) throw new InvalidOperationException("Each grid can _disibledevent=>> deserializerGenerator = () => { deserializer = GetDeserializer(typeof(T), reader, 0, -1, false); cache.Deserializer = deserializer; return deserializer; }; if (deserializer == null) { deserializer = deserializerGenerator(); } consumed = true; return ReadDeferred(gridIndex, deserializer, typedIdentity, deserializerGenerator); } private IEnumerable MultiReadInternal(object func, string splitOn) { var identity = this.identity.ForGrid(typeof(TReturn), new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) }, gridIndex); try { foreach (var r in SqlMapper.MultiMapImpl(null, null, func, null, null, splitOn, null, null, reader, identity)) { yield return r; } } finally { NextResult(); } } #if CSHARP30 public IEnumerable Read(Func func, string splitOn) #else public IEnumerable Read(Func func, string splitOn = "id") #endif { return MultiReadInternal(func, splitOn); } #if CSHARP30 public IEnumerable Read(Func func, string splitOn) #else public IEnumerable Read(Func func, string splitOn = "id") #endif { return MultiReadInternal(func, splitOn); } #if CSHARP30 public IEnumerable Read(Func func, string splitOn) #else public IEnumerable Read(Func func, string splitOn = "id") #endif { return MultiReadInternal(func, splitOn); } #if !CSHARP30 public IEnumerable Read(Func func, string splitOn = "id") { return MultiReadInternal(func, splitOn); } #endif private IEnumerable ReadDeferred(int index, Func deserializer, Identity typedIdentity, Func> deserializerGenerator) { try { while (index == gridIndex && reader.Read()) { object next; try { next = deserializer(reader); } catch (DataException) { deserializer = deserializerGenerator(); next = deserializer(reader); } yield return (T)next; } } finally // finally so that First etc progresses things even when multiple rows { if (index == gridIndex) { NextResult(); } } } private int gridIndex; private bool consumed; private void NextResult() { if (reader.NextResult()) { gridIndex++; consumed = false; } else { Dispose(); } } public void Dispose() { if (reader != null) { reader.Dispose(); reader = null; } if (command != null) { command.Dispose(); command = null; } } } } public class DynamicParameters : SqlMapper.IDynamicParameters { static Dictionary> paramReaderCache = new Dictionary>(); Dictionary parameters = new Dictionary(); List templates; class ParamInfo { public string Name { get; set; } public object Value { get; set; } public ParameterDirection ParameterDirection { get; set; } public DbType? DbType { get; set; } public int? Size { get; set; } public IDbDataParameter AttachedParam { get; set; } } public DynamicParameters() { } public DynamicParameters(object template) { if (template != null) { AddDynamicParams(template); } } /// /// Append a whole object full of params to the dynamic /// EG: AddParams(new {A = 1, B = 2}) // will add property A and B to the dynamic /// ///
public void AddDynamicParams( #if CSHARP30 object param #else dynamic param #endif ) { object obj = param as object; if (obj != null) { templates = templates ?? new List(); templates.Add(obj); } } public void Add( #if CSHARP30 string name, object value, DbType? dbType, ParameterDirection? direction, int? size #else string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null #endif ) { parameters[Clean(name)] = new ParamInfo() { Name = name, Value = value, ParameterDirection = direction ?? ParameterDirection.Input, DbType = dbType, Size = size }; } static string Clean(string name) { if (!string.IsNullOrEmpty(name)) { switch (name[0]) { case '@': case ':': case '?': return name.Substring(1); } } return name; } void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) { if (templates != null) { foreach (var template in templates) { var newIdent = identity.ForDynamicParameters(template.GetType()); Action appender; lock (paramReaderCache) { if (!paramReaderCache.TryGetValue(newIdent, out appender)) { appender = SqlMapper.CreateParamInfoGenerator(newIdent); paramReaderCache[newIdent] = appender; } } appender(command, template); } } foreach (var param in parameters.Values) { var p = command.CreateParameter(); var val = param.Value; p.ParameterName = param.Name; p.Value = val ?? DBNull.Value; p.Direction = param.ParameterDirection; var s = val as string; if (s != null) { if (s.Length <= 4000) { p.Size = 4000; } } if (param.Size != null) { p.Size = param.Size.Value; } if (param.DbType != null) { p.DbType = param.DbType.Value; } command.Parameters.Add(p); param.AttachedParam = p; } } public T Get(string name) { var val = parameters[Clean(name)].AttachedParam.Value; if (val == DBNull.Value) { if (default(T) != null) { throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!"); } return default(T); } return (T)val; } } public sealed class DbString { public DbString() { Length = -1; } public bool IsAnsi { get; set; } public bool IsFixedLength { get; set; } public int Length { get; set; } public string Value { get; set; } public void AddParameter(IDbCommand command, string name) { if (IsFixedLength && Length == -1) { throw new InvalidOperationException("If specifying IsFixedLength, a Length must also be specified"); } var param = command.CreateParameter(); param.ParameterName = name; param.Value = (object)Value ?? DBNull.Value; if (Length == -1 && Value != null && Value.Length <= 4000) { param.Size = 4000; } else { param.Size = Length; } param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String); command.Parameters.Add(param); } } }
Tags:  标准分享 分享到 淘宝分享 分享家 dapper

延伸阅读

最新评论

发表评论