zoukankan      html  css  js  c++  java
  • Dapper 封装oracle底层访问数据库

    如下代码,修改成只支持oracle:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Collections.Concurrent;
    using System.Reflection.Emit;
    
    using Dapper;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Dapper.Contrib.Extensions
    {
        /// <summary>
        /// The Dapper.Contrib extensions for Dapper
        /// </summary>
        public static partial class SqlMapperExtensions
        {
            /// <summary>
            /// Defined a proxy object with a possibly dirty state.
            /// </summary>
            public interface IProxy //must be kept public
            {
                /// <summary>
                /// Whether the object has been changed.
                /// </summary>
                bool IsDirty { get; set; }
            }
    
            /// <summary>
            /// Defines a table name mapper for getting table names from types.
            /// </summary>
            public interface ITableNameMapper
            {
                /// <summary>
                /// Gets a table name from a given <see cref="Type"/>.
                /// </summary>
                /// <param name="type">The <see cref="Type"/> to get a name from.</param>
                /// <returns>The table name for the given <paramref name="type"/>.</returns>
                string GetTableName(Type type);
            }
    
            /// <summary>
            /// The function to get a database type from the given <see cref="IDbConnection"/>.
            /// </summary>
            /// <param name="connection">The connection to get a database type name from.</param>
            public delegate string GetDatabaseTypeDelegate(IDbConnection connection);
            /// <summary>
            /// The function to get a a table name from a given <see cref="Type"/>
            /// </summary>
            /// <param name="type">The <see cref="Type"/> to get a table name for.</param>
            public delegate string TableNameMapperDelegate(Type type);
    
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ExplicitKeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> GetQueries = new ConcurrentDictionary<RuntimeTypeHandle, string>();
            private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeTableName = new ConcurrentDictionary<RuntimeTypeHandle, string>();
    
            private static readonly ISqlAdapter DefaultAdapter = new SqlServerAdapter();
            private static readonly Dictionary<string, ISqlAdapter> AdapterDictionary
                = new Dictionary<string, ISqlAdapter>
                {
                    ["sqlconnection"] = new SqlServerAdapter(),
                    ["oracleconnection"] = new OracleAdapter(),
                    ["sqlceconnection"] = new SqlCeServerAdapter(),                
                    ["mysqlconnection"] = new MySqlAdapter()              
                };
    
            private static List<PropertyInfo> ComputedPropertiesCache(Type type)
            {
                if (ComputedProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
                {
                    return pi.ToList();
                }
    
                var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ComputedAttribute)).ToList();
    
                ComputedProperties[type.TypeHandle] = computedProperties;
                return computedProperties;
            }
    
            private static List<PropertyInfo> ExplicitKeyPropertiesCache(Type type)
            {
                if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
                {
                    return pi.ToList();
                }
    
                var explicitKeyProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)).ToList();
    
                ExplicitKeyProperties[type.TypeHandle] = explicitKeyProperties;
                return explicitKeyProperties;
            }
    
            private static List<PropertyInfo> KeyPropertiesCache(Type type)
            {
                if (KeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
                {
                    return pi.ToList();
                }
    
                var allProperties = TypePropertiesCache(type);
                var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();
    
                if (keyProperties.Count == 0)
                {
                    var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase));
                    if (idProp != null && !idProp.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute))
                    {
                        keyProperties.Add(idProp);
                    }
                }
    
                KeyProperties[type.TypeHandle] = keyProperties;
                return keyProperties;
            }
    
            private static List<PropertyInfo> TypePropertiesCache(Type type)
            {
                if (TypeProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pis))
                {
                    return pis.ToList();
                }
    
                var properties = type.GetProperties().Where(IsWriteable).ToArray();
                TypeProperties[type.TypeHandle] = properties;
                return properties.ToList();
            }
    
            private static bool IsWriteable(PropertyInfo pi)
            {
                var attributes = pi.GetCustomAttributes(typeof(WriteAttribute), false).AsList();
                if (attributes.Count != 1) return true;
    
                var writeAttribute = (WriteAttribute)attributes[0];
                return writeAttribute.Write;
            }
    
            private static PropertyInfo GetSingleKey<T>(string method)
            {
                var type = typeof(T);
                var keys = KeyPropertiesCache(type);
                var explicitKeys = ExplicitKeyPropertiesCache(type);
                var keyCount = keys.Count + explicitKeys.Count;
                if (keyCount > 1)
                    throw new DataException($"{method}<T> only supports an entity with a single [Key] or [ExplicitKey] property");
                if (keyCount == 0)
                    throw new DataException($"{method}<T> only supports an entity with a [Key] or an [ExplicitKey] property");
    
                return keys.Count > 0 ? keys[0] : explicitKeys[0];
            }
    
            /// <summary>
            /// Returns a single entity by a single id from table "Ts".  
            /// Id must be marked with [Key] attribute.
            /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension
            /// for optimal performance. 
            /// </summary>
            /// <typeparam name="T">Interface or type to create and populate</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="id">Id of the entity to get, must be marked with [Key] attribute</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>Entity of T</returns>
            public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
    
                if (!GetQueries.TryGetValue(type.TypeHandle, out string sql))
                {
                    var key = GetSingleKey<T>(nameof(Get));
                    var name = GetTableName(type);
    
                    sql = $"SELECT * FROM {name} WHERE {key.Name} = :id";
                    GetQueries[type.TypeHandle] = sql;
                }
    
                var dynParms = new DynamicParameters();
                dynParms.Add(":id", id);
    
                T obj;
    
                if (type.IsInterface())
                {
                    var res = connection.Query(sql, dynParms).FirstOrDefault() as IDictionary<string, object>;
    
                    if (res == null)
                        return null;
    
                    obj = ProxyGenerator.GetInterfaceProxy<T>();
    
                    foreach (var property in TypePropertiesCache(type))
                    {
                        var val = res[property.Name];
                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);
                    }
    
                    ((IProxy)obj).IsDirty = false;   //reset change tracking and return
                }
                else
                {
                    obj = connection.Query<T>(sql, dynParms, transaction, commandTimeout: commandTimeout).FirstOrDefault();
                }
                return obj;
            }
    
            /// <summary>
            /// Returns a list of entites from table "Ts".  
            /// Id of T must be marked with [Key] attribute.
            /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension
            /// for optimal performance. 
            /// </summary>
            /// <typeparam name="T">Interface or type to create and populate</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>Entity of T</returns>
            public static IEnumerable<T> GetAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
                var cacheType = typeof(List<T>);
    
                if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql))
                {
                    GetSingleKey<T>(nameof(GetAll));
                    var name = GetTableName(type);
    
                    sql = "SELECT * FROM " + name;
                    GetQueries[cacheType.TypeHandle] = sql;
                }
    
                if (!type.IsInterface()) return connection.Query<T>(sql, null, transaction, commandTimeout: commandTimeout);
    
                var result = connection.Query(sql);
                var list = new List<T>();
                foreach (IDictionary<string, object> res in result)
                {
                    var obj = ProxyGenerator.GetInterfaceProxy<T>();
                    foreach (var property in TypePropertiesCache(type))
                    {
                        var val = res[property.Name];
                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);
                    }
                    ((IProxy)obj).IsDirty = false;   //reset change tracking and return
                    list.Add(obj);
                }
                return list;
            }
    
            /// <summary>
            /// Specify a custom table name mapper based on the POCO type name
            /// </summary>
            public static TableNameMapperDelegate TableNameMapper;
    
            private static string GetTableName(Type type)
            {
                if (TypeTableName.TryGetValue(type.TypeHandle, out string name)) return name;
    
                if (TableNameMapper != null)
                {
                    name = TableNameMapper(type);
                }
                else
                {
                    //NOTE: This as dynamic trick should be able to handle both our own Table-attribute as well as the one in EntityFramework 
                    var tableAttr = type
    #if NETSTANDARD1_3
                        .GetTypeInfo()
    #endif
                        .GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic;
                    if (tableAttr != null)
                    {
                        name = tableAttr.Name;
                    }
                    else
                    {
                        name = type.Name;// + "s";
                        if (type.IsInterface() && name.StartsWith("I"))
                            name = name.Substring(1);
                    }
                }
    
                TypeTableName[type.TypeHandle] = name;
                return name;
            }
    
            /// <summary>
            /// Inserts an entity into table "Ts" and returns identity id or number if inserted rows if inserting a list.
            /// </summary>
            /// <typeparam name="T">The type to insert.</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToInsert">Entity to insert, can be list of entities</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>Identity of inserted entity, or number of inserted rows if inserting a list</returns>
            public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var isList = false;
    
                var type = typeof(T);
    
                if (type.IsArray)
                {
                    isList = true;
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    isList = true;
                    type = type.GetGenericArguments()[0];
                }
    
                var name = GetTableName(type);
                var sbColumnList = new StringBuilder(null);
                var allProperties = TypePropertiesCache(type);
                var keyProperties = KeyPropertiesCache(type);
                var computedProperties = ComputedPropertiesCache(type);
                var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();
    
                var adapter = GetFormatter(connection);
    
                for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
                {
                    var property = allPropertiesExceptKeyAndComputed[i];
                    adapter.AppendColumnName(sbColumnList, property.Name);  //fix for issue #336
                    if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                        sbColumnList.Append(", ");
                }
    
                var sbParameterList = new StringBuilder(null);
                for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
                {
                    var property = allPropertiesExceptKeyAndComputed[i];
                    sbParameterList.AppendFormat(":{0}", property.Name);
                    if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                        sbParameterList.Append(", ");
                }
    
                int returnVal;
                var wasClosed = connection.State == ConnectionState.Closed;
                if (wasClosed) connection.Open();
    
                if (!isList)    //single entity
                {
                    returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
                        sbParameterList.ToString(), keyProperties, entityToInsert);
                }
                else
                {
                    //insert list of entities
                    var cmd = $"INSERT INTO {name} ({sbColumnList}) VALUES ({sbParameterList})";
                    returnVal = connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
                }
                if (wasClosed) connection.Close();
                return returnVal;
            }
    
            /// <summary>
            /// Updates entity in table "Ts", checks if the entity is modified if the entity is tracked by the Get() extension.
            /// </summary>
            /// <typeparam name="T">Type to be updated</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToUpdate">Entity to be updated</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
            public static bool Update<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                if (entityToUpdate is IProxy proxy && !proxy.IsDirty)
                {
                    return false;
                }
    
                var type = typeof(T);
    
                if (type.IsArray)
                {
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    type = type.GetGenericArguments()[0];
                }
    
                var keyProperties = KeyPropertiesCache(type).ToList();  //added ToList() due to issue #418, must work on a list copy
                var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
                if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
                    throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");
    
                var name = GetTableName(type);
    
                var sb = new StringBuilder();
                sb.AppendFormat("UPDATE {0} SET ", name);
    
                var allProperties = TypePropertiesCache(type);
                keyProperties.AddRange(explicitKeyProperties);
                var computedProperties = ComputedPropertiesCache(type);
                var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList();
    
                var adapter = GetFormatter(connection);
    
                for (var i = 0; i < nonIdProps.Count; i++)
                {
                    var property = nonIdProps[i];
                    adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
                    if (i < nonIdProps.Count - 1)
                        sb.AppendFormat(", ");
                }
                sb.Append(" WHERE ");
                for (var i = 0; i < keyProperties.Count; i++)
                {
                    var property = keyProperties[i];
                    adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
                    if (i < keyProperties.Count - 1)
                        sb.AppendFormat(" AND ");
                }
                var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
                return updated > 0;
            }
    
            /// <summary>
            /// Delete entity in table "Ts".
            /// </summary>
            /// <typeparam name="T">Type of entity</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToDelete">Entity to delete</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if deleted, false if not found</returns>
            public static bool Delete<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                if (entityToDelete == null)
                    throw new ArgumentException("Cannot Delete null Object", nameof(entityToDelete));
    
                var type = typeof(T);
    
                if (type.IsArray)
                {
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    type = type.GetGenericArguments()[0];
                }
    
                var keyProperties = KeyPropertiesCache(type).ToList();  //added ToList() due to issue #418, must work on a list copy
                var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
                if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
                    throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");
    
                var name = GetTableName(type);
                keyProperties.AddRange(explicitKeyProperties);
    
                var sb = new StringBuilder();
                sb.AppendFormat("DELETE FROM {0} WHERE ", name);
    
                var adapter = GetFormatter(connection);
    
                for (var i = 0; i < keyProperties.Count; i++)
                {
                    var property = keyProperties[i];
                    adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
                    if (i < keyProperties.Count - 1)
                        sb.AppendFormat(" AND ");
                }
                var deleted = connection.Execute(sb.ToString(), entityToDelete, transaction, commandTimeout);
                return deleted > 0;
            }
    
            /// <summary>
            /// Delete all entities in the table related to the type T.
            /// </summary>
            /// <typeparam name="T">Type of entity</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if deleted, false if none found</returns>
            public static bool DeleteAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
                var name = GetTableName(type);
                var statement = $"DELETE FROM {name}";
                var deleted = connection.Execute(statement, null, transaction, commandTimeout);
                return deleted > 0;
            }
    
            /// <summary>
            /// Specifies a custom callback that detects the database type instead of relying on the default strategy (the name of the connection type object).
            /// Please note that this callback is global and will be used by all the calls that require a database specific adapter.
            /// </summary>
            public static GetDatabaseTypeDelegate GetDatabaseType;
    
            private static ISqlAdapter GetFormatter(IDbConnection connection)
            {
                var name = GetDatabaseType?.Invoke(connection).ToLower()
                           ?? connection.GetType().Name.ToLower();
    
                return !AdapterDictionary.ContainsKey(name)
                    ? DefaultAdapter
                    : AdapterDictionary[name];
            }
    
            private static class ProxyGenerator
            {
                private static readonly Dictionary<Type, Type> TypeCache = new Dictionary<Type, Type>();
    
                private static AssemblyBuilder GetAsmBuilder(string name)
                {
    #if NETSTANDARD1_3 || NETSTANDARD2_0
                    return AssemblyBuilder.DefineDynamicAssembly(new AssemblyName { Name = name }, AssemblyBuilderAccess.Run);
    #else
                    return Thread.GetDomain().DefineDynamicAssembly(new AssemblyName { Name = name }, AssemblyBuilderAccess.Run);
    #endif
                }
    
                public static T GetInterfaceProxy<T>()
                {
                    Type typeOfT = typeof(T);
    
                    if (TypeCache.TryGetValue(typeOfT, out Type k))
                    {
                        return (T)Activator.CreateInstance(k);
                    }
                    var assemblyBuilder = GetAsmBuilder(typeOfT.Name);
    
                    var moduleBuilder = assemblyBuilder.DefineDynamicModule("SqlMapperExtensions." + typeOfT.Name); //NOTE: to save, add "asdasd.dll" parameter
    
                    var interfaceType = typeof(IProxy);
                    var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + "_" + Guid.NewGuid(),
                        TypeAttributes.Public | TypeAttributes.Class);
                    typeBuilder.AddInterfaceImplementation(typeOfT);
                    typeBuilder.AddInterfaceImplementation(interfaceType);
    
                    //create our _isDirty field, which implements IProxy
                    var setIsDirtyMethod = CreateIsDirtyProperty(typeBuilder);
    
                    // Generate a field for each property, which implements the T
                    foreach (var property in typeof(T).GetProperties())
                    {
                        var isId = property.GetCustomAttributes(true).Any(a => a is KeyAttribute);
                        CreateProperty<T>(typeBuilder, property.Name, property.PropertyType, setIsDirtyMethod, isId);
                    }
    
    #if NETSTANDARD1_3 || NETSTANDARD2_0
                    var generatedType = typeBuilder.CreateTypeInfo().AsType();
    #else
                    var generatedType = typeBuilder.CreateType();
    #endif
    
                    TypeCache.Add(typeOfT, generatedType);
                    return (T)Activator.CreateInstance(generatedType);
                }
    
                private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder)
                {
                    var propType = typeof(bool);
                    var field = typeBuilder.DefineField("_" + nameof(IProxy.IsDirty), propType, FieldAttributes.Private);
                    var property = typeBuilder.DefineProperty(nameof(IProxy.IsDirty),
                                                   System.Reflection.PropertyAttributes.None,
                                                   propType,
                                                   new[] { propType });
    
                    const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.SpecialName
                                                      | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig;
    
                    // Define the "get" and "set" accessor methods
                    var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + nameof(IProxy.IsDirty),
                                                 getSetAttr,
                                                 propType,
                                                 Type.EmptyTypes);
                    var currGetIl = currGetPropMthdBldr.GetILGenerator();
                    currGetIl.Emit(OpCodes.Ldarg_0);
                    currGetIl.Emit(OpCodes.Ldfld, field);
                    currGetIl.Emit(OpCodes.Ret);
                    var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + nameof(IProxy.IsDirty),
                                                 getSetAttr,
                                                 null,
                                                 new[] { propType });
                    var currSetIl = currSetPropMthdBldr.GetILGenerator();
                    currSetIl.Emit(OpCodes.Ldarg_0);
                    currSetIl.Emit(OpCodes.Ldarg_1);
                    currSetIl.Emit(OpCodes.Stfld, field);
                    currSetIl.Emit(OpCodes.Ret);
    
                    property.SetGetMethod(currGetPropMthdBldr);
                    property.SetSetMethod(currSetPropMthdBldr);
                    var getMethod = typeof(IProxy).GetMethod("get_" + nameof(IProxy.IsDirty));
                    var setMethod = typeof(IProxy).GetMethod("set_" + nameof(IProxy.IsDirty));
                    typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
                    typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
    
                    return currSetPropMthdBldr;
                }
    
                private static void CreateProperty<T>(TypeBuilder typeBuilder, string propertyName, Type propType, MethodInfo setIsDirtyMethod, bool isIdentity)
                {
                    //Define the field and the property 
                    var field = typeBuilder.DefineField("_" + propertyName, propType, FieldAttributes.Private);
                    var property = typeBuilder.DefineProperty(propertyName,
                                                   System.Reflection.PropertyAttributes.None,
                                                   propType,
                                                   new[] { propType });
    
                    const MethodAttributes getSetAttr = MethodAttributes.Public
                                                        | MethodAttributes.Virtual
                                                        | MethodAttributes.HideBySig;
    
                    // Define the "get" and "set" accessor methods
                    var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName,
                                                 getSetAttr,
                                                 propType,
                                                 Type.EmptyTypes);
    
                    var currGetIl = currGetPropMthdBldr.GetILGenerator();
                    currGetIl.Emit(OpCodes.Ldarg_0);
                    currGetIl.Emit(OpCodes.Ldfld, field);
                    currGetIl.Emit(OpCodes.Ret);
    
                    var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
                                                 getSetAttr,
                                                 null,
                                                 new[] { propType });
    
                    //store value in private field and set the isdirty flag
                    var currSetIl = currSetPropMthdBldr.GetILGenerator();
                    currSetIl.Emit(OpCodes.Ldarg_0);
                    currSetIl.Emit(OpCodes.Ldarg_1);
                    currSetIl.Emit(OpCodes.Stfld, field);
                    currSetIl.Emit(OpCodes.Ldarg_0);
                    currSetIl.Emit(OpCodes.Ldc_I4_1);
                    currSetIl.Emit(OpCodes.Call, setIsDirtyMethod);
                    currSetIl.Emit(OpCodes.Ret);
    
                    //TODO: Should copy all attributes defined by the interface?
                    if (isIdentity)
                    {
                        var keyAttribute = typeof(KeyAttribute);
                        var myConstructorInfo = keyAttribute.GetConstructor(new Type[] { });
                        var attributeBuilder = new CustomAttributeBuilder(myConstructorInfo, new object[] { });
                        property.SetCustomAttribute(attributeBuilder);
                    }
    
                    property.SetGetMethod(currGetPropMthdBldr);
                    property.SetSetMethod(currSetPropMthdBldr);
                    var getMethod = typeof(T).GetMethod("get_" + propertyName);
                    var setMethod = typeof(T).GetMethod("set_" + propertyName);
                    typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
                    typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
                }
            }
        }
    
        /// <summary>
        /// Defines the name of a table to use in Dapper.Contrib commands.
        /// </summary>
        [AttributeUsage(AttributeTargets.Class)]
        public class TableAttribute : Attribute
        {
            /// <summary>
            /// Creates a table mapping to a specific name for Dapper.Contrib commands
            /// </summary>
            /// <param name="tableName">The name of this table in the database.</param>
            public TableAttribute(string tableName)
            {
                Name = tableName;
            }
    
            /// <summary>
            /// The name of the table in the database
            /// </summary>
            public string Name { get; set; }
        }
    
        /// <summary>
        /// Specifies that this field is a primary key in the database
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class KeyAttribute : Attribute
        {
        }
    
        /// <summary>
        /// Specifies that this field is a explicitly set primary key in the database
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class ExplicitKeyAttribute : Attribute
        {
        }
    
        /// <summary>
        /// Specifies whether a field is writable in the database.
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class WriteAttribute : Attribute
        {
            /// <summary>
            /// Specifies whether a field is writable in the database.
            /// </summary>
            /// <param name="write">Whether a field is writable in the database.</param>
            public WriteAttribute(bool write)
            {
                Write = write;
            }
    
            /// <summary>
            /// Whether a field is writable in the database.
            /// </summary>
            public bool Write { get; }
        }
    
        /// <summary>
        /// Specifies that this is a computed column.
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class ComputedAttribute : Attribute
        {
        }
        internal static class TypeExtensions
        {
            public static string Name(this Type type) =>
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                type.GetTypeInfo().Name;
    #else
                type.Name;
    #endif
    
            public static bool IsValueType(this Type type) =>
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                type.GetTypeInfo().IsValueType;
    #else
                type.IsValueType;
    #endif
    
            public static bool IsEnum(this Type type) =>
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                type.GetTypeInfo().IsEnum;
    #else
                type.IsEnum;
    #endif
    
            public static bool IsGenericType(this Type type) =>
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                type.GetTypeInfo().IsGenericType;
    #else
                type.IsGenericType;
    #endif
    
            public static bool IsInterface(this Type type) =>
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                type.GetTypeInfo().IsInterface;
    #else
                type.IsInterface;
    #endif
    
    #if NETSTANDARD1_3 || NETCOREAPP1_0
            public static IEnumerable<Attribute> GetCustomAttributes(this Type type, bool inherit)
            {
                return type.GetTypeInfo().GetCustomAttributes(inherit);
            }
    
            public static TypeCode GetTypeCode(Type type)
            {
                if (type == null) return TypeCode.Empty;
                if (typeCodeLookup.TryGetValue(type, out TypeCode result)) return result;
    
                if (type.IsEnum())
                {
                    type = Enum.GetUnderlyingType(type);
                    if (typeCodeLookup.TryGetValue(type, out result)) return result;
                }
                return TypeCode.Object;
            }
    
            private static readonly Dictionary<Type, TypeCode> typeCodeLookup = new Dictionary<Type, TypeCode>
            {
                [typeof(bool)] = TypeCode.Boolean,
                [typeof(byte)] = TypeCode.Byte,
                [typeof(char)] = TypeCode.Char,
                [typeof(DateTime)] = TypeCode.DateTime,
                [typeof(decimal)] = TypeCode.Decimal,
                [typeof(double)] = TypeCode.Double,
                [typeof(short)] = TypeCode.Int16,
                [typeof(int)] = TypeCode.Int32,
                [typeof(long)] = TypeCode.Int64,
                [typeof(object)] = TypeCode.Object,
                [typeof(sbyte)] = TypeCode.SByte,
                [typeof(float)] = TypeCode.Single,
                [typeof(string)] = TypeCode.String,
                [typeof(ushort)] = TypeCode.UInt16,
                [typeof(uint)] = TypeCode.UInt32,
                [typeof(ulong)] = TypeCode.UInt64,
            };
    #else
            public static TypeCode GetTypeCode(Type type) => Type.GetTypeCode(type);
    #endif
    
            public static MethodInfo GetPublicInstanceMethod(this Type type, string name, Type[] types)
            {
    #if NETSTANDARD1_3 || NETCOREAPP1_0
                var method = type.GetMethod(name, types);
                return (method?.IsPublic == true && !method.IsStatic) ? method : null;
    #else
                return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public, null, types, null);
    #endif
            }
        }
    }
    
    /// <summary>
    /// The interface for all Dapper.Contrib database operations
    /// Implementing this is each provider's model.
    /// </summary>
    public partial interface ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        void AppendColumnName(StringBuilder sb, string columnName);
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        void AppendColumnNameEqualsValue(StringBuilder sb, string columnName);
    }
    
    /// <summary>
    /// The Oracle database adapter.
    /// </summary>
    public partial class OracleAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var keyFirst = keyProperties.First();
            var keyName = keyFirst.Name;
            var cmd = $"BEGIN INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}) RETURNING {keyName} INTO :ID; END;";//$"BEGIN INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}) RETURNING ID INTO :ID; END;";
    
            var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);
            var idKey = multi.Command.Parameters["ID"] as IDataParameter;
            if(keyFirst.PropertyType == typeof(int) || keyFirst.PropertyType == typeof(long))
            {
                return (int)idKey.Value;
            }
            return 1;   
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("{0}", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("{0} = :{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The SQL Server database adapter.
    /// </summary>
    public partial class SqlServerAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"insert into {tableName} ({columnList}) values ({parameterList});select SCOPE_IDENTITY() id";
            var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);
    
            var first = multi.Read().FirstOrDefault();
            if (first == null || first.id == null) return 0;
    
            var id = (int)first.id;
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (propertyInfos.Length == 0) return id;
    
            var idProperty = propertyInfos[0];
            idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);
    
            return id;
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("[{0}]", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("[{0}] = @{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The SQL Server Compact Edition database adapter.
    /// </summary>
    public partial class SqlCeServerAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"insert into {tableName} ({columnList}) values ({parameterList})";
            connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
            var r = connection.Query("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout).ToList();
    
            if (r[0].id == null) return 0;
            var id = (int)r[0].id;
    
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (propertyInfos.Length == 0) return id;
    
            var idProperty = propertyInfos[0];
            idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);
    
            return id;
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("[{0}]", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("[{0}] = @{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The MySQL database adapter.
    /// </summary>
    public partial class MySqlAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"insert into {tableName} ({columnList}) values ({parameterList})";
            connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
            var r = connection.Query("Select LAST_INSERT_ID() id", transaction: transaction, commandTimeout: commandTimeout);
    
            var id = r.First().id;
            if (id == null) return 0;
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (propertyInfos.Length == 0) return Convert.ToInt32(id);
    
            var idp = propertyInfos[0];
            idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);
    
            return Convert.ToInt32(id);
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("`{0}`", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("`{0}` = @{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The Postgres database adapter.
    /// </summary>
    public partial class PostgresAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var sb = new StringBuilder();
            sb.AppendFormat("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
    
            // If no primary key then safe to assume a join table with not too much data to return
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (propertyInfos.Length == 0)
            {
                sb.Append(" RETURNING *");
            }
            else
            {
                sb.Append(" RETURNING ");
                var first = true;
                foreach (var property in propertyInfos)
                {
                    if (!first)
                        sb.Append(", ");
                    first = false;
                    sb.Append(property.Name);
                }
            }
    
            var results = connection.Query(sb.ToString(), entityToInsert, transaction, commandTimeout: commandTimeout).ToList();
    
            // Return the key by assinging the corresponding property in the object - by product is that it supports compound primary keys
            var id = 0;
            foreach (var p in propertyInfos)
            {
                var value = ((IDictionary<string, object>)results[0])[p.Name.ToLower()];
                p.SetValue(entityToInsert, value, null);
                if (id == 0)
                    id = Convert.ToInt32(value);
            }
            return id;
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat(""{0}"", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat(""{0}" = @{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The SQLite database adapter.
    /// </summary>
    public partial class SQLiteAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}); SELECT last_insert_rowid() id";
            var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);
    
            var id = (int)multi.Read().First().id;
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (propertyInfos.Length == 0) return id;
    
            var idProperty = propertyInfos[0];
            idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);
    
            return id;
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat(""{0}"", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat(""{0}" = @{1}", columnName, columnName);
        }
    }
    
    /// <summary>
    /// The Firebase SQL adapeter.
    /// </summary>
    public partial class FbAdapter : ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"insert into {tableName} ({columnList}) values ({parameterList})";
            connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
    
            var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            var keyName = propertyInfos[0].Name;
            var r = connection.Query($"SELECT FIRST 1 {keyName} ID FROM {tableName} ORDER BY {keyName} DESC", transaction: transaction, commandTimeout: commandTimeout);
    
            var id = r.First().ID;
            if (id == null) return 0;
            if (propertyInfos.Length == 0) return Convert.ToInt32(id);
    
            var idp = propertyInfos[0];
            idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);
    
            return Convert.ToInt32(id);
        }
    
        /// <summary>
        /// Adds the name of a column.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnName(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("{0}", columnName);
        }
    
        /// <summary>
        /// Adds a column equality to a parameter.
        /// </summary>
        /// <param name="sb">The string builder  to append to.</param>
        /// <param name="columnName">The column name.</param>
        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)
        {
            sb.AppendFormat("{0} = @{1}", columnName, columnName);
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using Dapper;
    
    namespace Dapper.Contrib.Extensions
    {
        public static partial class SqlMapperExtensions
        {
            /// <summary>
            /// Returns a single entity by a single id from table "Ts" asynchronously using .NET 4.5 Task. T must be of interface type. 
            /// Id must be marked with [Key] attribute.
            /// Created entity is tracked/intercepted for changes and used by the Update() extension. 
            /// </summary>
            /// <typeparam name="T">Interface type to create and populate</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="id">Id of the entity to get, must be marked with [Key] attribute</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>Entity of T</returns>
            public static async Task<T> GetAsync<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
                if (!GetQueries.TryGetValue(type.TypeHandle, out string sql))
                {
                    var key = GetSingleKey<T>(nameof(GetAsync));
                    var name = GetTableName(type);
    
                    sql = $"SELECT * FROM {name} WHERE {key.Name} = :id";
                    GetQueries[type.TypeHandle] = sql;
                }
    
                var dynParms = new DynamicParameters();
                dynParms.Add(":id", id);
    
                if (!type.IsInterface())
                    return (await connection.QueryAsync<T>(sql, dynParms, transaction, commandTimeout).ConfigureAwait(false)).FirstOrDefault();
    
                var res = (await connection.QueryAsync<dynamic>(sql, dynParms).ConfigureAwait(false)).FirstOrDefault() as IDictionary<string, object>;
    
                if (res == null)
                    return null;
    
                var obj = ProxyGenerator.GetInterfaceProxy<T>();
    
                foreach (var property in TypePropertiesCache(type))
                {
                    var val = res[property.Name];
                    property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);
                }
    
                ((IProxy)obj).IsDirty = false;   //reset change tracking and return
    
                return obj;
            }
    
            /// <summary>
            /// Returns a list of entites from table "Ts".  
            /// Id of T must be marked with [Key] attribute.
            /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension
            /// for optimal performance. 
            /// </summary>
            /// <typeparam name="T">Interface or type to create and populate</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>Entity of T</returns>
            public static Task<IEnumerable<T>> GetAllAsync<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
                var cacheType = typeof(List<T>);
    
                if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql))
                {
                    GetSingleKey<T>(nameof(GetAll));
                    var name = GetTableName(type);
    
                    sql = "SELECT * FROM " + name;
                    GetQueries[cacheType.TypeHandle] = sql;
                }
    
                if (!type.IsInterface())
                {
                    return connection.QueryAsync<T>(sql, null, transaction, commandTimeout);
                }
                return GetAllAsyncImpl<T>(connection, transaction, commandTimeout, sql, type);
            }
    
            private static async Task<IEnumerable<T>> GetAllAsyncImpl<T>(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string sql, Type type) where T : class
            {
                var result = await connection.QueryAsync(sql).ConfigureAwait(false);
                var list = new List<T>();
                foreach (IDictionary<string, object> res in result)
                {
                    var obj = ProxyGenerator.GetInterfaceProxy<T>();
                    foreach (var property in TypePropertiesCache(type))
                    {
                        var val = res[property.Name];
                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);
                    }
                    ((IProxy)obj).IsDirty = false;   //reset change tracking and return
                    list.Add(obj);
                }
                return list;
            }
    
            /// <summary>
            /// Inserts an entity into table "Ts" asynchronously using .NET 4.5 Task and returns identity id.
            /// </summary>
            /// <typeparam name="T">The type being inserted.</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToInsert">Entity to insert</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <param name="sqlAdapter">The specific ISqlAdapter to use, auto-detected based on connection if null</param>
            /// <returns>Identity of inserted entity</returns>
            public static Task<int> InsertAsync<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null,
                int? commandTimeout = null, ISqlAdapter sqlAdapter = null) where T : class
            {
                var type = typeof(T);
                sqlAdapter = sqlAdapter ?? GetFormatter(connection);
    
                var isList = false;
                if (type.IsArray)
                {
                    isList = true;
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    isList = true;
                    type = type.GetGenericArguments()[0];
                }
    
                var name = GetTableName(type);
                var sbColumnList = new StringBuilder(null);
                var allProperties = TypePropertiesCache(type);
                var keyProperties = KeyPropertiesCache(type);
                var computedProperties = ComputedPropertiesCache(type);
                var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();
    
                for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
                {
                    var property = allPropertiesExceptKeyAndComputed[i];
                    sqlAdapter.AppendColumnName(sbColumnList, property.Name);
                    if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                        sbColumnList.Append(", ");
                }
    
                var sbParameterList = new StringBuilder(null);
                for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
                {
                    var property = allPropertiesExceptKeyAndComputed[i];
                    sbParameterList.AppendFormat(":{0}", property.Name);
                    if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                        sbParameterList.Append(", ");
                }
    
                if (!isList)    //single entity
                {
                    return sqlAdapter.InsertAsync(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
                        sbParameterList.ToString(), keyProperties, entityToInsert);
                }
    
                //insert list of entities
                var cmd = $"INSERT INTO {name} ({sbColumnList}) values ({sbParameterList})";
                return connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout);
            }
    
            /// <summary>
            /// Updates entity in table "Ts" asynchronously using .NET 4.5 Task, checks if the entity is modified if the entity is tracked by the Get() extension.
            /// </summary>
            /// <typeparam name="T">Type to be updated</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToUpdate">Entity to be updated</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
            public static async Task<bool> UpdateAsync<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                if ((entityToUpdate is IProxy proxy) && !proxy.IsDirty)
                {
                    return false;
                }
    
                var type = typeof(T);
    
                if (type.IsArray)
                {
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    type = type.GetGenericArguments()[0];
                }
    
                var keyProperties = KeyPropertiesCache(type);
                var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
                if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
                    throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");
    
                var name = GetTableName(type);
    
                var sb = new StringBuilder();
                sb.AppendFormat("UPDATE {0} SET ", name);
    
                var allProperties = TypePropertiesCache(type);
                keyProperties.AddRange(explicitKeyProperties);
                var computedProperties = ComputedPropertiesCache(type);
                var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList();
    
                var adapter = GetFormatter(connection);
    
                for (var i = 0; i < nonIdProps.Count; i++)
                {
                    var property = nonIdProps[i];
                    adapter.AppendColumnNameEqualsValue(sb, property.Name);
                    if (i < nonIdProps.Count - 1)
                        sb.AppendFormat(", ");
                }
                sb.Append(" WHERE ");
                for (var i = 0; i < keyProperties.Count; i++)
                {
                    var property = keyProperties[i];
                    adapter.AppendColumnNameEqualsValue(sb, property.Name);
                    if (i < keyProperties.Count - 1)
                        sb.AppendFormat(" AND ");
                }
                var updated = await connection.ExecuteAsync(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction).ConfigureAwait(false);
                return updated > 0;
            }
    
            /// <summary>
            /// Delete entity in table "Ts" asynchronously using .NET 4.5 Task.
            /// </summary>
            /// <typeparam name="T">Type of entity</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="entityToDelete">Entity to delete</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if deleted, false if not found</returns>
            public static async Task<bool> DeleteAsync<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                if (entityToDelete == null)
                    throw new ArgumentException("Cannot Delete null Object", nameof(entityToDelete));
    
                var type = typeof(T);
    
                if (type.IsArray)
                {
                    type = type.GetElementType();
                }
                else if (type.IsGenericType())
                {
                    type = type.GetGenericArguments()[0];
                }
    
                var keyProperties = KeyPropertiesCache(type);
                var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
                if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
                    throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");
    
                var name = GetTableName(type);
                keyProperties.AddRange(explicitKeyProperties);
    
                var sb = new StringBuilder();
                sb.AppendFormat("DELETE FROM {0} WHERE ", name);
    
                for (var i = 0; i < keyProperties.Count; i++)
                {
                    var property = keyProperties[i];
                    sb.AppendFormat("{0} = :{1}", property.Name, property.Name);
                    if (i < keyProperties.Count - 1)
                        sb.AppendFormat(" AND ");
                }
                var deleted = await connection.ExecuteAsync(sb.ToString(), entityToDelete, transaction, commandTimeout).ConfigureAwait(false);
                return deleted > 0;
            }
    
            /// <summary>
            /// Delete all entities in the table related to the type T asynchronously using .NET 4.5 Task.
            /// </summary>
            /// <typeparam name="T">Type of entity</typeparam>
            /// <param name="connection">Open SqlConnection</param>
            /// <param name="transaction">The transaction to run under, null (the default) if none</param>
            /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
            /// <returns>true if deleted, false if none found</returns>
            public static async Task<bool> DeleteAllAsync<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
            {
                var type = typeof(T);
                var statement = "DELETE FROM " + GetTableName(type);
                var deleted = await connection.ExecuteAsync(statement, null, transaction, commandTimeout).ConfigureAwait(false);
                return deleted > 0;
            }
        }
    }
    
    public partial interface ISqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);
    }
    
    public partial class OracleAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var keyFirst = keyProperties.First();
            var keyName = keyFirst.Name;
            var cmd = $"BEGIN INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}) RETURNING {keyName} INTO :ID; END;";//$"BEGIN INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}) RETURNING ID INTO :ID; END;";
    
            var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout);
            var idKey = multi.Command.Parameters["ID"] as IDataParameter;
            if (keyFirst.PropertyType == typeof(int) || keyFirst.PropertyType == typeof(long))
            {
                return (int)idKey.Value;
            }
            return 1;
        }
    }
    public partial class SqlServerAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"INSERT INTO {tableName} ({columnList}) values ({parameterList}); SELECT SCOPE_IDENTITY() id";
            var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);
    
            var first = multi.Read().FirstOrDefault();
            if (first == null || first.id == null) return 0;
    
            var id = (int)first.id;
            var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (pi.Length == 0) return id;
    
            var idp = pi[0];
            idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);
    
            return id;
        }
    }
    
    public partial class MySqlAdapter
    {
        /// <summary>
        /// Inserts <paramref name="entityToInsert"/> into the database, returning the Id of the row created.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        /// <param name="transaction">The transaction to use.</param>
        /// <param name="commandTimeout">The command timeout to use.</param>
        /// <param name="tableName">The table to insert into.</param>
        /// <param name="columnList">The columns to set with this insert.</param>
        /// <param name="parameterList">The parameters to set for this insert.</param>
        /// <param name="keyProperties">The key columns in this table.</param>
        /// <param name="entityToInsert">The entity to insert.</param>
        /// <returns>The Id of the row created.</returns>
        public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName,
            string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
        {
            var cmd = $"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList})";
            await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);
            var r = await connection.QueryAsync<dynamic>("SELECT LAST_INSERT_ID() id", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false);
    
            var id = r.First().id;
            if (id == null) return 0;
            var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
            if (pi.Length == 0) return Convert.ToInt32(id);
    
            var idp = pi[0];
            idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);
    
            return Convert.ToInt32(id);
        }
    }
    
    
    }
  • 相关阅读:
    Vue中computed和watch的区别
    JS基础语法
    JDBC
    表设计
    查询语句
    反射
    网络端
    多线程
    HashMap
    IO
  • 原文地址:https://www.cnblogs.com/ly7454/p/8012094.html
Copyright © 2011-2022 走看看