diff --git a/src/Runly/JobHost.cs b/src/Runly/JobHost.cs index aff2587..9a87a9c 100644 --- a/src/Runly/JobHost.cs +++ b/src/Runly/JobHost.cs @@ -133,7 +133,10 @@ public static Task RunJobAsync(this IHost host) /// The that represents the asynchronous operation. public static Task RunJobAsync(this IHost host, CancellationToken cancellationToken) { - var action = host.Services.GetService(); + using var scope = host.Services.CreateAsyncScope(); + + var action = scope.ServiceProvider.GetRequiredService(); + return action?.RunAsync(cancellationToken) ?? Task.CompletedTask; } } diff --git a/src/Runly/Processing/ExecutionBase.cs b/src/Runly/Processing/ExecutionBase.cs index 4d1c99b..c367d58 100644 --- a/src/Runly/Processing/ExecutionBase.cs +++ b/src/Runly/Processing/ExecutionBase.cs @@ -256,7 +256,9 @@ async Task ExecuteParallelTasksAsync() { try { - await ProcessScopeAsync(provider.CreateScope()); + using var scope = provider.CreateAsyncScope(); + + await ProcessScopeAsync(scope); } catch (Exception ex) when (Job.Config.Execution.HandleExceptions) { @@ -275,7 +277,7 @@ async Task ExecuteParallelTasksAsync() /// /// The containing a scoped to get services from. /// A representing the asynchronous execution of this method. - async Task ProcessScopeAsync(IServiceScope scope) + async Task ProcessScopeAsync(AsyncServiceScope scope) { bool @continue = true; var stopwatch = new Stopwatch(); diff --git a/src/Runly/ServiceExtensions.cs b/src/Runly/ServiceExtensions.cs index 4237abd..c0ce20b 100644 --- a/src/Runly/ServiceExtensions.cs +++ b/src/Runly/ServiceExtensions.cs @@ -340,7 +340,7 @@ static void AddRunAction(this IServiceCollection services, JobCache cache, Confi )); } - services.AddSingleton(s => + services.AddScoped(s => { var cache = s.GetRequiredService(); @@ -369,7 +369,7 @@ public static IServiceCollection AddJob(this IServiceCollection services, JobCac { var info = cache.Get(config.Job.Type); - services.AddTransient(info.JobType); + services.AddScoped(info.JobType); var type = config.GetType(); diff --git a/test/Runly.Tests/Dependencies.cs b/test/Runly.Tests/Dependencies.cs index 8e51cf4..3075af8 100644 --- a/test/Runly.Tests/Dependencies.cs +++ b/test/Runly.Tests/Dependencies.cs @@ -1,7 +1,9 @@ namespace Runly.Tests { - public class Dep1 { } - public class Dep2 { } + public interface IDep1 { } + public class Dep1 : IDep1 { } + public interface IDep2 { } + public class Dep2 : IDep2 { } public class Dep3 { } public class Dep4 { } public class Dep5 { } diff --git a/test/Runly.Tests/Jobs.cs b/test/Runly.Tests/Jobs.cs index ebddeb5..48d90a7 100644 --- a/test/Runly.Tests/Jobs.cs +++ b/test/Runly.Tests/Jobs.cs @@ -46,7 +46,22 @@ public override Task ProcessAsync(int item, Dep1 arg1) } } - public class Job2 : Job + public class Job1WithConstructorDep : Job + { + public Job1WithConstructorDep(IDep1 dep1) : base(new Config()) { } + + public override IAsyncEnumerable GetItemsAsync() + { + throw new NotImplementedException(); + } + + public override Task ProcessAsync(int item) + { + throw new NotImplementedException(); + } + } + + public class Job2 : Job { public Job2() : base(new Config()) { } @@ -61,7 +76,22 @@ public override Task ProcessAsync(int item, Dep1 arg1, Dep2 arg2) } } - public class Job3 : Job + public class Job2WithConstructorDep : Job + { + public Job2WithConstructorDep(IDep1 dep1, IDep2 dep2) : base(new Config()) { } + + public override IAsyncEnumerable GetItemsAsync() + { + throw new NotImplementedException(); + } + + public override Task ProcessAsync(int item) + { + throw new NotImplementedException(); + } + } + + public class Job3 : Job { public Job3() : base(new Config()) { } diff --git a/test/Runly.Tests/Scenarios/Running/Running_a_job.cs b/test/Runly.Tests/Scenarios/Running/Running_a_job.cs index 988cd8c..a90251e 100644 --- a/test/Runly.Tests/Scenarios/Running/Running_a_job.cs +++ b/test/Runly.Tests/Scenarios/Running/Running_a_job.cs @@ -1,5 +1,8 @@ using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Runly.Hosting; using Runly.Testing; +using System; using System.Threading.Tasks; using Xunit; @@ -28,5 +31,22 @@ public async Task should_run_a_single_item_job() runner.Execution.IsComplete.Should().BeTrue(); runner.Execution.Disposition.Should().Be(Disposition.Successful); } - } + + [Fact] + public async Task should_run_a_job_with_scoped_dependency_in_constructor() + { + // CreateDefaultBuilder is more strict with Environment = Dev + Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Development"); + + var action = JobHost.CreateDefaultBuilder(["Job1WithConstructorDep"], typeof(UnitTest).Assembly) + .ConfigureServices((context, services) => + { + services.AddScoped(s => new Dep1()); + services.AddSingleton(s => new Dep2()); + }) + .Build(); + + await action.RunJobAsync(); + } + } }