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();
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
.Select(featureId => DisableFeature(featureId, availableFeatures, force)).ToList()
.Select(featureId => DisableFeature(featureId, force)).ToList()
.SelectMany(ies => ies.Select(s => s));
if (featuresToDisable.Any()) {
@@ -130,6 +126,30 @@ namespace Orchard.Environment.Features {
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>
/// Enables a feature.
/// </summary>
@@ -179,16 +199,11 @@ namespace Orchard.Environment.Features {
/// Disables a feature.
/// </summary>
/// <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>
/// <returns>An enumeration of the disabled features.</returns>
private IEnumerable<string> DisableFeature(string featureId, IDictionary<FeatureDescriptor, bool> availableFeatures, bool force) {
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));
private IEnumerable<string> DisableFeature(string featureId, bool force) {
IEnumerable<string> featuresToDisable = GetDependentFeatures(featureId);
IEnumerable<string> featuresToDisable = GetAffectedFeatures(featureId, availableFeatures, getEnabledDependants);
if (featuresToDisable.Count() > 1 && !force) {
Logger.Warning("Additional features need to be disabled.");
if (FeatureDependencyNotification != null) {
@@ -201,11 +216,21 @@ namespace Orchard.Environment.Features {
return featuresToDisable;
}
private static IEnumerable<string> GetAffectedFeatures(string featureId, IDictionary<FeatureDescriptor, bool> features, Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) {
var dependencies = new List<string> { featureId };
private static IEnumerable<string> GetAffectedFeatures(
string featureId, IDictionary<FeatureDescriptor, bool> features,
Func<string, IDictionary<FeatureDescriptor, bool>, IDictionary<FeatureDescriptor, bool>> getAffectedDependencies) {
foreach (KeyValuePair<FeatureDescriptor, bool> dependency in getAffectedDependencies(featureId, features)) {
dependencies.AddRange(GetAffectedFeatures(dependency.Key.Id, features, getAffectedDependencies));
var dependencies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { featureId };
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;

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>
/// <returns>An enumeration with the disabled feature IDs.</returns>
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);
}
}