private static object CreateInstance(Type type, string retryMethodName, SqlRetryLogicOption option)
        {
            string methodName = MethodBase.GetCurrentMethod().Name;

            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Entry point.", TypeName, methodName);

            if (type == typeof(SqlConfigurableRetryFactory) || type == null)
            {
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> The given type `{2}` infers as internal `{3}` type."
                                                       , TypeName, methodName, type?.Name, typeof(SqlConfigurableRetryFactory).FullName);
                MethodInfo internalMethod = typeof(SqlConfigurableRetryFactory).GetMethod(retryMethodName);
                if (internalMethod == null)
                {
                    throw new InvalidOperationException($"Failed to resolve the '{retryMethodName}' method from `{typeof(SqlConfigurableRetryFactory).FullName}` type.");
                }

                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> The `{2}.{3}()` method has been discovered as the `{4}` method name."
                                                       , TypeName, methodName, internalMethod.ReflectedType.FullName, internalMethod.Name, retryMethodName);
                object[] internalFuncParams = PrepareParamValues(internalMethod.GetParameters(), option, retryMethodName);
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Parameters are prepared to invoke the `{2}.{3}()` method."
                                                       , TypeName, methodName, internalMethod.ReflectedType.FullName, internalMethod.Name);
                return(internalMethod.Invoke(null, internalFuncParams));
            }

            // Searches for the public MethodInfo from the specified type by the given method name
            // The search is case-sensitive
            MethodInfo method = type.GetMethod(retryMethodName);

            if (method == null)
            {
                throw new InvalidOperationException($"Failed to resolve the '{retryMethodName}' method from `{type.FullName}` type.");
            }
            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> The `{2}` method metadata has been extracted from the `{3}` type by using the `{4}` method name."
                                                   , TypeName, methodName, method.Name, type.FullName, retryMethodName);

            if (!typeof(SqlRetryLogicBaseProvider).IsAssignableFrom(method.ReturnType))
            {
                throw new InvalidCastException($"Invalid return type; Return type must be of `{typeof(SqlRetryLogicBaseProvider).FullName}` type.");
            }
            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> The return type of the `{2}.{3}()` method is valid."
                                                   , TypeName, methodName, type.FullName, method.Name);

            // Prepare the function parameters values
            object[] funcParams = PrepareParamValues(method.GetParameters(), option, retryMethodName);

            if (method.IsStatic)
            {
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Run the static `{2}` method without object creation of `{3}` type.",
                                                       TypeName, methodName, method.Name, type.FullName);
                return(method.Invoke(null, funcParams));
            }

            // Since there is no information about the parameters of the possible constructors,
            // the only acceptable constructor is the default constructor.
            object obj = Activator.CreateInstance(type);

            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> An instance of `{2}` type is created to invoke the `{3}` method.",
                                                   TypeName, methodName, type.FullName, method.Name);
            return(method.Invoke(obj, funcParams));
        }
Example #2
0
        private static SqlRetryLogicBaseProvider CreateRetryLogicProvider(string sectionName, ISqlConfigurableRetryConnectionSection configSection)
        {
            string methodName = MethodBase.GetCurrentMethod().Name;

            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Entry point.", TypeName, methodName);

            try
            {
                if (configSection == null)
                {
                    throw SqlReliabilityUtil.ArgumentNull(nameof(configSection));
                }

                // Create a SqlRetryLogicOption object from the discovered retry logic values
                var retryOption = new SqlRetryLogicOption()
                {
                    NumberOfTries   = configSection.NumberOfTries,
                    DeltaTime       = configSection.DeltaTime,
                    MinTimeInterval = configSection.MinTimeInterval,
                    MaxTimeInterval = configSection.MaxTimeInterval
                };

                // Prepare the transient error lists
                if (!string.IsNullOrEmpty(configSection.TransientErrors))
                {
                    retryOption.TransientErrors = configSection.TransientErrors.Split(',').Select(x => Convert.ToInt32(x)).ToList();
                }

                // Prepare the authorized SQL statement just for SqlCommands
                if (configSection is ISqlConfigurableRetryCommandSection cmdSection && !string.IsNullOrEmpty(cmdSection.AuthorizedSqlCondition))
                {
                    retryOption.AuthorizedSqlCondition = (x) => Regex.IsMatch(x, cmdSection.AuthorizedSqlCondition);
                }
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Successfully created a {2} object to use on creating a retry logic provider from the section '{3}'.",
                                                       TypeName, methodName, nameof(SqlRetryLogicOption), sectionName);

                // Extract the SqlRetryLogicBaseProvider object from the given information
                var provider = ResolveRetryLogicProvider(configSection.RetryLogicType, configSection.RetryMethod, retryOption);
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Successfully created a {2} object from the section '{3}'.",
                                                       TypeName, methodName, nameof(SqlRetryLogicBaseProvider), sectionName);
                return(provider);
            }
            catch (Exception e)
            {
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> {2}",
                                                       TypeName, methodName, e);
            }
            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Due to an exception, the default non-retriable logic will be applied.",
                                                   TypeName, methodName);
            // Return default provider if an exception occured.
            return(SqlConfigurableRetryFactory.CreateNoneRetryProvider());
        }
