diff --git a/ModularPipelines.Build/Modules/LocalMachine/UploadPackagesToLocalNuGetModule.cs b/ModularPipelines.Build/Modules/LocalMachine/UploadPackagesToLocalNuGetModule.cs index 83c990158b..eed834875a 100644 --- a/ModularPipelines.Build/Modules/LocalMachine/UploadPackagesToLocalNuGetModule.cs +++ b/ModularPipelines.Build/Modules/LocalMachine/UploadPackagesToLocalNuGetModule.cs @@ -18,7 +18,7 @@ protected override async Task OnBeforeExecute(IModuleContext context) foreach (var packagePath in packagePaths.Value!) { - context.Logger.LogInformation("Uploading {File}", packagePath); + context.Logger.LogInformation("[Local Directory] Uploading {File}", packagePath); } await base.OnBeforeExecute(context); diff --git a/ModularPipelines.Examples/Modules/DependentOn2.cs b/ModularPipelines.Examples/Modules/DependentOn2.cs new file mode 100644 index 0000000000..29e26881d3 --- /dev/null +++ b/ModularPipelines.Examples/Modules/DependentOn2.cs @@ -0,0 +1,16 @@ +using ModularPipelines.Attributes; +using ModularPipelines.Context; +using ModularPipelines.Models; +using ModularPipelines.Modules; + +namespace ModularPipelines.Examples.Modules; + +[DependsOn] +public class DependentOn2 : Module +{ + protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) + { + await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); + return null; + } +} \ No newline at end of file diff --git a/ModularPipelines.Examples/Modules/DependentOn3.cs b/ModularPipelines.Examples/Modules/DependentOn3.cs new file mode 100644 index 0000000000..f6748e4a9e --- /dev/null +++ b/ModularPipelines.Examples/Modules/DependentOn3.cs @@ -0,0 +1,16 @@ +using ModularPipelines.Attributes; +using ModularPipelines.Context; +using ModularPipelines.Models; +using ModularPipelines.Modules; + +namespace ModularPipelines.Examples.Modules; + +[DependsOn] +public class DependentOn3 : Module +{ + protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) + { + await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); + return null; + } +} \ No newline at end of file diff --git a/ModularPipelines.Examples/Modules/DependentOn4.cs b/ModularPipelines.Examples/Modules/DependentOn4.cs new file mode 100644 index 0000000000..7edadf6aed --- /dev/null +++ b/ModularPipelines.Examples/Modules/DependentOn4.cs @@ -0,0 +1,16 @@ +using ModularPipelines.Attributes; +using ModularPipelines.Context; +using ModularPipelines.Models; +using ModularPipelines.Modules; + +namespace ModularPipelines.Examples.Modules; + +[DependsOn] +public class DependentOn4 : Module +{ + protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) + { + await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); + return null; + } +} \ No newline at end of file diff --git a/ModularPipelines.Examples/Modules/DependentOnSuccessModule.cs b/ModularPipelines.Examples/Modules/DependentOnSuccessModule.cs index b0c5ebc677..0ba6c215cd 100644 --- a/ModularPipelines.Examples/Modules/DependentOnSuccessModule.cs +++ b/ModularPipelines.Examples/Modules/DependentOnSuccessModule.cs @@ -7,36 +7,6 @@ namespace ModularPipelines.Examples.Modules; [DependsOn] public class DependentOnSuccessModule : Module -{ - protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) - { - await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); - return null; - } -} - -[DependsOn] -public class DependentOn2 : Module -{ - protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) - { - await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); - return null; - } -} - -[DependsOn] -public class DependentOn3 : Module -{ - protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) - { - await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); - return null; - } -} - -[DependsOn] -public class DependentOn4 : Module { protected override async Task>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) { diff --git a/ModularPipelines.UnitTests/DirectCollisionTests.cs b/ModularPipelines.UnitTests/DirectCollisionTests.cs index 30c81bef65..0e4d037d35 100644 --- a/ModularPipelines.UnitTests/DirectCollisionTests.cs +++ b/ModularPipelines.UnitTests/DirectCollisionTests.cs @@ -21,7 +21,7 @@ public void Modules_Dependent_On_Each_Other_Throws_Exception() }) .ExecutePipelineAsync(), Throws.Exception.TypeOf() - .With.Message.EqualTo("Dependency collision detected: **ModularPipelines.UnitTests.DirectCollisionTests+DependencyConflictModule2** -> ModularPipelines.UnitTests.DirectCollisionTests+DependencyConflictModule1 -> **ModularPipelines.UnitTests.DirectCollisionTests+DependencyConflictModule2**")); + .With.Message.EqualTo("Dependency collision detected: **DependencyConflictModule1** -> DependencyConflictModule2 -> **DependencyConflictModule1**")); } [DependsOn] diff --git a/ModularPipelines.UnitTests/NestedCollisionTests.cs b/ModularPipelines.UnitTests/NestedCollisionTests.cs index cd36530356..272a685ba5 100644 --- a/ModularPipelines.UnitTests/NestedCollisionTests.cs +++ b/ModularPipelines.UnitTests/NestedCollisionTests.cs @@ -24,7 +24,7 @@ public void Modules_Dependent_On_Each_Other_Throws_Exception() }) .ExecutePipelineAsync(), Throws.Exception.TypeOf() - .With.Message.EqualTo("Dependency collision detected: **ModularPipelines.UnitTests.NestedCollisionTests+DependencyConflictModule5** -> ModularPipelines.UnitTests.NestedCollisionTests+DependencyConflictModule2 -> ModularPipelines.UnitTests.NestedCollisionTests+DependencyConflictModule3 -> ModularPipelines.UnitTests.NestedCollisionTests+DependencyConflictModule4 -> **ModularPipelines.UnitTests.NestedCollisionTests+DependencyConflictModule5**")); + .With.Message.EqualTo("Dependency collision detected: **DependencyConflictModule2** -> DependencyConflictModule3 -> DependencyConflictModule4 -> DependencyConflictModule5 -> **DependencyConflictModule2**")); } [DependsOn] diff --git a/ModularPipelines/Context/Command.cs b/ModularPipelines/Context/Command.cs index d7b82e4e0d..d5510d0d5b 100644 --- a/ModularPipelines/Context/Command.cs +++ b/ModularPipelines/Context/Command.cs @@ -11,7 +11,7 @@ namespace ModularPipelines.Context; internal class Command : ICommand { private readonly IModuleLoggerProvider _moduleLoggerProvider; - private ILogger Logger => _moduleLoggerProvider.Logger; + private ILogger Logger => _moduleLoggerProvider.GetLogger(); public Command(IModuleLoggerProvider moduleLoggerProvider) { diff --git a/ModularPipelines/Context/Downloader.cs b/ModularPipelines/Context/Downloader.cs index 99c65b2be6..9dad9f5a8b 100644 --- a/ModularPipelines/Context/Downloader.cs +++ b/ModularPipelines/Context/Downloader.cs @@ -37,7 +37,7 @@ public async Task DownloadFileAsync(DownloadOptions options, CancellationT await stream.CopyToAsync(newFile, cancellationToken); - _moduleLoggerProvider.Logger.LogInformation("File {Uri} downloaded to {SaveLocation}", options.DownloadUri, filePathToSave); + _moduleLoggerProvider.GetLogger().LogInformation("File {Uri} downloaded to {SaveLocation}", options.DownloadUri, filePathToSave); return filePathToSave!; } diff --git a/ModularPipelines/Context/IModuleContext.cs b/ModularPipelines/Context/IModuleContext.cs index 3de13d0196..e27767b3ff 100644 --- a/ModularPipelines/Context/IModuleContext.cs +++ b/ModularPipelines/Context/IModuleContext.cs @@ -18,6 +18,7 @@ public interface IModuleContext public IOptions PipelineOptions { get; } internal IDependencyCollisionDetector DependencyCollisionDetector { get; } internal IModuleResultRepository ModuleResultRepository { get; } + internal void FetchLogger(Type getType); public T? Get(); public ILogger Logger { get; } diff --git a/ModularPipelines/Context/ModuleContext.cs b/ModularPipelines/Context/ModuleContext.cs index a9369dd00b..ab1eb0d09c 100644 --- a/ModularPipelines/Context/ModuleContext.cs +++ b/ModularPipelines/Context/ModuleContext.cs @@ -13,8 +13,9 @@ namespace ModularPipelines.Context; internal class ModuleContext : IModuleContext { private readonly IModuleLoggerProvider _moduleLoggerProvider; + private ILogger? _logger; - public ILogger Logger => _moduleLoggerProvider.Logger; + public ILogger Logger => _logger ?? _moduleLoggerProvider.GetLogger(); public IServiceProvider ServiceProvider { get; } @@ -36,6 +37,11 @@ internal class ModuleContext : IModuleContext public IHex Hex { get; } public IBase64 Base64 { get; } + public void FetchLogger(Type getType) + { + _logger = _moduleLoggerProvider.GetLogger(getType); + } + public T Get() { return (T) ServiceProvider.GetRequiredService(typeof(T)); diff --git a/ModularPipelines/Engine/DependencyChainProvider.cs b/ModularPipelines/Engine/DependencyChainProvider.cs new file mode 100644 index 0000000000..f91c890f97 --- /dev/null +++ b/ModularPipelines/Engine/DependencyChainProvider.cs @@ -0,0 +1,48 @@ +using System.Reflection; +using ModularPipelines.Attributes; +using ModularPipelines.Models; +using ModularPipelines.Modules; + +namespace ModularPipelines.Engine; + +internal class DependencyChainProvider : IDependencyChainProvider +{ + public IReadOnlyList ModuleDependencyModels { get; } + + public DependencyChainProvider(IEnumerable modules) + { + ModuleDependencyModels = Detect(modules.Select(x => new ModuleDependencyModel(x)).ToList()); + } + + private List Detect(List allModules) + { + foreach (var moduleDependencyModel in allModules) + { + var dependencies = GetModuleDependencies(moduleDependencyModel, allModules).ToList(); + + moduleDependencyModel.IsDependentOn.AddRange(dependencies); + + foreach (var dependencyModel in dependencies) + { + dependencyModel.IsDependencyFor.Add(moduleDependencyModel); + } + } + + return allModules; + } + + private IEnumerable GetModuleDependencies(ModuleDependencyModel moduleDependencyModel, IReadOnlyCollection allModules) + { + var customAttributes = moduleDependencyModel.Module.GetType().GetCustomAttributes(true); + + foreach (var dependsOnAttribute in customAttributes) + { + yield return GetModuleDependencyModel(dependsOnAttribute.Type, allModules); + } + } + + private ModuleDependencyModel GetModuleDependencyModel(Type type, IEnumerable allModules) + { + return allModules.First(x => x.Module.GetType() == type); + } +} \ No newline at end of file diff --git a/ModularPipelines/Engine/DependencyDetector.cs b/ModularPipelines/Engine/DependencyDetector.cs index d6cb297ee5..3933bafe55 100644 --- a/ModularPipelines/Engine/DependencyDetector.cs +++ b/ModularPipelines/Engine/DependencyDetector.cs @@ -1,82 +1,22 @@ -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Logging; -using ModularPipelines.Attributes; -using ModularPipelines.Modules; +using ModularPipelines.Helpers; -namespace ModularPipelines; +namespace ModularPipelines.Engine; -public class DependencyDetector : IDependencyDetector +internal class DependencyDetector : IDependencyDetector { - private readonly ILogger _logger; - public IReadOnlyList ModuleDependencyModels { get; } + private readonly IDependencyCollisionDetector _dependencyCollisionDetector; + private readonly IDependencyPrinter _dependencyPrinter; - public DependencyDetector(IEnumerable modules, ILogger logger) + public DependencyDetector(IDependencyCollisionDetector dependencyCollisionDetector, + IDependencyPrinter dependencyPrinter) { - _logger = logger; - ModuleDependencyModels = Detect(modules.Select(x => new ModuleDependencyModel(x)).ToList()); + _dependencyCollisionDetector = dependencyCollisionDetector; + _dependencyPrinter = dependencyPrinter; } - public void Print() + public void Check() { - var stringBuilder = new StringBuilder(); - - foreach (var moduleDependencyModel in ModuleDependencyModels) - { - stringBuilder.AppendLine(); - Print(stringBuilder, moduleDependencyModel, 1); - } - - _logger.LogInformation("The following dependency chains have been detected:\r\n{Chain}", stringBuilder.ToString()); - } - - private void Print(StringBuilder stringBuilder, ModuleDependencyModel moduleDependencyModel, int i) - { - stringBuilder.Append(new string('-', i)); - stringBuilder.Append(' '); - stringBuilder.AppendLine(moduleDependencyModel.Module.GetType().Name); - - foreach (var dependencyModel in moduleDependencyModel.IsDependencyFor) - { - Print(stringBuilder, dependencyModel, i+2); - } - } - - private List Detect(List allModules) - { - foreach (var moduleDependencyModel in allModules) - { - var dependencies = GetModuleDependencies(moduleDependencyModel, allModules).ToList(); - - moduleDependencyModel.IsDependentOn.AddRange(dependencies); - - foreach (var dependencyModel in dependencies) - { - dependencyModel.IsDependencyFor.Add(moduleDependencyModel); - } - } - - return allModules.Where(x => !x.IsDependentOn.Any()).ToList(); + _dependencyCollisionDetector.CheckCollisions(); + _dependencyPrinter.Print(); } - - private IEnumerable GetModuleDependencies(ModuleDependencyModel moduleDependencyModel, IReadOnlyCollection allModules) - { - var customAttributes = moduleDependencyModel.Module.GetType().GetCustomAttributes(true); - - foreach (var dependsOnAttribute in customAttributes) - { - yield return GetModuleDependencyModel(dependsOnAttribute.Type, allModules); - } - } - - private ModuleDependencyModel GetModuleDependencyModel(Type type, IEnumerable allModules) - { - return allModules.First(x => x.Module.GetType() == type); - } -} - -public record ModuleDependencyModel(ModuleBase Module) -{ - public List IsDependencyFor { get; } = new(); - public List IsDependentOn { get; } = new(); } \ No newline at end of file diff --git a/ModularPipelines/Engine/DependencyPrinter.cs b/ModularPipelines/Engine/DependencyPrinter.cs new file mode 100644 index 0000000000..73fecc8c25 --- /dev/null +++ b/ModularPipelines/Engine/DependencyPrinter.cs @@ -0,0 +1,42 @@ +using System.Text; +using Microsoft.Extensions.Logging; +using ModularPipelines.Models; + +namespace ModularPipelines.Engine; + +internal class DependencyPrinter : IDependencyPrinter +{ + private readonly IDependencyChainProvider _dependencyChainProvider; + private readonly ILogger _logger; + + public DependencyPrinter(IDependencyChainProvider dependencyChainProvider, ILogger logger) + { + _dependencyChainProvider = dependencyChainProvider; + _logger = logger; + } + + public void Print() + { + var stringBuilder = new StringBuilder(); + + foreach (var moduleDependencyModel in _dependencyChainProvider.ModuleDependencyModels) + { + stringBuilder.AppendLine(); + Print(stringBuilder, moduleDependencyModel, 1); + } + + _logger.LogInformation("The following dependency chains have been detected:\r\n{Chain}", stringBuilder.ToString()); + } + + private void Print(StringBuilder stringBuilder, ModuleDependencyModel moduleDependencyModel, int i) + { + stringBuilder.Append(new string('-', i)); + stringBuilder.Append(' '); + stringBuilder.AppendLine(moduleDependencyModel.Module.GetType().Name); + + foreach (var dependencyModel in moduleDependencyModel.IsDependencyFor) + { + Print(stringBuilder, dependencyModel, i+2); + } + } +} \ No newline at end of file diff --git a/ModularPipelines/Engine/IDependencyChainProvider.cs b/ModularPipelines/Engine/IDependencyChainProvider.cs new file mode 100644 index 0000000000..1c72483b01 --- /dev/null +++ b/ModularPipelines/Engine/IDependencyChainProvider.cs @@ -0,0 +1,8 @@ +using ModularPipelines.Models; + +namespace ModularPipelines.Engine; + +internal interface IDependencyChainProvider +{ + IReadOnlyList ModuleDependencyModels { get; } +} \ No newline at end of file diff --git a/ModularPipelines/Engine/IDependencyDetector.cs b/ModularPipelines/Engine/IDependencyDetector.cs index 237828c0c0..f301d5d13b 100644 --- a/ModularPipelines/Engine/IDependencyDetector.cs +++ b/ModularPipelines/Engine/IDependencyDetector.cs @@ -1,7 +1,6 @@ -namespace ModularPipelines; +namespace ModularPipelines.Engine; -public interface IDependencyDetector -{ - IReadOnlyList ModuleDependencyModels { get; } - void Print(); +internal interface IDependencyDetector +{ + void Check(); } \ No newline at end of file diff --git a/ModularPipelines/Engine/IDependencyPrinter.cs b/ModularPipelines/Engine/IDependencyPrinter.cs new file mode 100644 index 0000000000..7df0ff8a5f --- /dev/null +++ b/ModularPipelines/Engine/IDependencyPrinter.cs @@ -0,0 +1,6 @@ +namespace ModularPipelines.Engine; + +internal interface IDependencyPrinter +{ + void Print(); +} \ No newline at end of file diff --git a/ModularPipelines/Engine/IModuleResultPrinter.cs b/ModularPipelines/Engine/IModuleResultPrinter.cs index 9f6def5504..1139299d88 100644 --- a/ModularPipelines/Engine/IModuleResultPrinter.cs +++ b/ModularPipelines/Engine/IModuleResultPrinter.cs @@ -1,6 +1,4 @@ -using ModularPipelines.Enums; - -namespace ModularPipelines.Engine; +namespace ModularPipelines.Engine; public interface IModuleResultPrinter { diff --git a/ModularPipelines/Engine/ModuleInitializer.cs b/ModularPipelines/Engine/ModuleInitializer.cs index 841e67366f..1665763c1e 100644 --- a/ModularPipelines/Engine/ModuleInitializer.cs +++ b/ModularPipelines/Engine/ModuleInitializer.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.DependencyInjection; using ModularPipelines.Context; using ModularPipelines.Modules; @@ -5,15 +6,15 @@ namespace ModularPipelines.Engine; internal class ModuleInitializer : IModuleInitializer { - private readonly IModuleContext _moduleContext; + private readonly IServiceProvider _serviceProvider; - public ModuleInitializer(IModuleContext moduleContext) + public ModuleInitializer(IServiceProvider serviceProvider) { - _moduleContext = moduleContext; + _serviceProvider = serviceProvider; } public ModuleBase Initialize(ModuleBase module) { - return module.Initialize(_moduleContext); + return module.Initialize(_serviceProvider.GetRequiredService()); } } \ No newline at end of file diff --git a/ModularPipelines/Engine/PipelineExecutor.cs b/ModularPipelines/Engine/PipelineExecutor.cs index f036291ca7..9410c223ed 100644 --- a/ModularPipelines/Engine/PipelineExecutor.cs +++ b/ModularPipelines/Engine/PipelineExecutor.cs @@ -13,7 +13,6 @@ internal class PipelineExecutor : IPipelineExecutor private readonly IModuleExecutor _moduleExecutor; private readonly EngineCancellationToken _engineCancellationToken; private readonly IDependencyDetector _dependencyDetector; - private readonly IDependencyCollisionDetector _dependencyCollisionDetector; private readonly IModuleResultPrinter _moduleResultPrinter; public PipelineExecutor( @@ -24,7 +23,6 @@ public PipelineExecutor( IModuleExecutor moduleExecutor, EngineCancellationToken engineCancellationToken, IDependencyDetector dependencyDetector, - IDependencyCollisionDetector dependencyCollisionDetector, IModuleResultPrinter moduleResultPrinter) { _pipelineSetupExecutor = pipelineSetupExecutor; @@ -34,17 +32,13 @@ public PipelineExecutor( _moduleExecutor = moduleExecutor; _engineCancellationToken = engineCancellationToken; _dependencyDetector = dependencyDetector; - _dependencyCollisionDetector = dependencyCollisionDetector; _moduleResultPrinter = moduleResultPrinter; } public async Task> ExecuteAsync() { - _dependencyDetector.Print(); - - // TODO - //_dependencyCollisionDetector.CheckDependencies(); - + _dependencyDetector.Check(); + await _pipelineSetupExecutor.OnStartAsync(); await _requirementsChecker.CheckRequirementsAsync(); diff --git a/ModularPipelines/Helpers/DependencyCollisionDetector.cs b/ModularPipelines/Helpers/DependencyCollisionDetector.cs index b57cf568dc..8787e14518 100644 --- a/ModularPipelines/Helpers/DependencyCollisionDetector.cs +++ b/ModularPipelines/Helpers/DependencyCollisionDetector.cs @@ -1,79 +1,47 @@ -using System.Collections.Concurrent; +using ModularPipelines.Engine; using ModularPipelines.Exceptions; +using ModularPipelines.Models; namespace ModularPipelines.Helpers; internal class DependencyCollisionDetector : IDependencyCollisionDetector { - private readonly IDependencyDetector _dependencyDetector; - private readonly ConcurrentDictionary> _history = new(); + private readonly IDependencyChainProvider _dependencyChainProvider; - public DependencyCollisionDetector(IDependencyDetector dependencyDetector) + public DependencyCollisionDetector(IDependencyChainProvider dependencyChainProvider) { - _dependencyDetector = dependencyDetector; - } - - public void CheckDependency(Type dependentType, Type dependencyType) - { - CheckDependency(dependentType, dependencyType, new() - { - $"**{dependentType.FullName}**" - }, true); - } - - public void CheckDependencies() - { - foreach (var moduleDependencyModel in _dependencyDetector.ModuleDependencyModels) - { - var allDescendentDependencies = GetDescendents(moduleDependencyModel).ToList(); - - var backwardsDependencyReference = allDescendentDependencies.FirstOrDefault(x => x.IsDependentOn.Contains(moduleDependencyModel)); - - if (backwardsDependencyReference != null) - { - var index = allDescendentDependencies.IndexOf(backwardsDependencyReference); - var typeChain = string.Join(" -> ", allDescendentDependencies.Take(index + 1)); - throw new DependencyCollisionException($"Dependency collision detected: {typeChain}"); - } - } + _dependencyChainProvider = dependencyChainProvider; } - private IEnumerable GetDescendents(ModuleDependencyModel moduleDependencyModel) + public void CheckCollisions() { - foreach (var directDependency in moduleDependencyModel.IsDependentOn) + foreach (var moduleDependencyModel in _dependencyChainProvider.ModuleDependencyModels) { - yield return directDependency; - - foreach (var nestedDependency in directDependency.IsDependentOn.SelectMany(GetDescendents)) - { - yield return nestedDependency; - } + CheckCollision(moduleDependencyModel); } } - private void CheckDependency(Type dependentType, Type dependencyType, List enumeratedTypes, bool shouldAdd) + private static void CheckCollision(ModuleDependencyModel moduleDependencyModel) { - enumeratedTypes.Add(dependencyType.FullName!); - - var existingDependenciesOfDependencyToAdd = _history.GetOrAdd(dependencyType, new ConcurrentBag()); + var allDescendentDependenciesAndSelf = moduleDependencyModel.AllDescendantDependenciesAndSelf().ToList(); + var allDescendentDependencies = allDescendentDependenciesAndSelf.Skip(1).ToList(); - if (existingDependenciesOfDependencyToAdd.Contains(dependentType)) - { - enumeratedTypes.Add($"**{dependentType.FullName}**"); - var typeChain = string.Join(" -> ", enumeratedTypes); - throw new DependencyCollisionException($"Dependency collision detected: {typeChain}"); - } - - foreach (var innerDependencyType in existingDependenciesOfDependencyToAdd) + if (!allDescendentDependencies.Contains(moduleDependencyModel)) { - CheckDependency(dependentType, innerDependencyType, enumeratedTypes.ToList(), false); + return; } - if (shouldAdd) - { - var existingDependencies = _history.GetOrAdd(dependentType, new ConcurrentBag()); + var index = allDescendentDependencies.IndexOf(moduleDependencyModel) + 1; - existingDependencies.Add(dependencyType); - } + var formattedArray = allDescendentDependenciesAndSelf + .Take(index + 1) + .Select(x => x.Module.GetType().Name) + .ToArray(); + + formattedArray[0] = $"**{formattedArray[0]}**"; + formattedArray[^1] = $"**{formattedArray[^1]}**"; + + var typeChain = string.Join(" -> ", formattedArray); + throw new DependencyCollisionException($"Dependency collision detected: {typeChain}"); } } \ No newline at end of file diff --git a/ModularPipelines/Helpers/IDependencyCollisionDetector.cs b/ModularPipelines/Helpers/IDependencyCollisionDetector.cs index d60df5b249..3696ff8a17 100644 --- a/ModularPipelines/Helpers/IDependencyCollisionDetector.cs +++ b/ModularPipelines/Helpers/IDependencyCollisionDetector.cs @@ -2,6 +2,5 @@ namespace ModularPipelines.Helpers; public interface IDependencyCollisionDetector { - void CheckDependencies(); - void CheckDependency(Type dependentType, Type dependencyType); + void CheckCollisions(); } \ No newline at end of file diff --git a/ModularPipelines/Helpers/IModuleLoggerProvider.cs b/ModularPipelines/Helpers/IModuleLoggerProvider.cs index 134e380751..36cd78ffcd 100644 --- a/ModularPipelines/Helpers/IModuleLoggerProvider.cs +++ b/ModularPipelines/Helpers/IModuleLoggerProvider.cs @@ -4,5 +4,6 @@ namespace ModularPipelines.Helpers; internal interface IModuleLoggerProvider { - ILogger Logger { get; } + ILogger GetLogger(Type type); + ILogger GetLogger(); } \ No newline at end of file diff --git a/ModularPipelines/Helpers/ModuleLoggerProvider.cs b/ModularPipelines/Helpers/ModuleLoggerProvider.cs index 707d61379f..4788b6019b 100644 --- a/ModularPipelines/Helpers/ModuleLoggerProvider.cs +++ b/ModularPipelines/Helpers/ModuleLoggerProvider.cs @@ -15,29 +15,61 @@ public ModuleLoggerProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } - public ILogger Logger => _logger ??= GetLogger(); - private bool IsModule(Type? type) + public ILogger GetLogger(Type type) => _logger ??= MakeLogger(type); + + public ILogger GetLogger() { - if (type is null) + if (_logger != null) { - return false; + return _logger; } - - return !type.IsAbstract && type.IsAssignableTo(typeof(ModuleBase)); - } - private ILogger GetLogger() - { - var module = new StackTrace().GetFrames().Select(x => x.GetMethod()?.ReflectedType?.ReflectedType).FirstOrDefault(IsModule); + var stackFrames = new StackTrace().GetFrames().ToList(); + var module = stackFrames.Select(x => x.GetMethod()?.ReflectedType?.ReflectedType).FirstOrDefault(IsModule); if (module == null) { + var getLoggerFrame = stackFrames.FirstOrDefault(sf => sf.GetMethod()?.Name == "get_Logger"); + + if (getLoggerFrame == null) + { + return _serviceProvider.GetRequiredService>(); + } + + var getLoggerFrameIndex = stackFrames.IndexOf(getLoggerFrame); + var nextFrame = stackFrames[getLoggerFrameIndex + 1]; + var type = nextFrame.GetMethod()?.ReflectedType; + + if (type != null) + { + return MakeLogger(type); + } + return _serviceProvider.GetRequiredService>(); } + return MakeLogger(module); + } + + private ILogger MakeLogger(Type module) + { var loggerType = typeof(ModuleLogger<>).MakeGenericType(module); - return (ILogger) _serviceProvider.GetRequiredService(loggerType); + var logger = (ILogger) _serviceProvider.GetRequiredService(loggerType); + + _logger = logger; + + return logger; + } + + private bool IsModule(Type? type) + { + if (type is null) + { + return false; + } + + return !type.IsAbstract && type.IsAssignableTo(typeof(ModuleBase)); } } \ No newline at end of file diff --git a/ModularPipelines/Host/PipelineHostBuilder.cs b/ModularPipelines/Host/PipelineHostBuilder.cs index d705b8485b..9ae17089cf 100644 --- a/ModularPipelines/Host/PipelineHostBuilder.cs +++ b/ModularPipelines/Host/PipelineHostBuilder.cs @@ -33,7 +33,17 @@ internal PipelineHostBuilder() // Transient services.AddTransient() .AddTransient() - .AddTransient(); + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient(); // Singletons services @@ -46,12 +56,10 @@ internal PipelineHostBuilder() .AddSingleton() .AddSingleton() .AddSingleton(typeof(ModuleLogger<>)) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -59,13 +67,7 @@ internal PipelineHostBuilder() .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); + .AddSingleton(); }); } diff --git a/ModularPipelines/Http.cs b/ModularPipelines/Http.cs index 2e183bde9e..c47698216b 100644 --- a/ModularPipelines/Http.cs +++ b/ModularPipelines/Http.cs @@ -48,7 +48,7 @@ public async Task PrintRequest(HttpRequestMessage request) await PrintBody(sb, request.Content); - _moduleLoggerProvider.Logger.LogInformation("---Request---\r\n{Request}", sb.ToString()); + _moduleLoggerProvider.GetLogger().LogInformation("---Request---\r\n{Request}", sb.ToString()); } public async Task PrintResponse(HttpResponseMessage response) @@ -67,7 +67,7 @@ public async Task PrintResponse(HttpResponseMessage response) await PrintBody(sb, response.Content); - _moduleLoggerProvider.Logger.LogInformation("---Response---\r\n{Response}", sb.ToString()); + _moduleLoggerProvider.GetLogger().LogInformation("---Response---\r\n{Response}", sb.ToString()); } private static void PrintHeaders(StringBuilder sb, HttpHeaders baseHeaders, HttpHeaders? contentHeaders) diff --git a/ModularPipelines/Models/ModuleDependencyModel.cs b/ModularPipelines/Models/ModuleDependencyModel.cs new file mode 100644 index 0000000000..e9d23619cf --- /dev/null +++ b/ModularPipelines/Models/ModuleDependencyModel.cs @@ -0,0 +1,62 @@ +using ModularPipelines.Modules; + +namespace ModularPipelines.Models; + +internal record ModuleDependencyModel(ModuleBase Module) +{ + public List IsDependencyFor { get; } = new(); + public List IsDependentOn { get; } = new(); + + public IEnumerable AllDescendantDependenciesAndSelf() + { + yield return this; + + foreach (var dependency in AllDescendantDependencies()) + { + yield return dependency; + } + } + + public IEnumerable AllDescendantDependencies() + { + foreach (var moduleDependencyModel in IsDependentOn) + { + yield return moduleDependencyModel; + + if (moduleDependencyModel.Module.GetType() == Module.GetType()) + { + yield break; + } + } + + foreach (var moduleDependencyModel in IsDependentOn.SelectMany(d => d.AllDescendantDependencies())) + { + yield return moduleDependencyModel; + + if (moduleDependencyModel.Module.GetType() == Module.GetType()) + { + yield break; + } + } + } + + public virtual bool Equals(ModuleDependencyModel? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Module.GetType() == other.Module.GetType(); + } + + public override int GetHashCode() + { + return Module.GetType().GetHashCode(); + } +} \ No newline at end of file diff --git a/ModularPipelines/Modules/Module.cs b/ModularPipelines/Modules/Module.cs index b97d8ac507..061952e7cf 100644 --- a/ModularPipelines/Modules/Module.cs +++ b/ModularPipelines/Modules/Module.cs @@ -21,7 +21,7 @@ public abstract partial class Module : ModuleBase { private readonly Stopwatch _stopwatch = new(); - private readonly List _dependentModules = new(); + internal List DependentModules { get; } = new(); private bool _initialized; private IModuleContext _context = null!; // Late Initialisation @@ -48,15 +48,7 @@ private void AddDependency(DependsOnAttribute dependsOnAttribute) throw new Exception($"{type.FullName} must be a module to add as a dependency"); } - _dependentModules.Add(dependsOnAttribute); - } - - private void CheckDependencyConflicts() - { - foreach (var dependentModule in _dependentModules) - { - _context.DependencyCollisionDetector.CheckDependency(GetType(), dependentModule.Type); - } + DependentModules.Add(dependsOnAttribute); } internal override async Task StartAsync() @@ -173,10 +165,8 @@ internal override async Task StartAsync() internal override ModuleBase Initialize(IModuleContext context) { + context.FetchLogger(GetType()); _context = context; - - CheckDependencyConflicts(); - _initialized = true; return this; } @@ -227,23 +217,21 @@ protected TModule GetModule() where TModule : ModuleBase throw new ModuleReferencingSelfException("A module cannot get itself"); } - _context.DependencyCollisionDetector.CheckDependency(GetType(), typeof(TModule)); - return _context.GetModule(); } private async Task WaitForModuleDependencies() { - if (!_dependentModules.Any()) + if (!DependentModules.Any()) { return; } try { - var modules = _dependentModules.Select(x => _context.GetModule(x.Type)).ToList(); + var modules = DependentModules.Select(x => _context.GetModule(x.Type)).ToList(); - var tasks = _dependentModules.Select(dependsOnAttribute => + var tasks = DependentModules.Select(dependsOnAttribute => { var module = _context.GetModule(dependsOnAttribute.Type);