예제 #1
0
        /// <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);
                }
            });
        }
예제 #3
0
        /// <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));
        }
예제 #4
0
        /// <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));
        }
예제 #9
0
        /// <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));
        }
예제 #10
0
        /// <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));
            }
        }
예제 #11
0
        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();
        }
예제 #12
0
        /// <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));
            }
        }
예제 #13
0
        /// <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());
 }
예제 #17
0
        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());
 }
예제 #21
0
 /// <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());
예제 #23
0
        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);
 }