private void RegisterCommandHandlersForCommandsWithNoAssociatedHandler(FunctionHostBuilder builder, ICommandRegistry commandRegistry) { // IN PROGRESS: This looks from the loaded set of assemblies and looks for a command handler for each command associated with a function. // If the handler is not already registered in the command registry then this registers it. IRegistrationCatalogue registrationCatalogue = (IRegistrationCatalogue)commandRegistry; HashSet <Type> registeredCommandTypes = new HashSet <Type>(registrationCatalogue.GetRegisteredCommands()); Dictionary <Type, List <Type> > commandTypesToHandlerTypes = null; foreach (AbstractFunctionDefinition functionDefinition in builder.FunctionDefinitions) { if (registeredCommandTypes.Contains(functionDefinition.CommandType)) { continue; } if (commandTypesToHandlerTypes == null) { commandTypesToHandlerTypes = HarvestCommandHandlers(); } if (commandTypesToHandlerTypes.TryGetValue(functionDefinition.CommandType, out List <Type> handlerTypes)) { foreach (Type handlerType in handlerTypes) { commandRegistry.Register(handlerType); } } } }
public async Task ExecuteAsync(ScheduledMessage scheduledMessage) { Type wrappedCommandType = _registry.GetRegisteredCommands() .FirstOrDefault(x => x.FullName == scheduledMessage.WrappedCommandFullTypeName); if (wrappedCommandType == null) { throw new ConfigurationException($"Command type {scheduledMessage.WrappedCommandFullTypeName} has not been registered"); } ICommand <bool> wrappedCommand = (ICommand <bool>)JsonConvert.DeserializeObject(scheduledMessage.WrappedCommandJson, wrappedCommandType); bool shouldReschedule = await _dispatcher.DispatchAsync(wrappedCommand); if (shouldReschedule) { // this should be the very last thing we do - otherwise an exception may cause a double queue of // the rescheduled message await _commandRescheduler.Reschedule(scheduledMessage); } // there is a chance that between here and this message being removed from the queue // that an exception occurs meaning the message will be popped and rescheduled again // leading to duplication // to protect against this the message ID is set on the service bus message and Service Bus // Deduplication should be turned on, consider the message ID time window based on the // recurrence interval - do not set the deuplication window to be larger than the interval // otherwise legitimate reschedules will be detected as duplicates by SB. }
private void RegisterCommandHandlersForCommandsWithNoAssociatedHandler(FunctionHostBuilder builder, ICommandRegistry commandRegistry) { // TODO: We can improve this so that auto-registration is decoupled and can be provided by a mediator package if (builder.MediatorType != typeof(DefaultMediatorDecorator)) { return; } // IN PROGRESS: This looks from the loaded set of assemblies and looks for a command handler for each command associated with a function. // If the handler is not already registered in the command registry then this registers it. IRegistrationCatalogue registrationCatalogue = (IRegistrationCatalogue)commandRegistry; HashSet <Type> registeredCommandTypes = new HashSet <Type>(registrationCatalogue.GetRegisteredCommands()); Dictionary <Type, List <Type> > commandTypesToHandlerTypes = null; foreach (AbstractFunctionDefinition functionDefinition in builder.FunctionDefinitions) { if (registeredCommandTypes.Contains(functionDefinition.CommandType)) { continue; } if (commandTypesToHandlerTypes == null) { commandTypesToHandlerTypes = HarvestCommandHandlers(); } if (commandTypesToHandlerTypes.TryGetValue(functionDefinition.CommandType, out List <Type> handlerTypes)) { foreach (Type handlerType in handlerTypes) { commandRegistry.Register(handlerType); } } } }
private IReadOnlyCollection <string> GetExternalAssemblyLocations( IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions) { HashSet <Assembly> assemblies = new HashSet <Assembly>(); foreach (AbstractFunctionDefinition functionDefinition in functionDefinitions) { assemblies.Add(_triggerReferenceProvider.GetTriggerReference(functionDefinition)); assemblies.Add(functionDefinition.CommandType.Assembly); if (functionDefinition.CommandResultType != null) { // skip system types if (functionDefinition.CommandResultType.Assembly != typeof(string).Assembly) { assemblies.Add(functionDefinition.CommandResultType.Assembly); } } } // TODO: Do we need this any more? We no longer run the startup code in the compilation process? foreach (ServiceDescriptor descriptor in _serviceCollection) { assemblies.Add(descriptor.ServiceType.Assembly); if (descriptor.ImplementationType != null) { assemblies.Add(descriptor.ImplementationType.Assembly); } if (descriptor.ImplementationInstance != null) { assemblies.Add(descriptor.ImplementationInstance.GetType().Assembly); } } IRegistrationCatalogue catalogue = (IRegistrationCatalogue)_commandRegistry; foreach (Type handler in catalogue.GetRegisteredHandlers()) { assemblies.Add(handler.Assembly); } foreach (Type command in catalogue.GetRegisteredCommands()) { assemblies.Add(command.Assembly); } assemblies.Add(_configurationSourceAssembly); // we have to add directly referenced assemblies in case the commands and result types make use of external types // TODO: their is an argument to restricting this foreach (Assembly assembly in assemblies.ToArray()) { AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies(); foreach (var referencedAssemblyName in referencedAssemblies) { if (referencedAssemblyName.Name == "netstandard" || referencedAssemblyName.Name == "System.Runtime") { continue; } var referencedAssembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(x => x.FullName == referencedAssemblyName.FullName); if (referencedAssembly != null) { assemblies.Add(referencedAssembly); } } } // at the moment we can't get the actual dispatcher types without actually calling the function and looking at ther result - needs thought return(assemblies.Select(x => x.Location).ToArray()); }