dapper

 支持framework4.0

/*
 License: http://www.apache.org/licenses/LICENSE-2.0 
 Home page: http://code.google.com/p/dapper-dot-net/
 Note: to build on C# 3.0 + .NET 3.5, include the CSHARP30 compiler symbol (and yes,
 I know the difference between language and runtime versions; this is a compromise).
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Globalization;
using System.Linq.Expressions;


namespace Dapper
{
    [AssemblyNeutral, AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
    internal sealed class AssemblyNeutralAttribute : Attribute { }


    ///
    /// Additional state flags that control command behaviour
    ///
    [Flags]
    public enum CommandFlags
    {
        ///
        /// No additional flags
        ///
        None = 0,
        ///
        /// Should data be buffered before returning?
        ///
        Buffered = 1,
        ///
        /// Can async queries be pipelined?
        ///
        Pipelined = 2,
        ///
        /// Should the plan cache be bypassed?
        ///
        NoCache = 4,
    }
    ///
    /// Represents the key aspects of a sql operation
    ///
    public struct CommandDefinition
    {
        internal static CommandDefinition ForCallback(object parameters)
        {
            if (parameters is DynamicParameters)
            {
                return new CommandDefinition(parameters);
            }
            else
            {
                return default(CommandDefinition);
            }
        }
        private readonly string commandText;
        private readonly object parameters;
        private readonly IDbTransaction transaction;
        private readonly int? commandTimeout;
        private readonly CommandType? commandType;
        private readonly CommandFlags flags;




        internal void OnCompleted()
        {
            if (parameters is SqlMapper.IParameterCallbacks)
            {
                ((SqlMapper.IParameterCallbacks)parameters).OnCompleted();
            }
        }
        ///
        /// The command (sql or a stored-procedure name) to execute
        ///
        public string CommandText { get { return commandText; } }
        ///
        /// The parameters associated with the command
        ///
        public object Parameters { get { return parameters; } }
        ///
        /// The active transaction for the command
        ///
        public IDbTransaction Transaction { get { return transaction; } }
        ///
        /// The effective timeout for the command
        ///
        public int? CommandTimeout { get { return commandTimeout; } }
        ///
        /// The type of command that the command-text represents
        ///
        public CommandType? CommandType { get { return commandType; } }


        ///
        /// Should data be buffered before returning?
        ///
        public bool Buffered { get { return (flags & CommandFlags.Buffered) != 0; } }


        ///
        /// Should the plan for this query be cached?
        ///
        internal bool AddToCache { get { return (flags & CommandFlags.NoCache) == 0; } }


        ///
        /// Additional state flags against this command
        ///
        public CommandFlags Flags { get { return flags; } }


        ///
        /// Can async queries be pipelined?
        ///
        public bool Pipelined { get { return (flags & CommandFlags.Pipelined) != 0; } }


        ///
        /// Initialize the command definition
        ///
#if CSHARP30
        public CommandDefinition(string commandText, object parameters, IDbTransaction transaction, int? commandTimeout,
            CommandType? commandType, CommandFlags flags)
#else
        public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered
#if ASYNC
            , CancellationToken cancellationToken = default(CancellationToken)
#endif
)
#endif
        {
            this.commandText = commandText;
            this.parameters = parameters;
            this.transaction = transaction;
            this.commandTimeout = commandTimeout;
            this.commandType = commandType;
            this.flags = flags;
#if ASYNC
            this.cancellationToken = cancellationToken;
#endif
        }


        private CommandDefinition(object parameters)
            : this()
        {
            this.parameters = parameters;
        }


#if ASYNC
        private readonly CancellationToken cancellationToken;
        ///
        /// For asynchronous operations, the cancellation-token
        ///
        public CancellationToken CancellationToken { get { return cancellationToken; } }
#endif


        internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> paramReader)
        {
            var cmd = cnn.CreateCommand();
            var init = GetInit(cmd.GetType());
            if (init != null) init(cmd);
            if (transaction != null)
                cmd.Transaction = transaction;
            cmd.CommandText = commandText;
            if (commandTimeout.HasValue)
                cmd.CommandTimeout = commandTimeout.Value;
            if (commandType.HasValue)
                cmd.CommandType = commandType.Value;
            if (paramReader != null)
            {
                paramReader(cmd, parameters);
            }
            return cmd;
        }


        static SqlMapper.Link<Type, Action> commandInitCache;
        static Action GetInit(Type commandType)
        {
            if (commandType == null) return null; // GIGO
            Action action;
            if (SqlMapper.Link<Type, Action>.TryGet(commandInitCache, commandType, out action))
            {
                return action;
            }
            var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
            var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));


            action = null;
            if (bindByName != null || initialLongFetchSize != null)
            {
                var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
                var il = method.GetILGenerator();


                if (bindByName != null)
                {
                    // .BindByName = true
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Castclass, commandType);
                    il.Emit(OpCodes.Ldc_I4_1);
                    il.EmitCall(OpCodes.Callvirt, bindByName, null);
                }
                if (initialLongFetchSize != null)
                {
                    // .InitialLONGFetchSize = -1
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Castclass, commandType);
                    il.Emit(OpCodes.Ldc_I4_M1);
                    il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null);
                }
                il.Emit(OpCodes.Ret);
                action = (Action)method.CreateDelegate(typeof(Action));
            }
            // cache it            
            SqlMapper.Link<Type, Action>.TryAdd(ref commandInitCache, commandType, ref action);
            return action;
        }
        static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType)
        {
            var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
            ParameterInfo[] indexers;
            if (prop != null && prop.CanWrite && prop.PropertyType == expectedType
                && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0))
            {
                return prop.GetSetMethod();
            }
            return null;
        }
    }


    ///
    /// Dapper, a light weight object mapper for ADO.NET
    ///
    static partial class SqlMapper
    {
        ///
        /// Implement this interface to pass an arbitrary db specific set of parameters to Dapper
        ///
        public partial interface IDynamicParameters
        {
            ///
            /// Add all the parameters needed to the command just before it executes
            ///
            ///The raw command prior to execution
            ///Information about the query
            void AddParameters(IDbCommand command, Identity identity);
        }


        ///
        /// Extends IDynamicParameters providing by-name lookup of parameter values
        ///
        public interface IParameterLookup : IDynamicParameters
        {
            ///
            /// Get the value of the specified parameter (return null if not found)
            ///
            object this[string name] { get; }
        }


        ///
        /// Extends IDynamicParameters with facilities for executing callbacks after commands have completed
        ///
        public partial interface IParameterCallbacks : IDynamicParameters
        {
            ///
            /// Invoked when the command has executed
            ///
            void OnCompleted();
        }


        ///
        /// Implement this interface to pass an arbitrary db specific parameter to Dapper
        ///
        [AssemblyNeutral]
        public interface ICustomQueryParameter
        {
            ///
            /// Add the parameter needed to the command before it executes
            ///
            ///The raw command prior to execution
            ///Parameter name
            void AddParameter(IDbCommand command, string name);
        }


        ///
        /// Implement this interface to perform custom type-based parameter handling and value parsing
        ///
        [AssemblyNeutral]
        public interface ITypeHandler
        {
            ///
            /// Assign the value of a parameter before a command executes
            ///
            ///The parameter to configure
            ///Parameter value
            void SetValue(IDbDataParameter parameter, object value);


            ///
            /// Parse a database value back to a typed value
            ///
            ///The value from the database
            ///The type to parse to
            ///The typed value
            object Parse(Type destinationType, object value);
        }


        ///
        /// A type handler for data-types that are supported by the underlying provider, but which need
        /// a well-known UdtTypeName to be specified
        ///
        public class UdtTypeHandler : ITypeHandler
        {
            private readonly string udtTypeName;
            ///
            /// Creates a new instance of UdtTypeHandler with the specified UdtTypeName
            ///
            public UdtTypeHandler(string udtTypeName)
            {
                if (string.IsNullOrEmpty(udtTypeName)) throw new ArgumentException("Cannot be null or empty", udtTypeName);
                this.udtTypeName = udtTypeName;
            }
            object ITypeHandler.Parse(Type destinationType, object value)
            {
                return value is DBNull ? null : value;
            }


            void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
            {
                parameter.Value = ((object)value) ?? DBNull.Value;
                if (parameter is System.Data.SqlClient.SqlParameter)
                {
                    ((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
                }
            }
        }


        ///
        /// Base-class for simple type-handlers
        ///
        public abstract class TypeHandler : ITypeHandler
        {
            ///
            /// Assign the value of a parameter before a command executes
            ///
            ///The parameter to configure
            ///Parameter value
            public abstract void SetValue(IDbDataParameter parameter, T value);


            ///
            /// Parse a database value back to a typed value
            ///
            ///The value from the database
            ///The typed value
            public abstract T Parse(object value);


            void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
            {
                if (value is DBNull)
                {
                    parameter.Value = value;
                }
                else
                {
                    SetValue(parameter, (T)value);
                }
            }


            object ITypeHandler.Parse(Type destinationType, object value)
            {
                return Parse(value);
            }
        }


        ///
        /// Implement this interface to change default mapping of reader columns to type members
        ///
        public interface ITypeMap
        {
            ///
            /// Finds best constructor
            ///
            ///DataReader column names
            ///DataReader column types
            ///Matching constructor or default one
            ConstructorInfo FindConstructor(string[] names, Type[] types);


            ///
            /// Returns a constructor which should *always* be used.
            /// 
            /// Parameters will be default values, nulls for reference types and zero'd for value types.
            /// 
            /// Use this class to force object creation away from parameterless constructors you don't control.
            ///
            ConstructorInfo FindExplicitConstructor();


            ///
            /// Gets mapping for constructor parameter
            ///
            ///Constructor to resolve
            ///DataReader column name
            ///Mapping implementation
            IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName);


            ///
            /// Gets member mapping for column
            ///
            ///DataReader column name
            ///Mapping implementation
            IMemberMap GetMember(string columnName);
        }


        ///
        /// Implements this interface to provide custom member mapping
        ///
        public interface IMemberMap
        {
            ///
            /// Source DataReader column name
            ///
            string ColumnName { get; }


            ///
            ///  Target member type
            ///
            Type MemberType { get; }


            ///
            /// Target property
            ///
            PropertyInfo Property { get; }


            ///
            /// Target field
            ///
            FieldInfo Field { get; }


            ///
            /// Target constructor parameter
            ///
            ParameterInfo Parameter { get; }
        }


        ///
        /// This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),
        /// and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**
        /// equality. The type is fully thread-safe.
        ///
        internal partial class Link<TKey, TValue> where TKey : class
        {
            public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value)
            {
                while (link != null)
                {
                    if ((object)key == (object)link.Key)
                    {
                        value = link.Value;
                        return true;
                    }
                    link = link.Tail;
                }
                value = default(TValue);
                return false;
            }
            public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue value)
            {
                bool tryAgain;
                do
                {
                    var snapshot = Interlocked.CompareExchange(ref head, null, null);
                    TValue found;
                    if (TryGet(snapshot, key, out found))
                    { // existing match; report the existing value instead
                        value = found;
                        return false;
                    }
                    var newNode = new Link<TKey, TValue>(key, value, snapshot);
                    // did somebody move our cheese?
                    tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot;
                } while (tryAgain);
                return true;
            }
            private Link(TKey key, TValue value, Link<TKey, TValue> tail)
            {
                Key = key;
                Value = value;
                Tail = tail;
            }
            public TKey Key { get; private set; }
            public TValue Value { get; private set; }
            public Link<TKey, TValue> Tail { get; private set; }
        }
        partial class CacheInfo
        {
            public DeserializerState Deserializer { get; set; }
            public Func<IDataReader, object>[] OtherDeserializers { get; set; }
            public Action<IDbCommand, object> ParamReader { get; set; }
            private int hitCount;
            public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
            public void RecordHit() { Interlocked.Increment(ref hitCount); }
        }
        static int GetColumnHash(IDataReader reader)
        {
            unchecked
            {
                int colCount = reader.FieldCount, hash = colCount;
                for (int i = 0; i < colCount; i++)
                {   // binding code is only interested in names - not types
                    object tmp = reader.GetName(i);
                    hash = (hash * 31) + (tmp == null ? 0 : tmp.GetHashCode());
                }
                return hash;
            }
        }
        struct DeserializerState
        {
            public readonly int Hash;
            public readonly Func<IDataReader, object> Func;


            public DeserializerState(int hash, Func<IDataReader, object> func)
            {
                Hash = hash;
                Func = func;
            }
        }


        ///
        /// Called if the query cache is purged via PurgeQueryCache
        ///
        public static event EventHandler QueryCachePurged;
        private static void OnQueryCachePurged()
        {
            var handler = QueryCachePurged;
            if (handler != null) handler(null, EventArgs.Empty);
        }
#if CSHARP30
        private static readonly Dictionary<Identity, CacheInfo> _queryCache = new Dictionary<Identity, CacheInfo>();
        // note: conflicts between readers and writers are so short-lived that it isn't worth the overhead of
        // ReaderWriterLockSlim etc; a simple lock is faster
        private static void SetQueryCache(Identity key, CacheInfo value)
        {
            lock (_queryCache) { _queryCache[key] = value; }
        }
        private static bool TryGetQueryCache(Identity key, out CacheInfo value)
        {
            lock (_queryCache) { return _queryCache.TryGetValue(key, out value); }
        }
        private static void PurgeQueryCacheByType(Type type)
        {
            lock (_queryCache)
            {
                var toRemove = _queryCache.Keys.Where(id => id.type == type).ToArray();
                foreach (var key in toRemove)
                    _queryCache.Remove(key);
            }
        }
        ///
        /// Purge the query cache 
        ///
        public static void PurgeQueryCache()
        {
            lock (_queryCache)
            {
                _queryCache.Clear();
            }
            OnQueryCachePurged();
        }
#else
        static readonly System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo> _queryCache = new System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo>();
        private static void SetQueryCache(Identity key, CacheInfo value)
        {
            if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)
            {
                CollectCacheGarbage();
            }
            _queryCache[key] = value;
        }


        private static void CollectCacheGarbage()
        {
            try
            {
                foreach (var pair in _queryCache)
                {
                    if (pair.Value.GetHitCount() <= COLLECT_HIT_COUNT_MIN)
                    {
                        CacheInfo cache;
                        _queryCache.TryRemove(pair.Key, out cache);
                    }
                }
            }


            finally
            {
                Interlocked.Exchange(ref collect, 0);
            }
        }


        private const int COLLECT_PER_ITEMS = 1000, COLLECT_HIT_COUNT_MIN = 0;
        private static int collect;
        private static bool TryGetQueryCache(Identity key, out CacheInfo value)
        {
            if (_queryCache.TryGetValue(key, out value))
            {
                value.RecordHit();
                return true;
            }
            value = null;
            return false;
        }


        ///
        /// Purge the query cache 
        ///
        public static void PurgeQueryCache()
        {
            _queryCache.Clear();
            OnQueryCachePurged();
        }


        private static void PurgeQueryCacheByType(Type type)
        {
            foreach (var entry in _queryCache)
            {
                CacheInfo cache;
                if (entry.Key.type == type)
                    _queryCache.TryRemove(entry.Key, out cache);
            }
        }


        ///
        /// Return a count of all the cached queries by dapper
        ///
        ///
        public static int GetCachedSQLCount()
        {
            return _queryCache.Count;
        }


        ///
        /// Return a list of all the queries cached by dapper
        ///
        ///
        ///
        public static IEnumerable<Tuple<string, string, int>> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)
        {
            var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount()));
            if (ignoreHitCountAbove < int.MaxValue) data = data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove);
            return data;
        }


        ///
        /// Deep diagnostics only: find any hash collisions in the cache
        ///
        ///
        public static IEnumerable<Tuple<int, int>> GetHashCollissions()
        {
            var counts = new Dictionary<int, int>();
            foreach (var key in _queryCache.Keys)
            {
                int count;
                if (!counts.TryGetValue(key.hashCode, out count))
                {
                    counts.Add(key.hashCode, 1);
                }
                else
                {
                    counts[key.hashCode] = count + 1;
                }
            }
            return from pair in counts
                   where pair.Value > 1
                   select Tuple.Create(pair.Key, pair.Value);


        }
#endif




        static Dictionary<Type, DbType> typeMap;


        static SqlMapper()
        {
            typeMap = new Dictionary<Type, DbType>();
            typeMap[typeof(byte)] = DbType.Byte;
            typeMap[typeof(sbyte)] = DbType.SByte;
            typeMap[typeof(short)] = DbType.Int16;
            typeMap[typeof(ushort)] = DbType.UInt16;
            typeMap[typeof(int)] = DbType.Int32;
            typeMap[typeof(uint)] = DbType.UInt32;
            typeMap[typeof(long)] = DbType.Int64;
            typeMap[typeof(ulong)] = DbType.UInt64;
            typeMap[typeof(float)] = DbType.Single;
            typeMap[typeof(double)] = DbType.Double;
            typeMap[typeof(decimal)] = DbType.Decimal;
            typeMap[typeof(bool)] = DbType.Boolean;
            typeMap[typeof(string)] = DbType.String;
            typeMap[typeof(char)] = DbType.StringFixedLength;
            typeMap[typeof(Guid)] = DbType.Guid;
            typeMap[typeof(DateTime)] = DbType.DateTime;
            typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
            typeMap[typeof(TimeSpan)] = DbType.Time;
            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(TimeSpan?)] = DbType.Time;
            typeMap[typeof(object)] = DbType.Object;


            AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), false);
        }


        ///
        /// Clear the registered type handlers
        ///
        public static void ResetTypeHandlers()
        {
            typeHandlers = new Dictionary<Type, ITypeHandler>();
            AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), true);
        }
        ///
        /// Configure the specified type to be mapped to a given db-type
        ///
        public static void AddTypeMap(Type type, DbType dbType)
        {
            // use clone, mutate, replace to avoid threading issues
            var snapshot = typeMap;


            DbType oldValue;
            if (snapshot.TryGetValue(type, out oldValue) && oldValue == dbType) return; // nothing to do


            var newCopy = new Dictionary<Type, DbType>(snapshot);
            newCopy[type] = dbType;
            typeMap = newCopy;
        }


        ///
        /// Configure the specified type to be processed by a custom handler
        ///
        public static void AddTypeHandler(Type type, ITypeHandler handler)
        {
            AddTypeHandlerImpl(type, handler, true);
        }


        ///
        /// Configure the specified type to be processed by a custom handler
        ///
        public static void AddTypeHandlerImpl(Type type, ITypeHandler handler, bool clone)
        {
            if (type == null) throw new ArgumentNullException("type");


            Type secondary = null;
            if (type.IsValueType)
            {
                var underlying = Nullable.GetUnderlyingType(type);
                if (underlying == null)
                {
                    secondary = typeof(Nullable<>).MakeGenericType(type); // the Nullable
                    // type is already the T
                }
                else
                {
                    secondary = type; // the Nullable
                    type = underlying; // the T
                }
            }


            var snapshot = typeHandlers;
            ITypeHandler oldValue;
            if (snapshot.TryGetValue(type, out oldValue) && handler == oldValue) return; // nothing to do


            var newCopy = clone ? new Dictionary<Type, ITypeHandler>(snapshot) : snapshot;


#pragma warning disable 618
            typeof(TypeHandlerCache<>).MakeGenericType(type).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
            if (secondary != null)
            {
                typeof(TypeHandlerCache<>).MakeGenericType(secondary).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
            }
#pragma warning restore 618
            if (handler == null)
            {
                newCopy.Remove(type);
                if (secondary != null) newCopy.Remove(secondary);
            }
            else
            {
                newCopy[type] = handler;
                if (secondary != null) newCopy[secondary] = handler;
            }
            typeHandlers = newCopy;
        }


        ///
        /// Configure the specified type to be processed by a custom handler
        ///
        public static void AddTypeHandler(TypeHandler handler)
        {
            AddTypeHandlerImpl(typeof(T), handler, true);
        }


        ///
        /// Not intended for direct usage
        ///
        [Obsolete("Not intended for direct usage", false)]
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public static class TypeHandlerCache
        {
            ///
            /// Not intended for direct usage
            ///
            [Obsolete("Not intended for direct usage", true)]
            public static T Parse(object value)
            {
                return (T)handler.Parse(typeof(T), value);

            }


            ///
            /// Not intended for direct usage
            ///
            [Obsolete("Not intended for direct usage", true)]
            public static void SetValue(IDbDataParameter parameter, object value)
            {
                handler.SetValue(parameter, value);
            }


            internal static void SetHandler(ITypeHandler handler)
            {
#pragma warning disable 618
                TypeHandlerCache.handler = handler;
#pragma warning restore 618
            }


            private static ITypeHandler handler;
        }


        private static Dictionary<Type, ITypeHandler> typeHandlers = new Dictionary<Type, ITypeHandler>();


        internal const string LinqBinary = "System.Data.Linq.Binary";


        ///
        /// Get the DbType that maps to a given value
        ///
        [Obsolete("This method is for internal use only"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public static DbType GetDbType(object value)
        {
            if (value == null || value is DBNull) return DbType.Object;


            ITypeHandler handler;
            return LookupDbType(value.GetType(), "n/a", false, out handler);


        }
        internal static DbType LookupDbType(Type type, string name, bool demand, out ITypeHandler handler)
        {
            DbType dbType;
            handler = null;
            var nullUnderlyingType = Nullable.GetUnderlyingType(type);
            if (nullUnderlyingType != null) type = nullUnderlyingType;
            if (type.IsEnum && !typeMap.ContainsKey(type))
            {
                type = Enum.GetUnderlyingType(type);
            }
            if (typeMap.TryGetValue(type, out dbType))
            {
                return dbType;
            }
            if (type.FullName == LinqBinary)
            {
                return DbType.Binary;
            }
            if (typeof(IEnumerable).IsAssignableFrom(type))
            {
                return DynamicParameters.EnumerableMultiParameter;
            }


            if (typeHandlers.TryGetValue(type, out handler))
            {
                return DbType.Object;
            }
            switch (type.FullName)
            {
                case "Microsoft.SqlServer.Types.SqlGeography":
                    AddTypeHandler(type, handler = new UdtTypeHandler("GEOGRAPHY"));
                    return DbType.Object;
                case "Microsoft.SqlServer.Types.SqlGeometry":
                    AddTypeHandler(type, handler = new UdtTypeHandler("GEOMETRY"));
                    return DbType.Object;
                case "Microsoft.SqlServer.Types.SqlHierarchyId":
                    AddTypeHandler(type, handler = new UdtTypeHandler("HIERARCHYID"));
                    return DbType.Object;
            }
            if (demand)
                throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type.FullName));
            return DbType.Object;


        }


        ///
        /// Identity of a cached query in Dapper, used for extensibility
        ///
        public partial class Identity : IEquatable
        {
            internal Identity ForGrid(Type primaryType, int gridIndex)
            {
                return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);
            }


            internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
            {
                return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
            }
            ///
            /// Create an identity for use with DynamicParameters, internal use only
            ///
            ///
            ///
            public Identity ForDynamicParameters(Type type)
            {
                return new Identity(sql, commandType, connectionString, this.type, type, null, -1);
            }


            internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
                : this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)
            { }
            private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex)
            {
                this.sql = sql;
                this.commandType = commandType;
                this.connectionString = connectionString;
                this.type = type;
                this.parametersType = parametersType;
                this.gridIndex = gridIndex;
                unchecked
                {
                    hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this
                    hashCode = hashCode * 23 + commandType.GetHashCode();
                    hashCode = hashCode * 23 + gridIndex.GetHashCode();
                    hashCode = hashCode * 23 + (sql == null ? 0 : sql.GetHashCode());
                    hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode());
                    if (otherTypes != null)
                    {
                        foreach (var t in otherTypes)
                        {
                            hashCode = hashCode * 23 + (t == null ? 0 : t.GetHashCode());
                        }
                    }
                    hashCode = hashCode * 23 + (connectionString == null ? 0 : SqlMapper.connectionStringComparer.GetHashCode(connectionString));
                    hashCode = hashCode * 23 + (parametersType == null ? 0 : parametersType.GetHashCode());
                }
            }


            ///
            /// 
            ///
            ///
            ///
            public override bool Equals(object obj)
            {
                return Equals(obj as Identity);
            }
            ///
            /// The sql
            ///
            public readonly string sql;
            ///
            /// The command type 
            ///
            public readonly CommandType? commandType;


            ///
            /// 
            ///
            public readonly int hashCode, gridIndex;
            ///
            /// 
            ///
            public readonly Type type;
            ///
            /// 
            ///
            public readonly string connectionString;
            ///
            /// 
            ///
            public readonly Type parametersType;
            ///
            /// 
            ///
            ///
            public override int GetHashCode()
            {
                return hashCode;
            }
            ///
            /// Compare 2 Identity objects
            ///
            ///
            /// 
            public bool Equals(Identity other)
            {
                return
                    other != null &&
                    gridIndex == other.gridIndex &&
                    type == other.type &&
                    sql == other.sql &&
                    commandType == other.commandType &&
                    SqlMapper.connectionStringComparer.Equals(connectionString, other.connectionString) &&
                    parametersType == other.parametersType;
            }
        }


#if CSHARP30
        /// 
        /// 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);
        }
 
 
        /// 
        /// Execute parameterized SQL
        /// 
        /// Number of rows affected
        public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
        {
            return Execute(cnn, sql, param, transaction, null, null);
        }
 
 
        /// 
        /// Execute parameterized SQL
        /// 
        /// Number of rows affected
        public static int Execute(this IDbConnection cnn, string sql, object param, CommandType commandType)
        {
            return Execute(cnn, sql, param, null, null, commandType);
        }
 
 
        /// 
        /// Execute parameterized SQL
        /// 
        /// Number of rows affected
        public static int Execute(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
        {
            return Execute(cnn, sql, param, transaction, null, commandType);
        }
 
 
        /// 
        /// Execute parameterized SQL and return an 
        /// 
        /// An  that can be used to iterate over the results of the SQL query.
        public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param)
        {
            return ExecuteReader(cnn, sql, param, null, null, null);
        }
 
 
        /// 
        /// Execute parameterized SQL and return an 
        /// 
        /// An  that can be used to iterate over the results of the SQL query.
        public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
        {
            return ExecuteReader(cnn, sql, param, transaction, null, null);
        }
 
 
        /// 
        /// Execute parameterized SQL and return an 
        /// 
        /// An  that can be used to iterate over the results of the SQL query.
        public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, CommandType commandType)
        {
            return ExecuteReader(cnn, sql, param, null, null, commandType);
        }
 
 
        /// 
        /// Execute parameterized SQL and return an 
        /// 
        /// An  that can be used to iterate over the results of the SQL query.
        public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
        {
            return ExecuteReader(cnn, sql, param, transaction, null, commandType);
        }
 
 
        /// 
        /// Executes a query, returning the data typed as per T
        /// 
        /// 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);
        }
 
 
        /// 
        /// Executes a query, returning the data typed as per T
        /// 
        /// 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, IDbTransaction transaction)
        {
            return Query(cnn, sql, param, transaction, true, null, null);
        }
 
 
        /// 
        /// Executes a query, returning the data typed as per T
        /// 
        /// 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, CommandType commandType)
        {
            return Query(cnn, sql, param, null, true, null, commandType);
        }
 
 
        /// 
        /// Executes a query, returning the data typed as per T
        /// 
        /// 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, IDbTransaction transaction, CommandType commandType)
        {
            return Query(cnn, sql, param, transaction, true, null, commandType);
        }
 
 
        /// 
        /// Execute a command that returns multiple result sets, and access each in turn
        /// 
        public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction)
        {
            return QueryMultiple(cnn, sql, param, transaction, null, null);
        }
 
 
        /// 
        /// Execute a command that returns multiple result sets, and access each in turn
        /// 
        public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, CommandType commandType)
        {
            return QueryMultiple(cnn, sql, param, null, null, commandType);
        }
 
 
        /// 
        /// Execute a command that returns multiple result sets, and access each in turn
        /// 
        public static GridReader QueryMultiple(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, CommandType commandType)
        {
            return QueryMultiple(cnn, sql, param, transaction, null, commandType);
        }
#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, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
        {
            var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
            return ExecuteImpl(cnn, ref command);
        }
        /// 
        /// Execute parameterized SQL  
        /// 
        /// Number of rows affected
        public static int Execute(this IDbConnection cnn, CommandDefinition command)
        {
            return ExecuteImpl(cnn, ref command);
        }




        /// 
        /// Execute parameterized SQL that selects a single value
        /// 
        /// The first cell selected
        public static object ExecuteScalar(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
        {
            var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
            return ExecuteScalarImpl
SqlMapper.cs

 

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Web;
using log4net; 
using Dapper;

namespace Dapper
{
    public class DapperHelper
    {
        static readonly ILog log = LogManager.GetLogger(typeof(DapperHelper));

        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        public static string connectionString = "";
        
        public static string GetConnectionString(string constr)
        {
            if (constr == "")
            {
                return connectionString;
            }
            else
            {
                return constr;
            }
        } 


        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="sql">查询的sql</param>
        /// <param name="param">替换参数</param>
        /// <returns></returns>
        public static List<T> Query<T>(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.Query<T>(sql, param).ToList();
            }
        }
        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="sql">查询的sql</param>
        /// <param name="param">替换参数</param>
        /// <returns></returns>
        public static IEnumerable<dynamic> Query(string sql, object param = null, string constr = "")
        {

            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.Query(sql, param).ToList();
            }
        }

        public static DataTable QueryDataTable(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                DataTable table = new DataTable("Table");
                var reader = con.ExecuteReader(sql, param);
                table.Load(reader);
                return table;
            }
        } 

        /// <summary>
        /// 增删改
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static int Execute(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.Execute(sql, param);
            }
        }

        /// <summary>
        /// Reader获取数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static IDataReader ExecuteReader(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.ExecuteReader(sql, param);
            }
        }

        /// <summary>
        /// Scalar获取数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static object ExecuteScalar(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.ExecuteScalar(sql, param);
            }
        }

        /// <summary>
        /// Scalar获取数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static T ExecuteScalarForT<T>(string sql, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                return con.ExecuteScalar<T>(sql, param);
            }
        }

        /// <summary>
        /// 带参数的存储过程
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static List<T> ExecutePro<T>(string proc, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                List<T> list = con.Query<T>(proc,
                    param,
                    null,
                    true,
                    null,
                    CommandType.StoredProcedure).ToList();
                return list;
            }
        }

        public static object ExecutePro(string proc, object param = null, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {                           // 执行存储过程
                return con.Execute(proc, param, commandType: CommandType.StoredProcedure);

            }
        }

        /// <summary>
        /// 事务1 - 全SQL
        /// </summary>
        /// <param name="sqlarr">多条SQL</param>
        /// <param name="param">param</param>
        /// <returns></returns>
        public static int ExecuteTransaction(string[] sqlarr, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                con.Open();
                using (var transaction = con.BeginTransaction())
                {
                    try
                    {
                        int result = 0;
                        foreach (var sql in sqlarr)
                        {
                            result += con.Execute(sql, null, transaction);
                        }

                        transaction.Commit();
                        return result;
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        log.Error(ex.Message);
                        foreach (var sql in sqlarr)
                        {
                            log.Error(sql);
                        }
                        return 0;
                    }
                }
            }
        }

        /// <summary>
        /// 事务2 - 声明参数
        ///demo:
        ///dic.Add("Insert into Users values (@UserName, @Email, @Address)",
        ///        new { UserName = "jack", Email = "380234234@qq.com", Address = "上海" });
        /// </summary>
        /// <param name="Key">多条SQL</param>
        /// <param name="Value">param</param>
        /// <returns></returns>
        public static int ExecuteTransaction(Dictionary<string, object> dic, string constr = "")
        {
            using (SqlConnection con = new SqlConnection(GetConnectionString(constr)))
            {
                con.Open();
                using (var transaction = con.BeginTransaction())
                {
                    try
                    {
                        int result = 0;
                        foreach (var sql in dic)
                        {
                            result += con.Execute(sql.Key, sql.Value, transaction);
                        }

                        transaction.Commit();
                        return result;
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        log.Error(ex.Message);
                        foreach (var sql in dic)
                        {
                            log.Error(sql.Key + sql.Value.ToString());
                        }
                        return 0;
                    }
                }
            }
        }



        #region 生成sql

        public static string CreateClassFile(string tablename)
        {


            StringBuilder strcolumn = new StringBuilder(1000);

            string sql = @"select * from syscolumns where id=object_id('" + tablename + @"') ";
            var syscolumnsList = Query(sql).ToList();
            foreach (var column in syscolumnsList)
            {
                strcolumn.Append(@" 

                    /// <summary>
                    /// Desc:
                    /// </summary>
                    public string " + column.name + @" { get; set; }");
            }


            sql = @"select * from syscolumns where id=object_id('" + tablename + @"') and colstat!=1 and xtype!=189";
            string UpdateSql = "UPDATE " + tablename + @" SET ";
            string InsertSql = "";

            var xx = Query(sql).ToList();
            foreach (var x in xx)
            {
                UpdateSql += "" + x.name + "=@" + x.name + ",";
                InsertSql += "@" + x.name + ",";
            }
            InsertSql = InsertSql.Substring(0, InsertSql.Length - 1);
            UpdateSql = UpdateSql.Substring(0, UpdateSql.Length - 1) + " where 1=2 ";

            InsertSql = "insert into dbo." + tablename + @"(" + InsertSql.Replace("@", "") + ")values(" + InsertSql + @") ";


            string strdto = @"

                /// <summary>
                /// 
                /// </summary>
                public partial class " + tablename + @"
                {
                    public " + tablename + @"()
                    {
                    }

                    public string InsertSql=""" + InsertSql + @""";
                    public string UpdateSql= """ + UpdateSql + @""" ;

                    " + strcolumn.ToString() + @"
                }
            ";
            return strdto;
        }


        #endregion

    }
}
DapperHelper

 

posted on 2020-03-23 18:38  苏上话  阅读(161)  评论(0编辑  收藏  举报