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 == )
- {
- 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 != ) return true;
- var writeAttribute = (WriteAttribute)attributes[];
- 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 > )
- throw new DataException($"{method}<T> only supports an entity with a single [Key] or [ExplicitKey] property");
- if (keyCount == )
- throw new DataException($"{method}<T> only supports an entity with a [Key] or an [ExplicitKey] property");
- return keys.Count > ? keys[] : explicitKeys[];
- }
- /// <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();
- }
- }
- 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()[];
- }
- 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 = ; i < allPropertiesExceptKeyAndComputed.Count; i++)
- {
- var property = allPropertiesExceptKeyAndComputed[i];
- adapter.AppendColumnName(sbColumnList, property.Name); //fix for issue #336
- if (i < allPropertiesExceptKeyAndComputed.Count - )
- sbColumnList.Append(", ");
- }
- var sbParameterList = new StringBuilder(null);
- for (var i = ; i < allPropertiesExceptKeyAndComputed.Count; i++)
- {
- var property = allPropertiesExceptKeyAndComputed[i];
- sbParameterList.AppendFormat(":{0}", property.Name);
- if (i < allPropertiesExceptKeyAndComputed.Count - )
- 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()[];
- }
- var keyProperties = KeyPropertiesCache(type).ToList(); //added ToList() due to issue #418, must work on a list copy
- var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
- if (keyProperties.Count == && explicitKeyProperties.Count == )
- 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 = ; i < nonIdProps.Count; i++)
- {
- var property = nonIdProps[i];
- adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336
- if (i < nonIdProps.Count - )
- sb.AppendFormat(", ");
- }
- sb.Append(" WHERE ");
- for (var i = ; i < keyProperties.Count; i++)
- {
- var property = keyProperties[i];
- adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336
- if (i < keyProperties.Count - )
- sb.AppendFormat(" AND ");
- }
- var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
- return updated > ;
- }
- /// <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()[];
- }
- var keyProperties = KeyPropertiesCache(type).ToList(); //added ToList() due to issue #418, must work on a list copy
- var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
- if (keyProperties.Count == && explicitKeyProperties.Count == )
- 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 = ; i < keyProperties.Count; i++)
- {
- var property = keyProperties[i];
- adapter.AppendColumnNameEqualsValue(sb, property.Name); //fix for issue #336
- if (i < keyProperties.Count - )
- sb.AppendFormat(" AND ");
- }
- var deleted = connection.Execute(sb.ToString(), entityToDelete, transaction, commandTimeout);
- return deleted > ;
- }
- /// <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 > ;
- }
- /// <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 ;
- }
- /// <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 ;
- var id = (int)first.id;
- var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
- if (propertyInfos.Length == ) return id;
- var idProperty = propertyInfos[];
- 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[].id == null) return ;
- var id = (int)r[].id;
- var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
- if (propertyInfos.Length == ) return id;
- var idProperty = propertyInfos[];
- 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 ;
- var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
- if (propertyInfos.Length == ) return Convert.ToInt32(id);
- var idp = propertyInfos[];
- 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 == )
- {
- 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 = ;
- foreach (var p in propertyInfos)
- {
- var value = ((IDictionary<string, object>)results[])[p.Name.ToLower()];
- p.SetValue(entityToInsert, value, null);
- if (id == )
- 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 == ) return id;
- var idProperty = propertyInfos[];
- 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[].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 ;
- if (propertyInfos.Length == ) return Convert.ToInt32(id);
- var idp = propertyInfos[];
- 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()[];
} 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 = ; i < allPropertiesExceptKeyAndComputed.Count; i++)
{
var property = allPropertiesExceptKeyAndComputed[i];
sqlAdapter.AppendColumnName(sbColumnList, property.Name);
if (i < allPropertiesExceptKeyAndComputed.Count - )
sbColumnList.Append(", ");
} var sbParameterList = new StringBuilder(null);
for (var i = ; i < allPropertiesExceptKeyAndComputed.Count; i++)
{
var property = allPropertiesExceptKeyAndComputed[i];
sbParameterList.AppendFormat(":{0}", property.Name);
if (i < allPropertiesExceptKeyAndComputed.Count - )
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()[];
} var keyProperties = KeyPropertiesCache(type);
var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
if (keyProperties.Count == && explicitKeyProperties.Count == )
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 = ; i < nonIdProps.Count; i++)
{
var property = nonIdProps[i];
adapter.AppendColumnNameEqualsValue(sb, property.Name);
if (i < nonIdProps.Count - )
sb.AppendFormat(", ");
}
sb.Append(" WHERE ");
for (var i = ; i < keyProperties.Count; i++)
{
var property = keyProperties[i];
adapter.AppendColumnNameEqualsValue(sb, property.Name);
if (i < keyProperties.Count - )
sb.AppendFormat(" AND ");
}
var updated = await connection.ExecuteAsync(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction).ConfigureAwait(false);
return updated > ;
} /// <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()[];
} var keyProperties = KeyPropertiesCache(type);
var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
if (keyProperties.Count == && explicitKeyProperties.Count == )
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 = ; i < keyProperties.Count; i++)
{
var property = keyProperties[i];
sb.AppendFormat("{0} = :{1}", property.Name, property.Name);
if (i < keyProperties.Count - )
sb.AppendFormat(" AND ");
}
var deleted = await connection.ExecuteAsync(sb.ToString(), entityToDelete, transaction, commandTimeout).ConfigureAwait(false);
return deleted > ;
} /// <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 > ;
}
}
} 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 ;
}
}
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 ; var id = (int)first.id;
var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
if (pi.Length == ) return id; var idp = pi[];
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 ;
var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
if (pi.Length == ) return Convert.ToInt32(id); var idp = pi[];
idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null); return Convert.ToInt32(id);
}
} }
Dapper 封装oracle底层访问数据库的更多相关文章
- ABP框架用Dapper实现通过SQL访问数据库
ABP的框架(2) - 访问数据库 为了防止不提供原网址的转载,特在这里加上原文链接:http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的 ...
- 免安装oracle驱动访问数据库
try { string connStr = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.110.110)( ...
- NET在64位系統使用32位oracle客户端访问数据库
客户在win7 64位系统中安装32位的ora客户端,NET 安装后连线数据库 引发BadImageFomatException. 按客户机安装64位ora客户端也不现实,可能会影响其他应用的正常使用 ...
- 手工搭建基于ABP的框架(2) - 访问数据库
为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的访问.我们将实现两种数据库访问方法来访 ...
- 对比传统方式访问数据库和SpringData访问数据库
我们在写代码的时候应该一边写一边测试,这样的话可以尽快的找到错误,在代码写多了之后去找错误的话不容易给错误定位 传统方式访问数据库 1:创建一个Maven web项目 2:修改pom.xml为以下内容 ...
- 使用Spring.net中对Ado.net的抽象封装来访问数据库
使用Spring.net中对Ado.net的抽象封装来访问数据库 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入 ...
- oracle 事务简介,锁的概念,java访问数据库注意事项
java链接oracle和连接其他数据库一样有两种方式:1 桥接 jdbc-obdc2 jbdc insert语句一次插入大量数据 insert into table (列1,列2,列3) selec ...
- .Net程序员学用Oracle系列(16):访问数据库(ODP.NET)
1..Net for Oracle 常见数据库驱动 1.1.微软提供的驱动 1.2.甲骨文提供的驱动 1.3.其它厂商提供的驱动 2.ODP.NET 常见问题分析 2.1.参数化问题 2.2.方法调用 ...
- Winform 利用 Oracle.ManagedDataAccess访问Oracle数据库
Winform 利用 Oracle.ManagedDataAccess访问Oracle数据库时出现以下错误: Message = "每个配置文件中只允许存在一个 <configSect ...
随机推荐
- Redis代码——Python篇
需要安装的库:redis import redis # 连接数据库 r = redis.StrictRedis(host="localhost", port=6379, passw ...
- 20175204 张湲祯 2018-2019-2《Java程序设计》2
20175204 张湲祯 2018-2019-2<Java程序设计>2 必做课下作业MyCP 要求 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP ...
- Matlab imshow, image, imagesc 三者详细分析
1.显示RGB图像 相同点:这三个函数都是把m*n*3的矩阵中的数值当做RGB值来显示的. 区别:imshow将图像以原始尺寸显示,image和imagesc则会对图像进行适当的缩放(显示出来的尺寸大 ...
- mui 记录
1.轮播添加无限循环 需要在 .mui-slider-group节点上增加.mui-slider-loop类 2.web移动端侧滑与滑动同时存在 参考https://segmentfault.com/ ...
- GDOI2019游记
只是提前开坑啊,CCF不要禁我赛啊QwQ 虽然才初三,不能进省队,但还是要拼一把,至少不能垫底吧. NTF和CDW两位初二巨佬都在四川省选拿了非正式选手Rank3,4,我还有什么理由去摸鱼? Day\ ...
- 树链剖分——模板题hdu3966
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 50005 ]; int he ...
- SpringBoot 整合Dubbo
RPC框架可参考:https://blog.csdn.net/top_code/article/details/54615853 整合可参考:https://www.dalaoyang.cn/arti ...
- 2018-2019-2 网络对抗技术 20165314 Exp3 免杀原理与实践
免杀原理与实践说明 一.实验说明 任务一:正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己利用shellcode编程等免杀工具或技巧:(1.5分) 任务 ...
- 末学者笔记--Centos7系统部署cobbler批量安装系统
[前言]: cobbler是一个可以实现批量安装系统的Linux应用程序.它有别于pxe+kickstart,cobbler可以实现同个服务器批量安装不同操作系统版本. 系统环境准备及其下载cob ...
- 关于DataTable 判断 列名是否存在的方法中英文符合不区分?
最近系统出现一个错误,排查了很久,发现判断DataTable 列名是否存在时,发现一个坑,居然不会区分中英文符合. 有谁知道其中的原理?先记录一下,免得以后忘记这个天坑. 一. 先初始化一个DataT ...