private UpdateHandlerActions GetMetadataUpdateHandlerActions() { // We need to execute MetadataUpdateHandlers in a well-defined order. For v1, the strategy that is used is to topologically // sort assemblies so that handlers in a dependency are executed before the dependent (e.g. the reflection cache action // in System.Private.CoreLib is executed before System.Text.Json clears it's own cache.) // This would ensure that caches and updates more lower in the application stack are up to date // before ones higher in the stack are recomputed. var sortedAssemblies = TopologicalSort(AppDomain.CurrentDomain.GetAssemblies()); var handlerActions = new UpdateHandlerActions(); foreach (var assembly in sortedAssemblies) { foreach (var attr in assembly.GetCustomAttributesData()) { // Look up the attribute by name rather than by type. This would allow netstandard targeting libraries to // define their own copy without having to cross-compile. if (attr.AttributeType.FullName != "System.Reflection.Metadata.MetadataUpdateHandlerAttribute") { continue; } IList <CustomAttributeTypedArgument> ctorArgs = attr.ConstructorArguments; if (ctorArgs.Count != 1 || !(ctorArgs[0].Value is Type handlerType)) { _log($"'{attr}' found with invalid arguments."); continue; } GetHandlerActions(handlerActions, handlerType); } } return(handlerActions); }
internal void GetHandlerActions(UpdateHandlerActions handlerActions, Type handlerType) { bool methodFound = false; if (GetUpdateMethod(handlerType, "ClearCache") is MethodInfo clearCache) { handlerActions.ClearCache.Add(CreateAction(clearCache)); methodFound = true; } if (GetUpdateMethod(handlerType, "UpdateApplication") is MethodInfo updateApplication) { handlerActions.UpdateApplication.Add(CreateAction(updateApplication)); methodFound = true; } if (!methodFound) { _log($"No invokable methods found on metadata handler type '{handlerType}'. " + $"Allowed methods are ClearCache, UpdateApplication"); } Action <Type[]?> CreateAction(MethodInfo update) { var action = (Action <Type[]?>)update.CreateDelegate(typeof(Action <Type[]?>)); return(types => { try { action(types); } catch (Exception ex) { _log($"Exception from '{action}': {ex}"); } }); } MethodInfo?GetUpdateMethod(Type handlerType, string name) { if (handlerType.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) is MethodInfo updateMethod && updateMethod.ReturnType == typeof(void)) { return(updateMethod); } foreach (MethodInfo method in handlerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) { if (method.Name == name) { _log($"Type '{handlerType}' has method '{method}' that does not match the required signature."); break; } } return(null); } }