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)); }
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()); }
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); }
/// <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);