Example #3
0
        private static SqlRetryLogicBaseProvider InternalCreateRetryProvider(SqlRetryLogicOption retryLogicOption, SqlRetryIntervalBaseEnumerator enumerator)
        {
            if (retryLogicOption == null)
            {
                throw new ArgumentNullException(nameof(retryLogicOption));
            }
            Debug.Assert(enumerator != null, $"The '{nameof(enumerator)}' mustn't be null.");

            var retryLogic = new SqlRetryLogic(retryLogicOption.NumberOfTries, enumerator,
                                               (e) => TransientErrorsCondition(e, retryLogicOption.TransientErrors ?? s_defaultTransientErrors),
                                               retryLogicOption.AuthorizedSqlCondition);

            return(new SqlRetryLogicProvider(retryLogic));
        }
        private static object[] PrepareParamValues(ParameterInfo[] parameterInfos, SqlRetryLogicOption option, string retryMethod)
        {
            // The retry method must have at least one `SqlRetryLogicOption`
            if (parameterInfos.FirstOrDefault(x => x.ParameterType == typeof(SqlRetryLogicOption)) == null)
            {
                string message = $"Failed to create {nameof(SqlRetryLogicBaseProvider)} object because of invalid {retryMethod}'s parameters." +
                                 $"{Environment.NewLine}The function must have a paramter of type '{nameof(SqlRetryLogicOption)}'.";
                throw new InvalidOperationException(message);
            }

            object[] funcParams = new object[parameterInfos.Length];
            for (int i = 0; i < parameterInfos.Length; i++)
            {
                ParameterInfo paramInfo = parameterInfos[i];

                // Create parameters with default values that are not a SqlRetryLogicOption type.
                if (paramInfo.HasDefaultValue && paramInfo.ParameterType != typeof(SqlRetryLogicOption))
                {
                    funcParams[i] = paramInfo.DefaultValue;
                }

                // Assign the 'option' object to the first parameter with 'SqlRetryLogicOption' type
                // neither it doesn't have default value
                // or there isn't another parameter with the same type and without a default value.
                else if (paramInfo.ParameterType == typeof(SqlRetryLogicOption))
                {
                    if (!paramInfo.HasDefaultValue ||
                        (paramInfo.HasDefaultValue &&
                         parameterInfos.FirstOrDefault(x => x != paramInfo && !x.HasDefaultValue && x.ParameterType == typeof(SqlRetryLogicOption)) == null))
                    {
                        funcParams[i] = option;
                    }
                    else
                    {
                        funcParams[i] = paramInfo.DefaultValue;
                    }
                }
                else
                {
                    string message = $"Failed to create {nameof(SqlRetryLogicBaseProvider)} object because of invalid {nameof(retryMethod)}'s parameters."
                                     + $"{Environment.NewLine}Parameter '{paramInfo.ParameterType.Name} {paramInfo.Name}' doesn't have default value.";
                    throw new InvalidOperationException(message);
                }
            }
            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Parameters are prepared to invoke the `{2}.{3}()` method."
                                                   , TypeName, MethodBase.GetCurrentMethod().Name, typeof(SqlConfigurableRetryFactory).FullName, retryMethod);
            return(funcParams);
        }
        private static SqlRetryLogicBaseProvider ResolveRetryLogicProvider(string configurableRetryType, string retryMethod, SqlRetryLogicOption option)
        {
            string methodName = MethodBase.GetCurrentMethod().Name;

            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Entry point.", TypeName, methodName);

            if (string.IsNullOrEmpty(retryMethod))
            {
                throw new ArgumentNullException($"Failed to create {nameof(SqlRetryLogicBaseProvider)} object because the {nameof(retryMethod)} value is null or empty.");
            }

            Type type = null;

            try
            {
                // Resolve a Type object from the given type name
                // Different implementation in .NET Framework & .NET Core
                type = LoadType(configurableRetryType);
            }
            catch (Exception e)
            {
                // Try to use 'SqlConfigurableRetryFactory' as a default type to discover retry methods
                // if there is a problem, resolve using the 'configurableRetryType' type.
                type = typeof(SqlConfigurableRetryFactory);
                SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Unable to load the '{2}' type; Trying to use the internal `{3}` type: {4}",
                                                       TypeName, methodName, configurableRetryType, type.FullName, e);
            }

            // Run the function by using the resolved values to get the SqlRetryLogicBaseProvider object
            try
            {
                // Create an instance from the discovered type by its default constructor
                object result = CreateInstance(type, retryMethod, option);

                if (result is SqlRetryLogicBaseProvider provider)
                {
                    SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> The created instace is a {2} type.",
                                                           TypeName, methodName, typeof(SqlRetryLogicBaseProvider).FullName);
                    provider.Retrying += OnRetryingEvent;
                    return(provider);
                }
            }
            catch (Exception e)
            {
                // In order to invoke a function dynamically, any type of exception can occur here;
                // The main exception and its stack trace will be accessible through the inner exception.
                // i.e: Opening a connection or executing a command while invoking a function
                // runs the application to the `TargetInvocationException`.
                // And using an isolated zone like a specific AppDomain results in an infinite loop.
                throw new Exception($"Exception occurred when running the `{type.FullName}.{retryMethod}()` method.", e);
            }

            SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Unable to resolve a valid provider; Returns `null`.", TypeName, methodName);
            return(null);
        }
Example #6
0
 /// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml' path='docs/members[@name="SqlConfigurableRetryFactory"]/CreateFixedRetryProvider/*' />
 public static SqlRetryLogicBaseProvider CreateFixedRetryProvider(SqlRetryLogicOption retryLogicOption)
 => InternalCreateRetryProvider(retryLogicOption,
                                retryLogicOption != null ? new SqlFixedIntervalEnumerator(retryLogicOption.DeltaTime, retryLogicOption.MaxTimeInterval, retryLogicOption.MinTimeInterval) : null);