Skip to content

Instantly share code, notes, and snippets.

@xiaomi7732
Last active September 24, 2022 19:02
Show Gist options
  • Save xiaomi7732/585c2a7b8cdfb419ae46c2e62d2eccfc to your computer and use it in GitHub Desktop.
Save xiaomi7732/585c2a7b8cdfb419ae46c2e62d2eccfc to your computer and use it in GitHub Desktop.
What happens when you call ConfigureOptions<T> on IServiceCollection

What happens when you call ConfigureOptions<T> on IServiceCollection

It will findout all the interfaces on the target type, and if they are one of these interfaces:

  • IConfigureOptions<>
  • IPostConfigureOptions<>
  • IValidateOptions<>

It will register it by calling:

services.AddTransient(item, configureType);

In which, item is the matched interface, configureType is the type of the options.

For example, if ConfigureMyOptions implements IConfigureOptions<MyOptions> and IPostConfigureOptions<MyOptinos>, calling:

services.ConfigureOptions<ConfigureMyOptions>();

That is equivalent to:

serivces.AddTransient<IConfigure<MyOptions>, ConfigureMyOptions>();
services.AddTransient<IPostConfigureOptions<MyOptions>, ConfigureMyOptions>();

How is it implemented?

By decompile, here's the code of ConfgiureOptions:

public static IServiceCollection ConfigureOptions(this IServiceCollection services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type configureType)
{
	services.AddOptions();
	bool flag = false;
	foreach (Type item in FindConfigurationServices(configureType))
	{
		services.AddTransient(item, configureType);
		flag = true;
	}
	if (!flag)
	{
		ThrowNoConfigServices(configureType);
	}
	return services;
}

Notice how are the interface discovered by reflection:

private static IEnumerable<Type> FindConfigurationServices(Type type)
{
	Type[] array = GetInterfacesOnType(type);
	foreach (Type type2 in array)
	{
		if (type2.IsGenericType)
		{
			Type genericTypeDefinition = type2.GetGenericTypeDefinition();
			if (genericTypeDefinition == typeof(IConfigureOptions<>) || genericTypeDefinition == typeof(IPostConfigureOptions<>) || genericTypeDefinition == typeof(IValidateOptions<>))
			{
				yield return type2;
			}
		}
	}
	[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "This method only looks for interfaces referenced in its code. The trimmer will keep the interface and thus all of its implementations in that case. The call to GetInterfaces may return less results in trimmed apps, but it will include the interfaces this method looks for if they should be there.")]
	static Type[] GetInterfacesOnType(Type t)
	{
		return t.GetInterfaces();
	}
}

And that's how the magic got pulled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment