async Task <ConcurrentDictionary <string, IFeatureEventContext> > InvokeFeatureEventsAsync( IList <IShellFeature> features, Func <IFeatureEventContext, IFeatureEventHandler, Task <ConcurrentDictionary <string, IFeatureEventContext> > > handler, Func <IFeatureEventContext, Task <ConcurrentDictionary <string, IFeatureEventContext> > > noHandler) { // Holds the results of all our event execution contexts var contexts = new ConcurrentDictionary <string, IFeatureEventContext>(); // Build a list of all unique features we are enabling / disabling var uniqueFeatures = new ConcurrentDictionary <string, IShellFeature>(); foreach (var feature in features) { // The feature may also reference dependencies so ensure we also // add any dependencies for the features to our temporary shell descriptors if (feature.FeatureDependencies.Any()) { foreach (var dependency in feature.FeatureDependencies) { if (!uniqueFeatures.ContainsKey(dependency.ModuleId)) { uniqueFeatures.TryAdd(dependency.ModuleId, dependency); } } } if (!uniqueFeatures.ContainsKey(feature.ModuleId)) { uniqueFeatures.TryAdd(feature.ModuleId, feature); } } // Ensure minimum features are always available within the temporary shell descriptor // We may depend upon services from the required features within the features we are enabling / disabling var minimumShellDescriptor = _shellContextFactory.MinimumShellDescriptor(); // Add features and dependencies we are enabling / disabling to our minimum shell descriptor foreach (var feature in uniqueFeatures.Values) { minimumShellDescriptor.Modules.Add(new ShellModule(feature.ModuleId, feature.Version)); } // Create a new shell context with features and all dependencies we need to enable / disable using (var shellContext = _shellContextFactory.CreateDescribedContext(_shellSettings, minimumShellDescriptor)) { using (var scope = shellContext.ServiceProvider.CreateScope()) { var handlers = scope.ServiceProvider.GetServices <IFeatureEventHandler>(); var handlersList = handlers.ToList(); // Iterate through each feature we wish to invoke // Use the event handlers if available else just add to contexts collection foreach (var feature in features) { // Only invoke non required features if (feature.IsRequired) { continue; } // Context that will be passed around var context = new FeatureEventContext() { Feature = feature, ServiceProvider = scope.ServiceProvider, Logger = _logger }; // Get event handler for feature we are invoking var featureHandler = handlersList.FirstOrDefault(h => h.ModuleId == feature.ModuleId); // Get response from responsible func var handlerContexts = featureHandler != null ? await handler(context, featureHandler) : await noHandler(context); // Compile results from delegates if (handlerContexts != null) { foreach (var handlerContext in handlerContexts) { contexts.AddOrUpdate(feature.ModuleId, handlerContext.Value, (k, v) => { foreach (var error in handlerContext.Value.Errors) { v.Errors.Add(error.Key, error.Value); } return(v); }); } } // Log any errors if (context.Errors.Count > 0) { foreach (var error in context.Errors) { _logger.LogCritical(error.Value, $"An error occurred whilst invoking within {this.GetType().FullName}"); } } } } } return(contexts); }
public async Task <IShellDescriptor> GetEnabledDescriptorAsync() { return(await _shellDescriptorStore.GetAsync() ?? _shellContextFactory.MinimumShellDescriptor()); }