Refactoring feature manager. Added method for fetching dependent features. Removed recursion when traversing dependency tree and added circular dependency safeguard.

This commit is contained in:
Piotr Szmyd
2014-02-10 21:53:56 +01:00
parent 755e55062a
commit 185642d44b
2 changed files with 48 additions and 16 deletions

View File

@@ -107,12 +107,8 @@ namespace Orchard.Environment.Features {
ShellDescriptor shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); ShellDescriptor shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
List<ShellFeature> enabledFeatures = shellDescriptor.Features.ToList(); List<ShellFeature> enabledFeatures = shellDescriptor.Features.ToList();
IDictionary<FeatureDescriptor, bool> availableFeatures = GetAvailableFeatures()
.ToDictionary(featureDescriptor => featureDescriptor,
featureDescriptor => enabledFeatures.FirstOrDefault(shellFeature => shellFeature.Name.Equals(featureDescriptor.Id)) != null);
IEnumerable<string> featuresToDisable = featureIds IEnumerable<string> featuresToDisable = featureIds
.Select(featureId => DisableFeature(featureId, availableFeatures, force)).ToList() .Select(featureId => DisableFeature(featureId, force)).ToList()
.SelectMany(ies => ies.Select(s => s)); .SelectMany(ies => ies.Select(s => s));
if (featuresToDisable.Any()) { if (featuresToDisable.Any()) {
@@ -130,6 +126,30 @@ namespace Orchard.Environment.Features {
return featuresToDisable; return featuresToDisable;
} }
/// <summary>
/// Lists all enabled features that depend on a given feature.
/// </summary>
/// <param name="featureId">ID of the feature to check.</param>
/// <returns>An enumeration with dependent feature IDs.</returns>
public IEnumerable<string> GetDependentFeatures(string featureId) {
var getEnabledDependants =
new Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>>(
(currentFeatureId, fs) => fs
.Where(f => f.Value && f.Key.Dependencies != null && f.Key.Dependencies
.Select(s => s.ToLowerInvariant())
.Contains(currentFeatureId.ToLowerInvariant()))
.ToDictionary(f => f.Key, f => f.Value));
ShellDescriptor shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
List<ShellFeature> enabledFeatures = shellDescriptor.Features.ToList();
IDictionary<FeatureDescriptor, bool> availableFeatures = GetAvailableFeatures()
.ToDictionary(featureDescriptor => featureDescriptor,
featureDescriptor => enabledFeatures.FirstOrDefault(shellFeature => shellFeature.Name.Equals(featureDescriptor.Id)) != null);
return GetAffectedFeatures(featureId, availableFeatures, getEnabledDependants);
}
/// <summary> /// <summary>
/// Enables a feature. /// Enables a feature.
/// </summary> /// </summary>
@@ -179,16 +199,11 @@ namespace Orchard.Environment.Features {
/// Disables a feature. /// Disables a feature.
/// </summary> /// </summary>
/// <param name="featureId">The ID of the feature to be enabled.</param> /// <param name="featureId">The ID of the feature to be enabled.</param>
/// <param name="availableFeatures"></param>
/// <param name="force">Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise.</param> /// <param name="force">Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise.</param>
/// <returns>An enumeration of the disabled features.</returns> /// <returns>An enumeration of the disabled features.</returns>
private IEnumerable<string> DisableFeature(string featureId, IDictionary<FeatureDescriptor, bool> availableFeatures, bool force) { private IEnumerable<string> DisableFeature(string featureId, bool force) {
var getEnabledDependants = IEnumerable<string> featuresToDisable = GetDependentFeatures(featureId);
new Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>>(
(currentFeatureId, fs) => fs.Where(f => f.Value && f.Key.Dependencies != null && f.Key.Dependencies.Select(s => s.ToLowerInvariant()).Contains(currentFeatureId.ToLowerInvariant()))
.ToDictionary(f => f.Key, f => f.Value));
IEnumerable<string> featuresToDisable = GetAffectedFeatures(featureId, availableFeatures, getEnabledDependants);
if (featuresToDisable.Count() > 1 && !force) { if (featuresToDisable.Count() > 1 && !force) {
Logger.Warning("Additional features need to be disabled."); Logger.Warning("Additional features need to be disabled.");
if (FeatureDependencyNotification != null) { if (FeatureDependencyNotification != null) {
@@ -201,11 +216,21 @@ namespace Orchard.Environment.Features {
return featuresToDisable; return featuresToDisable;
} }
private static IEnumerable<string> GetAffectedFeatures(string featureId, IDictionary<FeatureDescriptor, bool> features, Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) { private static IEnumerable<string> GetAffectedFeatures(
var dependencies = new List<string> { featureId }; string featureId, IDictionary<FeatureDescriptor, bool> features,
Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) {
foreach (KeyValuePair<FeatureDescriptor, bool> dependency in getAffectedDependencies(featureId, features)) { var dependencies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { featureId };
dependencies.AddRange(GetAffectedFeatures(dependency.Key.Id, features, getAffectedDependencies)); var stack = new Stack<IDictionary<FeatureDescriptor, bool>>();
stack.Push(getAffectedDependencies(featureId, features));
while (stack.Any()) {
var next = stack.Pop();
foreach (var dependency in next.Where(dependency => !dependencies.Contains(dependency.Key.Id))) {
dependencies.Add(dependency.Key.Id);
stack.Push(getAffectedDependencies(dependency.Key.Id, features));
}
} }
return dependencies; return dependencies;

View File

@@ -48,5 +48,12 @@ namespace Orchard.Environment.Features {
/// <param name="force">Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise.</param> /// <param name="force">Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise.</param>
/// <returns>An enumeration with the disabled feature IDs.</returns> /// <returns>An enumeration with the disabled feature IDs.</returns>
IEnumerable<string> DisableFeatures(IEnumerable<string> featureIds, bool force); IEnumerable<string> DisableFeatures(IEnumerable<string> featureIds, bool force);
/// <summary>
/// Lists all enabled features that depend on a given feature.
/// </summary>
/// <param name="featureId">ID of the feature to check.</param>
/// <returns>An enumeration with dependent feature IDs.</returns>
IEnumerable<string> GetDependentFeatures(string featureId);
} }
} }