Skip to content

Commit 632ff60

Browse files
stub hosting extensions
stub a UseHost extension method to configure app with an IHost and IHostBuilder Inject CommandContext as a service
1 parent 87fbb45 commit 632ff60

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<AssemblyTitle>CommandDotNet.Hosting</AssemblyTitle>
5+
<Description>Integrates Microsoft.Extensions.Hosting with CommandDotNet</Description>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<Compile Remove="output\**" />
9+
</ItemGroup>
10+
<ItemGroup>
11+
<EmbeddedResource Remove="output\**" />
12+
</ItemGroup>
13+
<ItemGroup>
14+
<None Remove="output\**" />
15+
</ItemGroup>
16+
<ItemGroup>
17+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
18+
<PackageReference Include="Nullable" Version="1.2.1">
19+
<PrivateAssets>all</PrivateAssets>
20+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
<ItemGroup>
24+
<ProjectReference Include="..\CommandDotNet\CommandDotNet.csproj" />
25+
</ItemGroup>
26+
27+
</Project>
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using CommandDotNet.Execution;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.Hosting;
8+
9+
namespace CommandDotNet.Hosting
10+
{
11+
public static class HostingExtensions
12+
{
13+
public static AppRunner UseHost(this AppRunner runner,
14+
string[]? args = null,
15+
Action<IHostBuilder>? configureHost = null,
16+
Func<IHostBuilder> hostBuilderFactory = null,
17+
bool supportReplSessions = false)
18+
{
19+
var hostBuilder = hostBuilderFactory?.Invoke() ?? Host.CreateDefaultBuilder(args);
20+
21+
hostBuilder.ConfigureHostConfiguration(hostConfig =>
22+
{
23+
hostConfig.SetBasePath(Directory.GetCurrentDirectory());
24+
});
25+
26+
configureHost?.Invoke(hostBuilder);
27+
return runner.UseHost(hostBuilder, supportReplSessions);
28+
}
29+
30+
public static AppRunner UseHost(this AppRunner runner, IHostBuilder hostBuilder, bool supportReplSessions = false)
31+
{
32+
if (hostBuilder == null)
33+
{
34+
throw new ArgumentNullException(nameof(hostBuilder));
35+
}
36+
37+
return runner.Configure(cfg =>
38+
{
39+
hostBuilder.ConfigureServices(services =>
40+
{
41+
if (supportReplSessions)
42+
{
43+
services.AddScoped<ScopedServices>();
44+
services.AddScoped(p => p.GetRequiredService<ScopedServices>().CommandContext!);
45+
services.AddScoped(p => p.GetRequiredService<ScopedServices>().CommandContext!.Console);
46+
}
47+
else
48+
{
49+
services.AddSingleton<ScopedServices>();
50+
services.AddSingleton(p => p.GetRequiredService<ScopedServices>().CommandContext!);
51+
services.AddSingleton(p => p.GetRequiredService<ScopedServices>().CommandContext!.Console);
52+
}
53+
});
54+
55+
var host = hostBuilder.Build();
56+
57+
cfg.UseMiddleware(RunHost, MiddlewareSteps.CancellationHandler + 1000);
58+
cfg.UseMiddleware(InjectCommandContextForDI, MiddlewareSteps.CancellationHandler + 1001);
59+
cfg.Services.Add(new Config(host, supportReplSessions));
60+
});
61+
}
62+
63+
private static async Task<int> RunHost(CommandContext context, ExecutionDelegate next)
64+
{
65+
var config = context.AppConfig.Services.GetOrThrow<Config>();
66+
67+
if (config.IsStarted)
68+
{
69+
// this call is within a REPL session.
70+
// do not recreate the host
71+
return await next(context);
72+
}
73+
74+
config.IsStarted = true;
75+
await config.Host.StartAsync();
76+
var result = await next(context);
77+
await config.Host.StopAsync();
78+
79+
return result;
80+
}
81+
82+
private class Config
83+
{
84+
public bool IsStarted;
85+
public IHost Host { get; }
86+
public bool SupportReplSessions { get; }
87+
88+
public Config(IHost host, bool supportReplSessions)
89+
{
90+
Host = host ?? throw new ArgumentNullException(nameof(host));
91+
SupportReplSessions = supportReplSessions;
92+
}
93+
}
94+
95+
private class ScopedServices
96+
{
97+
public CommandContext? CommandContext;
98+
}
99+
100+
private static Task<int> InjectCommandContextForDI(CommandContext context, ExecutionDelegate next)
101+
{
102+
var config = context.AppConfig.Services.GetOrThrow<Config>();
103+
var serviceProvider = config.Host.Services;
104+
105+
if (config.SupportReplSessions)
106+
{
107+
using var scope = serviceProvider.CreateScope();
108+
scope.ServiceProvider.GetRequiredService<ScopedServices>().CommandContext = context;
109+
return next(context);
110+
}
111+
112+
serviceProvider.GetRequiredService<ScopedServices>().CommandContext = context;
113+
return next(context);
114+
}
115+
}
116+
}

CommandDotNet.sln

+10
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandDotNet.Example.Tests
4040
EndProject
4141
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandDotNet.DataAnnotations", "CommandDotNet.DataAnnotations\CommandDotNet.DataAnnotations.csproj", "{558AA426-06D4-4FC9-B2E5-B6742F0A5D77}"
4242
EndProject
43+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandDotNet.Hosting", "CommandDotNet.Hosting\CommandDotNet.Hosting.csproj", "{969B52A2-8790-47C6-A624-AB7FA1383AE7}"
44+
EndProject
4345
Global
4446
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4547
Debug|Any CPU = Debug|Any CPU
@@ -94,6 +96,14 @@ Global
9496
{558AA426-06D4-4FC9-B2E5-B6742F0A5D77}.Debug|Any CPU.Build.0 = Debug|Any CPU
9597
{558AA426-06D4-4FC9-B2E5-B6742F0A5D77}.Release|Any CPU.ActiveCfg = Release|Any CPU
9698
{558AA426-06D4-4FC9-B2E5-B6742F0A5D77}.Release|Any CPU.Build.0 = Release|Any CPU
99+
{969B52A2-8790-47C6-A624-AB7FA1383AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
100+
{969B52A2-8790-47C6-A624-AB7FA1383AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
101+
{969B52A2-8790-47C6-A624-AB7FA1383AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
102+
{969B52A2-8790-47C6-A624-AB7FA1383AE7}.Release|Any CPU.Build.0 = Release|Any CPU
103+
{1149E03F-FF07-448A-BB42-82876459B138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
104+
{1149E03F-FF07-448A-BB42-82876459B138}.Debug|Any CPU.Build.0 = Debug|Any CPU
105+
{1149E03F-FF07-448A-BB42-82876459B138}.Release|Any CPU.ActiveCfg = Release|Any CPU
106+
{1149E03F-FF07-448A-BB42-82876459B138}.Release|Any CPU.Build.0 = Release|Any CPU
97107
EndGlobalSection
98108
GlobalSection(SolutionProperties) = preSolution
99109
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)