/// <summary> /// Creates and returns a new DbConnection. /// </summary> /// <param name="builder">The DbConnectionStringBuilder containing the connection string.</param> /// <returns>A closed DbConnection.</returns> public static DbConnection Connection(this DbConnectionStringBuilder builder) { if (builder == null) { throw new ArgumentNullException("builder"); } DbConnection connection = null; DbConnection disposable = null; try { // get the connection from the provider connection = InsightDbProvider.For(builder).CreateDbConnection(); disposable = connection; if (connection == null) { throw new ArgumentException("Cannot determine the type of connection from the ConnectionStringBuilder", "builder"); } connection.ConnectionString = builder.ConnectionString; disposable = null; return(connection); } finally { if (disposable != null) { disposable.Dispose(); } } }
/// <summary> /// Create a parameter generator for a dynamic object. /// </summary> /// <param name="command">The command to parse.</param> /// <returns>An action that fills in the command parameters from a dynamic object.</returns> private static Action <IDbCommand, object> CreateDynamicInputParameterGenerator(IDbCommand command) { var provider = InsightDbProvider.For(command); var parameters = provider.DeriveParameters(command); return((cmd, o) => { // make sure that we have a dictionary implementation IDictionary <string, object> dyn = o as IDictionary <string, object>; if (dyn == null) { throw new InvalidOperationException("Dynamic object must support IDictionary<string, object>."); } foreach (var template in parameters) { var p = provider.CloneParameter(cmd, template); // get the value from the object, converting null to db null // note that if the dictionary does not have the value, we leave the value null and then the parameter gets defaulted object value = null; if (dyn.TryGetValue(p.ParameterName, out value)) { if (value == null) { value = DBNull.Value; } else { DbType sqlType = LookupDbType(value.GetType(), null, p.DbType); if (sqlType == DbTypeEnumerable) { cmd.Parameters.Add(p); ListParameterHelper.ConvertListParameter(p, value, cmd); continue; } } } p.Value = value; // if it's a string, fill in the length IDbDataParameter dbDataParameter = p as IDbDataParameter; if (dbDataParameter != null) { dbDataParameter.Size = GetStringParameterLength(value as String, command.CommandType, dbDataParameter.DbType); } // explicitly set the type of the parameter if (value != null && _typeToDbTypeMap.ContainsKey(value.GetType())) { dbDataParameter.DbType = _typeToDbTypeMap[value.GetType()]; } provider.FixupParameter(cmd, p, p.DbType, dyn.GetType(), SerializationMode.Default); cmd.Parameters.Add(p); } }); }
/// <summary> /// Gets the serializer for a specific parameter. /// </summary> /// <param name="command">The command that is being bound.</param> /// <param name="parameter">The parameter that is being bound.</param> /// <param name="prop">The property that is being bound.</param> /// <returns>The serializer.</returns> internal static IDbObjectSerializer GetSerializer(IDbCommand command, IDataParameter parameter, ClassPropInfo prop) { if (InsightDbProvider.For(parameter).IsXmlParameter(command, parameter)) { return(XmlObjectSerializer.Serializer); } return(EvaluateRules(prop)); }
/// <summary> /// Gets the serializer for a specific parameter. /// </summary> /// <param name="reader">The reader that is being bound.</param> /// <param name="column">The index of the column that is being bound.</param> /// <param name="prop">The property that is being bound.</param> /// <returns>The serializer.</returns> internal static IDbObjectSerializer GetSerializer(IDataReader reader, int column, ClassPropInfo prop) { if (InsightDbProvider.For(reader).IsXmlColumn(reader.GetSchemaTable(), column)) { return(XmlObjectSerializer.Serializer); } return(EvaluateRules(prop)); }
/// <inheritdoc/> public override IDbConnection CloneDbConnection(IDbConnection connection) { if (connection == null) throw new ArgumentNullException("connection"); // clone the inner connection var reliable = (ReliableConnection)connection; var innerConnection = GetInnerConnection(connection); var innerProvider = InsightDbProvider.For(innerConnection); var clonedInnerConnection = innerProvider.CloneDbConnection(innerConnection); return new ReliableConnection((DbConnection)clonedInnerConnection, reliable.RetryStrategy); }
/// <summary> /// Create a parameter generator for a dynamic object. /// </summary> /// <param name="command">The command to parse.</param> /// <returns>An action that fills in the command parameters from a dynamic object.</returns> static Action <IDbCommand, object> CreateDynamicInputParameterGenerator(IDbCommand command) { var provider = InsightDbProvider.For(command); var parameters = provider.DeriveParameters(command); return((cmd, o) => { // make sure that we have a dictionary implementation IDictionary <string, object> dyn = o as IDictionary <string, object>; if (dyn == null) { throw new InvalidOperationException("Dynamic object must support IDictionary<string, object>."); } foreach (var template in parameters) { var p = provider.CloneParameter(command, template); // get the value from the object, converting null to db null // note that if the dictionary does not have the value, we leave the value null and then the parameter gets defaulted object value = null; if (dyn.TryGetValue(p.ParameterName, out value) && value == null) { value = DBNull.Value; } p.Value = value; // if it's a string, fill in the length IDbDataParameter dbDataParameter = p as IDbDataParameter; if (dbDataParameter != null) { string s = value as string; if (s != null) { int length = s.Length; if (length > 4000) { length = -1; } dbDataParameter.Size = length; } } // explicitly set the type of the parameter if (value != null && _typeToDbTypeMap.ContainsKey(value.GetType())) { dbDataParameter.DbType = _typeToDbTypeMap[value.GetType()]; } cmd.Parameters.Add(p); } }); }
/// <summary> /// Add parameters to a given command. /// </summary> /// <param name="cmd">The command to add parameters to.</param> /// <param name="parameters">The object containing parameters to add.</param> public static void AddParameters(this IDbCommand cmd, object parameters = null) { // fill in a null parameter with empty parameter if (parameters == null) { parameters = Parameters.Empty; } DbParameterGenerator.GetInputParameterGenerator(cmd, parameters.GetType())(cmd, parameters); InsightDbProvider.For(cmd).FixupCommand(cmd); }
/// <inheritdoc/> public override IDbConnection CloneDbConnection(IDbConnection connection) { if (connection == null) { throw new ArgumentNullException("connection"); } // clone the inner connection var innerConnection = GetInnerConnection(connection); var innerProvider = InsightDbProvider.For(innerConnection); var clonedInnerConnection = (DbConnection)innerProvider.CloneDbConnection(innerConnection); return(new GlimpseDbConnection(clonedInnerConnection)); }
/// <summary> /// Determines if an exception is a transient error. /// </summary> /// <param name="exception">The exception to test.</param> /// <returns>True if the exception is a transient error, false if the command should not be retried.</returns> public virtual bool IsTransientException(Exception exception) { InsightDbProvider provider; try { provider = InsightDbProvider.For(exception); } catch (NotImplementedException) { // if the provider lookup fails, then this can't be a transient exception return(false); } return(provider.IsTransientException(exception)); }
/// <summary> /// Derive the parameters that are needed to execute a given command. /// </summary> /// <param name="cmd">The command to execute.</param> private void DeriveParameters(IDbCommand cmd) { var provider = InsightDbProvider.For(cmd); // look in the concurrent dictionary to find the parameters. // if not found, call the Server to get them. var parameterList = _parameters.GetOrAdd( cmd.CommandText, name => provider.DeriveParameters(cmd).ToList()); // copy the parameter list foreach (IDataParameter parameter in parameterList) { cmd.Parameters.Add(provider.CloneParameter(cmd, parameter)); } }
private ObjectReader(IDbCommand command, Type type, IDataReader reader) { var provider = InsightDbProvider.For(command); _columns = new List <ColumnInfo>(); var accessors = new List <Func <object, object> >(); // create a mapping, and only keep mappings that match our modified schema var sourceMappings = ColumnMapping.MapColumns(type, reader).ToList(); var sourceColumns = ColumnInfo.FromDataReader(reader); IsAtomicType = TypeHelper.IsAtomicType(type); if (!IsAtomicType) { int columnCount = sourceMappings.Count; for (int i = 0; i < columnCount; i++) { var mapping = sourceMappings[i]; var column = sourceColumns[i]; if (column.IsReadOnly && !column.IsIdentity) { continue; } _columns.Add(column); if (mapping == null) { accessors.Add(null); } else { accessors.Add(GenerateAccessor(type, mapping, column)); } } } else { var column = sourceColumns.First(); _columns.Add(column); accessors.Add(GenerateAccessor(type, null, column)); } _accessors = accessors.ToArray(); }
/// <summary> /// Initialize the serializer on the given mapping. /// </summary> /// <param name="mapping">The mapping being evaluated.</param> /// <param name="reader">The reader being evaluated.</param> /// <param name="command">The command being evaluated.</param> /// <param name="parameters">The parameters being evaluated.</param> /// <param name="i">The index of the parameter being evaluated.</param> private static void InitializeMappingSerializer(ColumnMappingEventArgs mapping, IDataReader reader, IDbCommand command, IList <IDataParameter> parameters, int i) { // if the provider knows that this is an xml field, then automatically use that if ((command != null && InsightDbProvider.For(command).IsXmlParameter(command, parameters[i])) || (reader != null && InsightDbProvider.For(reader).IsXmlColumn(command, reader.GetSchemaTable(), i))) { mapping.SerializationMode = SerializationMode.Xml; mapping.Serializer = typeof(XmlObjectSerializer); } else { mapping.SerializationMode = mapping.SerializationMode ?? mapping.ClassPropInfo.SerializationMode; switch (mapping.SerializationMode) { default: case SerializationMode.Default: mapping.Serializer = ColumnMapping.DefaultObjectSerializer; break; case SerializationMode.Xml: mapping.Serializer = typeof(XmlObjectSerializer); break; case SerializationMode.Json: mapping.Serializer = JsonObjectSerializer.SerializerType; break; case SerializationMode.ToString: mapping.Serializer = typeof(ToStringObjectSerializer); break; case SerializationMode.Custom: mapping.Serializer = mapping.Serializer ?? mapping.ClassPropInfo.Serializer ?? ColumnMapping.DefaultObjectSerializer; break; } } // if we allow atomic types to get a different serializer, then there are certain situations where we can't figure out the right thing to do if (mapping.SerializationMode != SerializationMode.Default && TypeHelper.IsAtomicType(mapping.ClassPropInfo.MemberType) && mapping.ClassPropInfo.MemberType != typeof(string)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Atomic types cannot have a column serializer: {0}.{1}", mapping.ClassPropInfo.Type.Name, mapping.ClassPropInfo.Name)); } }
/// <summary> /// Takes the output parameters of a result set and inserts them into the result object. /// </summary> /// <typeparam name="T">The type of object to return.</typeparam> /// <param name="command">The command to evaluate.</param> /// <param name="result">The result to insert into.</param> /// <returns>The object that was filled in.</returns> public static T OutputParameters <T>(this IDbCommand command, T result) { if (command == null) { throw new ArgumentNullException("command"); } // if there is no output object, don't attempt to fill it in if (result == null) { return(result); } InsightDbProvider.For(command).FixupOutputParameters(command); if (result is DynamicObject) { // handle dynamic objects by assigning their properties right into the dictionary IDictionary <string, object> dictionary = result as IDictionary <string, object>; if (dictionary == null) { throw new InvalidOperationException("Dynamic object must support IDictionary<string, object>."); } foreach (IDataParameter p in command.Parameters) { if (p.Direction.HasFlag(ParameterDirection.Output)) { dictionary[p.ParameterName] = p.Value; } } } else { DbParameterGenerator.GetOutputParameterConverter(command, result.GetType())(command, result); } return(result); }
/// <summary> /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. /// </summary> public static void RegisterProvider() { InsightDbProvider.RegisterProvider(new PostgreSQLInsightDbProvider()); }
static Action <IDbCommand, object> CreateClassInputParameterGenerator(IDbCommand command, Type type) { var provider = InsightDbProvider.For(command); var parameters = provider.DeriveParameters(command); // special case if the parameters object is an IEnumerable or Array // look for the parameter that is a Structured object and pass the array to the TVP // note that string supports ienumerable, so exclude atomic types var enumerable = type.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable <>)); if (enumerable != null && type != typeof(string) && parameters.OfType <IDataParameter>().Where(p => p.Direction.HasFlag(ParameterDirection.Input)).Count() == 1) { return((IDbCommand cmd, object o) => { // don't use the provider above. The command may be unwrapped by the time we get back here var tableParameter = InsightDbProvider.For(cmd).CloneParameter(cmd, parameters.OfType <IDataParameter>().Single(p => p.Direction.HasFlag(ParameterDirection.Input))); cmd.Parameters.Add(tableParameter); ListParameterHelper.AddListParameter(tableParameter, o, cmd); }); } // get the mapping of the properties for the type var mappings = ColumnMapping.Parameters.CreateMapping(type, null, command, parameters, null, 0, parameters.Count, true); // start creating a dynamic method Type typeOwner = type.HasElementType ? type.GetElementType() : type; var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateInputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true); var il = dm.GetILGenerator(); // copy the parameters into the command object var parametersLocal = il.DeclareLocal(typeof(IDataParameter[])); new StaticFieldStorage(provider).EmitLoad(il); il.Emit(OpCodes.Ldarg_0); new StaticFieldStorage(parameters).EmitLoad(il); il.Emit(OpCodes.Call, typeof(InsightDbProvider).GetMethod("CopyParameters", BindingFlags.NonPublic | BindingFlags.Instance)); il.Emit(OpCodes.Stloc, parametersLocal); // go through all of the mappings for (int i = 0; i < mappings.Length; i++) { var mapping = mappings[i]; var dbParameter = parameters[i]; // if there is no mapping for the parameter if (mapping == null) { // sql will silently eat table parameters that are not specified, and that can be difficult to debug if (provider.IsTableValuedParameter(command, dbParameter)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Table parameter {0} must be specified", dbParameter.ParameterName)); } // unspecified input parameters get skipped if (dbParameter.Direction == ParameterDirection.Input) { parameters[i] = null; } continue; } // get the parameter il.Emit(OpCodes.Ldloc, parametersLocal); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldelem, typeof(IDataParameter)); var prop = mapping.ClassPropInfo; // look up the best type to use for the parameter DbType sqlType = LookupDbType(mapping, dbParameter.DbType); // give the provider an opportunity to fix up the template parameter (e.g. set UDT type names) provider.FixupParameter(command, dbParameter, sqlType, prop.MemberType); /////////////////////////////////////////////////////////////// // We have a parameter, start handling all of the other types /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // Get the value from the object onto the stack /////////////////////////////////////////////////////////////// il.Emit(OpCodes.Ldarg_1); prop.EmitGetValue(il); /////////////////////////////////////////////////////////////// // Special case support for enumerables. If the type is -1 (our workaround, then call the list parameter method) /////////////////////////////////////////////////////////////// if (sqlType == DbTypeEnumerable) { // we have the parameter and the value as object, add the command il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, typeof(ListParameterHelper).GetMethod("AddListParameter", BindingFlags.Static | BindingFlags.NonPublic)); continue; } // if this is a value type, then box the value so the compiler can check the type and we can call methods on it if (prop.MemberType.IsValueType) { il.Emit(OpCodes.Box, prop.MemberType); } // special conversions for timespan to datetime if ((sqlType == DbType.Time && dbParameter.DbType != DbType.Time) || (dbParameter.DbType == DbType.DateTime || dbParameter.DbType == DbType.DateTime2 || dbParameter.DbType == DbType.DateTimeOffset)) { IlHelper.EmitLdInt32(il, (int)dbParameter.DbType); il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("ObjectToSqlDateTime")); } // if it's class type, boxed value type (in an object), or nullable, then we have to check for null Label readyToSetLabel = il.DefineLabel(); if (!prop.MemberType.IsValueType || Nullable.GetUnderlyingType(prop.MemberType) != null) { Label notNull = il.DefineLabel(); // check to see if it's not null il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, notNull); // it's null. replace the value with DbNull il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldsfld, _dbNullValue); // value is set to null. ready to set the property. il.Emit(OpCodes.Br_S, readyToSetLabel); // we know the value is not null il.MarkLabel(notNull); } /////////////////////////////////////////////////////////////// // if this is a linq binary, convert it to a byte array /////////////////////////////////////////////////////////////// if (prop.MemberType == TypeHelper.LinqBinaryType) { il.Emit(OpCodes.Callvirt, TypeHelper.LinqBinaryToArray); } else if (prop.MemberType == typeof(XmlDocument)) { // we are sending up an XmlDocument. ToString just returns the classname, so use the outerxml. il.Emit(OpCodes.Callvirt, prop.MemberType.GetProperty("OuterXml").GetGetMethod()); } else if (prop.MemberType == typeof(XDocument)) { // we are sending up an XDocument. Use ToString. il.Emit(OpCodes.Callvirt, prop.MemberType.GetMethod("ToString", new Type[] { })); } else if (prop.MemberType.GetInterfaces().Contains(typeof(IConvertible)) || prop.MemberType == typeof(object)) { // if the type supports IConvertible, then let SQL convert it // if the type is object, we can't do anything, so let SQL attempt to convert it } else if (!TypeHelper.IsAtomicType(prop.MemberType)) { if (mapping.Serializer != null) { var serializerMethod = mapping.Serializer.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(Type) }, null); if (serializerMethod == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Serializer type {0} needs the method 'public static string Serialize(object, Type)'", mapping.Serializer.Name)); } il.EmitLoadType(prop.MemberType); il.Emit(OpCodes.Call, serializerMethod); } else if (dbParameter.DbType != DbType.Object) { // it's not a system type and it's not IConvertible, so let's add it as a string and let the data engine convert it. il.Emit(OpCodes.Callvirt, prop.MemberType.GetMethod("ToString", new Type[] { })); } // if we touched the value, it's possible that serializing returns a null if (dbParameter.DbType != DbType.Object) { Label internalValueIsNotNull = il.DefineLabel(); // check to see if it's not null il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, internalValueIsNotNull); // it's null. replace the value with DbNull il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldsfld, _dbNullValue); il.MarkLabel(internalValueIsNotNull); } } /////////////////////////////////////////////////////////////// // p.Value = value /////////////////////////////////////////////////////////////// // push parameter is at top of method // value is above il.MarkLabel(readyToSetLabel); if (prop.MemberType == typeof(string)) { il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterStringValue", BindingFlags.NonPublic | BindingFlags.Static)); } else { il.Emit(OpCodes.Callvirt, _iDataParameterSetValue); } } il.Emit(OpCodes.Ret); return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>))); }
/// <summary> /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. /// </summary> public static void RegisterProvider() { InsightDbProvider.RegisterProvider(new MiniProfilerInsightDbProvider()); }
private static Action <IDbCommand, object> CreateClassInputParameterGenerator(IDbCommand command, Type type) { var provider = InsightDbProvider.For(command); var parameters = provider.DeriveParameters(command); // special case if the parameters object is an IEnumerable or Array // look for the parameter that is a Structured object and pass the array to the TVP // note that string supports ienumerable, so exclude atomic types var enumerable = type.GetInterfaces().FirstOrDefault(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IEnumerable <>)); if (enumerable != null && type != typeof(string) && parameters.OfType <IDataParameter>().Where(p => p.Direction.HasFlag(ParameterDirection.Input)).Count() == 1) { return((IDbCommand cmd, object o) => { // don't use the provider above. The command may be unwrapped by the time we get back here var tableParameter = InsightDbProvider.For(cmd).CloneParameter(cmd, parameters.OfType <IDataParameter>().Single(p => p.Direction.HasFlag(ParameterDirection.Input))); cmd.Parameters.Add(tableParameter); ListParameterHelper.ConvertListParameter(tableParameter, o, cmd); }); } // get the mapping of the properties for the type var mappings = ColumnMapping.MapParameters(type, command, parameters); // start creating a dynamic method Type typeOwner = type.HasElementType ? type.GetElementType() : type; var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateInputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true); var il = dm.GetILGenerator(); // copy the parameters into the command object var parametersLocal = il.DeclareLocal(typeof(IDataParameter[])); StaticFieldStorage.EmitLoad(il, provider); il.Emit(OpCodes.Ldarg_0); StaticFieldStorage.EmitLoad(il, parameters); il.Emit(OpCodes.Call, typeof(InsightDbProvider).GetMethod("CopyParameters", BindingFlags.NonPublic | BindingFlags.Instance)); il.Emit(OpCodes.Stloc, parametersLocal); // go through all of the mappings for (int i = 0; i < mappings.Count; i++) { var mapping = mappings[i]; var dbParameter = parameters[i]; // if there is no mapping for the parameter if (mapping == null) { // sql will silently eat table parameters that are not specified, and that can be difficult to debug if (provider.IsTableValuedParameter(command, dbParameter)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Table parameter {0} must be specified", dbParameter.ParameterName)); } // unspecified input parameters get skipped if (dbParameter.Direction == ParameterDirection.Input) { parameters[i] = null; } continue; } var memberType = mapping.Member.MemberType; var serializer = mapping.Serializer; // get the parameter il.Emit(OpCodes.Ldloc, parametersLocal); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldelem, typeof(IDataParameter)); // look up the best type to use for the parameter DbType sqlType = LookupDbType(memberType, serializer, dbParameter.DbType); // give the provider an opportunity to fix up the template parameter (e.g. set UDT type names) provider.FixupParameter(command, dbParameter, sqlType, memberType, mapping.Member.SerializationMode); // give a chance to override the best guess parameter DbType overriddenSqlType = sqlType; if (sqlType != DbTypeEnumerable) { overriddenSqlType = ColumnMapping.MapParameterDataType(memberType, command, dbParameter, sqlType); } /////////////////////////////////////////////////////////////// // We have a parameter, start handling all of the other types /////////////////////////////////////////////////////////////// if (overriddenSqlType != sqlType) { sqlType = overriddenSqlType; dbParameter.DbType = sqlType; } /////////////////////////////////////////////////////////////// // Get the value from the object onto the stack /////////////////////////////////////////////////////////////// il.Emit(OpCodes.Ldarg_1); if (type.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Unbox_Any, type); } /////////////////////////////////////////////////////////////// // Special case support for enumerables. If the type is -1 (our workaround, then call the list parameter method) /////////////////////////////////////////////////////////////// if (sqlType == DbTypeEnumerable) { // we have the parameter and the value as object, add the command ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, typeof(ListParameterHelper).GetMethod("ConvertListParameter", BindingFlags.Static | BindingFlags.NonPublic)); continue; } Label readyToSetLabel = il.DefineLabel(); ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel); // special conversions for timespan to datetime if ((sqlType == DbType.Time && dbParameter.DbType != DbType.Time) || (dbParameter.DbType == DbType.DateTime || dbParameter.DbType == DbType.DateTime2 || dbParameter.DbType == DbType.DateTimeOffset)) { IlHelper.EmitLdInt32(il, (int)dbParameter.DbType); il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("ObjectToSqlDateTime")); } // if it's class type, boxed value type (in an object), or nullable, then we have to check for null var nullableUnderlyingType = Nullable.GetUnderlyingType(memberType); if (!memberType.GetTypeInfo().IsValueType || nullableUnderlyingType != null) { Label notNull = il.DefineLabel(); // check to see if it's not null il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue, notNull); // it's null. replace the value with DbNull il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldsfld, _dbNullValue); // value is set to null. ready to set the property. il.Emit(OpCodes.Br, readyToSetLabel); // we know the value is not null il.MarkLabel(notNull); } // some providers (notably npgsql > 4.0) don't convert enums to ints, so we do it for them if ((memberType != null && memberType.GetTypeInfo() != null && memberType.GetTypeInfo().IsEnum) || (nullableUnderlyingType != null && nullableUnderlyingType.GetTypeInfo() != null && nullableUnderlyingType.GetTypeInfo().IsEnum)) { var enumType = nullableUnderlyingType ?? memberType; // ClassPropInfo.EmitGetValue has the enum boxed, so unbox, cast, and re-box switch (dbParameter.DbType) { case DbType.Int16: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I2); il.Emit(OpCodes.Box, typeof(Int16)); break; case DbType.Int32: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Box, typeof(Int32)); break; case DbType.Int64: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Box, typeof(Int64)); break; } } /////////////////////////////////////////////////////////////// // if this is a linq binary, convert it to a byte array /////////////////////////////////////////////////////////////// if (memberType == TypeHelper.LinqBinaryType) { il.Emit(OpCodes.Callvirt, TypeHelper.LinqBinaryToArray); } else if (memberType == typeof(XmlDocument)) { // we are sending up an XmlDocument. ToString just returns the classname, so use the outerxml. il.Emit(OpCodes.Callvirt, memberType.GetProperty("OuterXml").GetGetMethod()); } else if (memberType == typeof(XDocument)) { // we are sending up an XDocument. Use ToString. il.Emit(OpCodes.Callvirt, memberType.GetMethod("ToString", new Type[] { })); } else if (serializer != null && serializer.CanSerialize(memberType, sqlType)) { il.EmitLoadType(memberType); StaticFieldStorage.EmitLoad(il, serializer); il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SerializeParameterValue", BindingFlags.NonPublic | BindingFlags.Static)); } /////////////////////////////////////////////////////////////// // p.Value = value /////////////////////////////////////////////////////////////// // push parameter is at top of method // value is above il.MarkLabel(readyToSetLabel); if (memberType == typeof(string)) { il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterStringValue", BindingFlags.NonPublic | BindingFlags.Static)); } else if ((memberType == typeof(Guid?) || (memberType == typeof(Guid))) && dbParameter.DbType != DbType.Guid && command.CommandType == CommandType.StoredProcedure) { il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterGuidValue", BindingFlags.NonPublic | BindingFlags.Static)); } else { il.Emit(OpCodes.Callvirt, _iDataParameterSetValue); } } il.Emit(OpCodes.Ret); return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>))); }
protected internal object DoInvokeMember(InvokeMemberBinder binder, object[] args, Type returnType) { if (binder == null) { throw new ArgumentNullException("binder"); } if (args == null) { throw new ArgumentNullException("args"); } bool doAsync = false; IDbCommand cmd = null; int specialParameters = 0; int? timeout = null; IDbTransaction transaction = null; IQueryReader returns = null; object outputParameters = null; CancellationToken cancellationToken = CancellationToken.None; CallInfo callInfo = binder.CallInfo; int unnamedParameterCount = callInfo.ArgumentCount - callInfo.ArgumentNames.Count; // check the proc name - if it ends with Async, then call it asynchronously and return the results string procName = binder.Name; if (procName.EndsWith("async", StringComparison.OrdinalIgnoreCase)) { procName = procName.Substring(0, procName.Length - 5); doAsync = true; } // if there is a schema, use it if (!String.IsNullOrWhiteSpace(_schema)) { procName = _schema + "." + procName; } // go through the arguments and look for our special arguments // NOTE: this is intentionally case-sensitive so that you can use other cases if you need to pass a parameter by the same name. var argumentNames = callInfo.ArgumentNames; for (int i = 0; i < argumentNames.Count; i++) { switch (argumentNames[i]) { case "cancellationToken": cancellationToken = (CancellationToken)args[i + unnamedParameterCount]; specialParameters++; break; case "transaction": transaction = (IDbTransaction)args[i + unnamedParameterCount]; specialParameters++; break; case "commandTimeout": timeout = (int)args[i + unnamedParameterCount]; specialParameters++; break; case "returnType": returnType = (Type)args[i + unnamedParameterCount]; specialParameters++; break; case "returns": returns = (IQueryReader)args[i + unnamedParameterCount]; specialParameters++; break; case "outputParameters": outputParameters = args[i + unnamedParameterCount]; specialParameters++; break; #if !NOCOMPATIBILITY case "withGraph": { var withGraph = (Type)args[i + unnamedParameterCount]; dynamic graph = System.Activator.CreateInstance(withGraph); returns = graph.GetListReader(); specialParameters++; break; } case "withGraphs": { var types = (Type[])args[i + unnamedParameterCount]; returns = (IQueryReader)types[0].GetMethod("GetDefinitionFromGraphArray", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Invoke(null, new object[] { returnType, types }); specialParameters++; break; } #endif } } try { // if there is exactly one unnamed parameter, and the named parameters are all special parameters, and it's a reference type (and not a string) // then we will attempt to use the object's fields as the parameter values // this is so you can send an entire object to an insert method if (unnamedParameterCount == 1 && (callInfo.ArgumentNames.Count == specialParameters) && !args[0].GetType().IsValueType&& args[0].GetType() != typeof(String)) { cmd = _connection.CreateCommand(procName, args[0], CommandType.StoredProcedure, timeout, transaction); } else { // this isn't a single-object parameter, so we are going to map the parameters by position and by name // create a command cmd = _connection.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = procName; if (timeout.HasValue) { cmd.CommandTimeout = timeout.Value; } // unwrap the transaction because the transaction has to match the command and connection if (transaction != null) { cmd.Transaction = DBConnectionExtensions.UnwrapDbTransaction(transaction); } // fill in the parameters for the command object // we will do the values next DeriveParameters(cmd); // look at the unnamed parameters first. we will add them by position. var inputParameters = cmd.Parameters.OfType <IDataParameter>().Where(p => p.Direction.HasFlag(ParameterDirection.Input)).ToList(); for (int i = 0; i < unnamedParameterCount; i++) { inputParameters[i].Value = args[i]; } // go through all of the named arguments next. Note that they may overwrite indexed parameters. for (int i = unnamedParameterCount; i < callInfo.ArgumentNames.Count; i++) { string argumentName = callInfo.ArgumentNames[i]; // ignore our special parameters if (argumentName == "cancellationToken" || argumentName == "transaction" || argumentName == "commandTimeout" || argumentName == "returnType" || argumentName == "returns" || #if !NOCOMPATIBILITY argumentName == "withGraph" || argumentName == "withGraphs" || #endif argumentName == "outputParameters") { continue; } IDataParameter p = cmd.Parameters.OfType <IDataParameter>().First(parameter => String.Equals(parameter.ParameterName, argumentName, StringComparison.OrdinalIgnoreCase)); p.Value = args[i]; } // special handling for table parameters - replace them with list parameters // note that we may be modifying the parameters collection, so we copy the list here var provider = InsightDbProvider.For(cmd); foreach (var p in cmd.Parameters.OfType <IDataParameter>().Where(p => provider.IsTableValuedParameter(cmd, p)).ToList()) { // if any parameters are missing table parameters, then alert the developer if (p.Value == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Table parameter {0} must be specified", p.ParameterName)); } // convert the value to an objectreader DbParameterGenerator.ListParameterHelper.ConvertListParameter(p, p.Value, cmd); } } // if we don't have a type, use FastExpando if (returnType == null) { returnType = typeof(FastExpando); } // if there was no named returns definition, check for an unnamed IQueryParameter if (returns == null) { returns = args.OfType <IQueryReader>().FirstOrDefault(); } // if there is no returns definition supplied, get one from the return type if (returns == null) { if (returnType.IsSubclassOf(typeof(Results))) { returns = (IQueryReader)returnType.GetMethod("GetReader", BindingFlags.Public | BindingFlags.Static).Invoke(null, Parameters.EmptyArray); } else { returns = (IQueryReader)typeof(ListReader <>).MakeGenericType(returnType).GetField("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null); } } // get the proper query method to call based on whether we are doing this async and whether there is a single or multiple result set // the nice thing is that the generic expansion will automatically create the proper return type like IList<T> or Results<T>. if (doAsync) { return(CallQueryAsync(cmd, returns, cancellationToken)); } else { return(CallQuery(cmd, returns, outputParameters)); } } finally { if (cmd != null) { cmd.Dispose(); } } }
/// <summary> /// Add a list of objects as a table-valued parameter. /// </summary> /// <param name="parameter">The parameter to modify.</param> /// <param name="list">The list to add to the parameter.</param> /// <param name="command">The command to add parameters to.</param> /// <param name="listType">The type that the list contains.</param> private static void AddListParameterByClass(IDataParameter parameter, IEnumerable list, IDbCommand command, Type listType) { InsightDbProvider.For(command).SetupTableValuedParameter(command, parameter, list, listType); }
/// <summary> /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. /// </summary> public static void RegisterProvider() { InsightDbProvider.RegisterProvider(new GlimpseInsightDbProvider()); }
/// <summary> /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. /// </summary> public static void RegisterProvider() { InsightDbProvider.RegisterProvider(new SqlInsightDbProvider()); }
/// <summary> /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. /// </summary> public static void RegisterProvider() => InsightDbProvider.RegisterProvider(new MySqlConnectorInsightDbProvider());
private ObjectReader(IDbCommand command, Type type, IDataReader reader) { var provider = InsightDbProvider.For(command); // copy the schema and fix it SchemaTable = reader.GetSchemaTable().Copy(); FixupSchemaNumericScale(); FixupSchemaRemoveReadOnlyColumns(); IsAtomicType = TypeHelper.IsAtomicType(type); if (!IsAtomicType) { // create a mapping, and only keep mappings that match our modified schema var mappings = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, true) .Where(m => m != null).ToArray(); int columnCount = SchemaTable.Rows.Count; _accessors = new Func <object, object> [columnCount]; _memberTypes = new Type[columnCount]; for (int i = 0; i < columnCount; i++) { var columnName = SchemaTable.Rows[i]["ColumnName"].ToString(); var mapping = mappings.FirstOrDefault(m => String.Compare(m.ColumnName, columnName, StringComparison.OrdinalIgnoreCase) == 0); if (mapping == null) { continue; } ClassPropInfo propInfo = mapping.ClassPropInfo; // create a new anonymous method that takes an object and returns the value var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true); var il = dm.GetILGenerator(); // convert the object reference to the desired type il.Emit(OpCodes.Ldarg_0); if (type.IsValueType) { // access the field/property of a value type var valueHolder = il.DeclareLocal(type); il.Emit(OpCodes.Unbox_Any, type); il.Emit(OpCodes.Stloc, valueHolder); il.Emit(OpCodes.Ldloca_S, valueHolder); } else { il.Emit(OpCodes.Isinst, type); // cast object -> type } // get the value from the object propInfo.EmitGetValue(il); // if the type is nullable, handle nulls Type sourceType = propInfo.MemberType; Type targetType = (Type)SchemaTable.Rows[i]["DataType"]; Type underlyingType = Nullable.GetUnderlyingType(sourceType); if (underlyingType != null) { // check for not null Label notNullLabel = il.DefineLabel(); var nullableHolder = il.DeclareLocal(propInfo.MemberType); il.Emit(OpCodes.Stloc, nullableHolder); il.Emit(OpCodes.Ldloca_S, nullableHolder); il.Emit(OpCodes.Call, sourceType.GetProperty("HasValue").GetGetMethod()); il.Emit(OpCodes.Brtrue_S, notNullLabel); // it's null, just return null il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); il.MarkLabel(notNullLabel); // it's not null, so unbox to the underlyingtype il.Emit(OpCodes.Ldloca, nullableHolder); il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod()); // at this point we have de-nulled value, so use those converters sourceType = underlyingType; } if (sourceType != targetType && !sourceType.IsValueType && sourceType != typeof(string)) { // if the provider type is Xml, then serialize the value var serializerMethod = mapping.Serializer.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(Type) }, null); if (serializerMethod == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Serializer type {0} needs the method 'public static string Serialize(object, Type)'", mapping.Serializer.Name)); } il.EmitLoadType(sourceType); il.Emit(OpCodes.Call, serializerMethod); } else { // attempt to convert the value // either way, we are putting it in an object variable, so box it if (TypeConverterGenerator.EmitConversionOrCoersion(il, sourceType, targetType)) { il.Emit(OpCodes.Box, targetType); } else { il.Emit(OpCodes.Box, sourceType); } } il.Emit(OpCodes.Ret); _memberTypes[i] = propInfo.MemberType; _accessors[i] = (Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>)); } } else { // we are working off a single-column atomic type _memberTypes = new Type[1] { type }; _accessors = new Func <object, object>[] { o => o }; } }
/// <inheritdoc/> public override void BulkCopy(IDbConnection connection, string tableName, IDataReader reader, Action <InsightBulkCopy> configure, InsightBulkCopyOptions options, IDbTransaction transaction) { connection = GetInnerConnection(connection); transaction = ((GlimpseDbTransaction)transaction).InnerTransaction; InsightDbProvider.For(connection).BulkCopy(connection, tableName, reader, configure, options, transaction); }