从上述代码中我们可以了解到查找并执行ConfigureServices方法的具体步骤可分为三步,首先在startupType类型中根据环境变量名称查找具体方法返回ConfigureServicesBuilder实例,然后构建ConfigureServicesBuilder实例返回ConfigureServices方法的委托,最后传递IServiceCollection对象执行委托方法。接下来我们就来查看具体实现源码。
我们在StartupLoader类中找到了FindConfigureServicesDelegate方法的相关实现[点击查看源码👈]
internal static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName) { //根据startupType和根据environmentName构建的Configure{0}Services字符串先去查找返回类型为IServiceProvider的方法 //找不到在查找返回值为void类型的方法 var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false) ?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false); //根据查找的到的MethodInfo去构建ConfigureServicesBuilder实例 return new ConfigureServicesBuilder(servicesMethod); }
通过这里的源码我们可以看到在startupType类型里去查找名字为environmentName构建的Configure{0}Services的方法信息,然后根据查找的方法信息即MethodInfo对象去构建ConfigureServicesBuilder实例。接下里我们就来查询FindMethod方法的实现
private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true) { //包含环境变量的ConfigureServices方法名称比如(ConfigureDevelopmentServices) var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName); //名为ConfigureServices的方法 var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, ""); //方法是共有的静态的或非静态的方法 var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); //查找包含环境变量的ConfigureServices方法名称 var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList(); if (selectedMethods.Count > 1) { //找打多个满足规则的方法直接抛出异常 throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv)); } //如果不存在包含环境变量的ConfigureServices的方法比如(ConfigureDevelopmentServices),则直接查找方法名为ConfigureServices的方法 if (selectedMethods.Count == 0) { selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList(); //如果存在多个则同样抛出异常 if (selectedMethods.Count > 1) { throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv)); } } var methodInfo = selectedMethods.FirstOrDefault(); //如果没找到满足规则的方法,并且满足required参数,则抛出未找到方法的异常 if (methodInfo == null) { if (required) { throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.", methodNameWithEnv, methodNameWithNoEnv, startupType.FullName)); } return null; } //如果找到了名称一致的方法,但是返回类型和预期的不一致,也抛出异常 if (returnType != null && methodInfo.ReturnType != returnType) { if (required) { throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.", methodInfo.Name, startupType.FullName, returnType.Name)); } return null; } return methodInfo; }
通过FindMethod方法我们可以得到几个结论,首先ConfigureServices方法的名称可以是包含环境变量的名称比如(ConfigureDevelopmentServices),其次方法可以为共有的静态或非静态方法。FindMethod方法是真正执行查找的逻辑所在,如果找到相关方法则返回MethodInfo。FindMethod查找的方法名称是通过methodName参数传递进来的,我们标注的注释代码都是直接写死了ConfigureServices方法,只是为了便于说明理解,但其实FindMethod是通用方法,接下来我们要讲解的内容还会涉及到这个方法,到时候关于这个代码的逻辑我们就不会在进行说明了,因为是同一个方法,希望大家能注意到这一点。
通过上面的相关方法,我们了解到了是通过什么样的规则去查找到ConfigureServices的方法信息的,我们也看到了ConfigureServicesBuilder正是通过查找到的MethodInfo去构造实例的,接下来我们就来查看下ConfigureServicesBuilder的实现源码[点击查看源码👈]