Skip to content

Commit 3ee159f

Browse files
committed
Merge branch 'feat/aspnetcore-bridge' into develop
# Conflicts: # SquidStd.slnx # tests/SquidStd.Tests/SquidStd.Tests.csproj
2 parents 74a9e37 + ec3346f commit 3ee159f

12 files changed

Lines changed: 437 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Short description goes here.
2020
| Package | Version | Downloads |
2121
|---------|---------|-----------|
2222
| `SquidStd.Abstractions` | [![NuGet](https://img.shields.io/nuget/v/SquidStd.Abstractions.svg)](https://www.nuget.org/packages/SquidStd.Abstractions/) | ![Downloads](https://img.shields.io/nuget/dt/SquidStd.Abstractions.svg) |
23+
| `SquidStd.AspNetCore` | [![NuGet](https://img.shields.io/nuget/v/SquidStd.AspNetCore.svg)](https://www.nuget.org/packages/SquidStd.AspNetCore/) | ![Downloads](https://img.shields.io/nuget/dt/SquidStd.AspNetCore.svg) |
2324
| `SquidStd.Core` | [![NuGet](https://img.shields.io/nuget/v/SquidStd.Core.svg)](https://www.nuget.org/packages/SquidStd.Core/) | ![Downloads](https://img.shields.io/nuget/dt/SquidStd.Core.svg) |
2425
| `SquidStd.Network` | [![NuGet](https://img.shields.io/nuget/v/SquidStd.Network.svg)](https://www.nuget.org/packages/SquidStd.Network/) | ![Downloads](https://img.shields.io/nuget/dt/SquidStd.Network.svg) |
2526
| `SquidStd.Plugin.Abstractions` | [![NuGet](https://img.shields.io/nuget/v/SquidStd.Plugin.Abstractions.svg)](https://www.nuget.org/packages/SquidStd.Plugin.Abstractions/) | ![Downloads](https://img.shields.io/nuget/dt/SquidStd.Plugin.Abstractions.svg) |

SquidStd.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<Solution>
22
<Folder Name="/src/">
3+
<Project Path="src/SquidStd.AspNetCore/SquidStd.AspNetCore.csproj" />
34
<Project Path="src/SquidStd.Messaging.Abstractions/SquidStd.Messaging.Abstractions.csproj" />
45
<Project Path="src/SquidStd.Messaging/SquidStd.Messaging.csproj" />
56
</Folder>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using DryIoc;
2+
using DryIoc.Microsoft.DependencyInjection;
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Hosting;
6+
using SquidStd.AspNetCore.Services;
7+
using SquidStd.Core.Data.Bootstrap;
8+
using SquidStd.Services.Core.Services.Bootstrap;
9+
10+
namespace SquidStd.AspNetCore.Extensions;
11+
12+
/// <summary>
13+
/// Extension methods for connecting SquidStd to ASP.NET Core Minimal API applications.
14+
/// </summary>
15+
public static class SquidStdAspNetCoreBuilderExtensions
16+
{
17+
/// <param name="builder">ASP.NET Core application builder.</param>
18+
extension(WebApplicationBuilder builder)
19+
{
20+
/// <summary>
21+
/// Registers SquidStd using DryIoc as the ASP.NET Core service provider.
22+
/// </summary>
23+
/// <param name="configureOptions">Optional SquidStd bootstrap options callback.</param>
24+
/// <returns>The same builder for chaining.</returns>
25+
public WebApplicationBuilder UseSquidStd(Action<SquidStdOptions>? configureOptions = null)
26+
=> builder.UseSquidStd(configureOptions, null);
27+
28+
/// <summary>
29+
/// Registers SquidStd using DryIoc as the ASP.NET Core service provider.
30+
/// </summary>
31+
/// <param name="configureOptions">Optional SquidStd bootstrap options callback.</param>
32+
/// <param name="configureContainer">Optional DryIoc registration callback.</param>
33+
/// <returns>The same builder for chaining.</returns>
34+
public WebApplicationBuilder UseSquidStd(
35+
Action<SquidStdOptions>? configureOptions,
36+
Func<IContainer, IContainer>? configureContainer
37+
)
38+
{
39+
ArgumentNullException.ThrowIfNull(builder);
40+
41+
var options = new SquidStdOptions
42+
{
43+
RootDirectory = builder.Environment.ContentRootPath
44+
};
45+
configureOptions?.Invoke(options);
46+
ValidateOptions(options);
47+
48+
var container = new Container();
49+
builder.Host.UseServiceProviderFactory(new DryIocServiceProviderFactory(container));
50+
SquidStdBootstrap.Create(options, container);
51+
52+
var configuredContainer = configureContainer?.Invoke(container) ?? container;
53+
54+
if (!ReferenceEquals(configuredContainer, container))
55+
{
56+
throw new InvalidOperationException("ConfigureSquidStdContainer must return the DryIoc container instance.");
57+
}
58+
59+
builder.Services.AddHostedService<SquidStdHostedService>();
60+
61+
return builder;
62+
}
63+
}
64+
65+
private static void ValidateOptions(SquidStdOptions options)
66+
{
67+
ArgumentNullException.ThrowIfNull(options);
68+
ArgumentException.ThrowIfNullOrWhiteSpace(options.ConfigName);
69+
ArgumentException.ThrowIfNullOrWhiteSpace(options.RootDirectory);
70+
}
71+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Microsoft.Extensions.Hosting;
2+
using SquidStd.Core.Interfaces.Bootstrap;
3+
4+
namespace SquidStd.AspNetCore.Services;
5+
6+
/// <summary>
7+
/// Bridges the ASP.NET Core host lifecycle to the SquidStd bootstrap lifecycle.
8+
/// </summary>
9+
internal sealed class SquidStdHostedService : IHostedService
10+
{
11+
private readonly ISquidStdBootstrap _bootstrap;
12+
13+
/// <summary>
14+
/// Initializes the hosted service.
15+
/// </summary>
16+
/// <param name="bootstrap">SquidStd bootstrap instance started with the ASP.NET host.</param>
17+
public SquidStdHostedService(ISquidStdBootstrap bootstrap)
18+
{
19+
_bootstrap = bootstrap;
20+
}
21+
22+
/// <inheritdoc />
23+
public async Task StartAsync(CancellationToken cancellationToken)
24+
{
25+
await _bootstrap.StartAsync(cancellationToken);
26+
}
27+
28+
/// <inheritdoc />
29+
public async Task StopAsync(CancellationToken cancellationToken)
30+
{
31+
await _bootstrap.StopAsync(cancellationToken);
32+
}
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0"/>
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\SquidStd.Services.Core\SquidStd.Services.Core.csproj"/>
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<InternalsVisibleTo Include="SquidStd.Tests"/>
23+
</ItemGroup>
24+
25+
</Project>

src/SquidStd.Services.Core/Services/Bootstrap/SquidStdBootstrap.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public sealed class SquidStdBootstrap : ISquidStdBootstrap
2020
{
2121
private readonly Lock _syncRoot = new();
2222
private readonly List<ISquidStdService> _startedServices = [];
23+
private readonly bool _ownsContainer;
2324
private int _disposed;
2425
private bool _loggerConfigured;
2526
private BootstrapStateType _state;
@@ -34,16 +35,20 @@ public sealed class SquidStdBootstrap : ISquidStdBootstrap
3435
/// Initializes a bootstrapper with default options.
3536
/// </summary>
3637
public SquidStdBootstrap()
37-
: this(new SquidStdOptions()) { }
38+
: this(new SquidStdOptions())
39+
{
40+
}
3841

3942
/// <summary>
4043
/// Initializes a bootstrapper with the specified options.
4144
/// </summary>
4245
/// <param name="options">Bootstrap options used to register core services.</param>
4346
public SquidStdBootstrap(SquidStdOptions options)
44-
: this(options, new Container()) { }
47+
: this(options, new Container(), true)
48+
{
49+
}
4550

46-
private SquidStdBootstrap(SquidStdOptions options, IContainer container)
51+
private SquidStdBootstrap(SquidStdOptions options, IContainer container, bool ownsContainer)
4752
{
4853
ArgumentNullException.ThrowIfNull(options);
4954
ArgumentNullException.ThrowIfNull(container);
@@ -52,6 +57,7 @@ private SquidStdBootstrap(SquidStdOptions options, IContainer container)
5257

5358
Options = options;
5459
Container = container;
60+
_ownsContainer = ownsContainer;
5561

5662
Container.RegisterInstance<ISquidStdBootstrap>(this, IfAlreadyRegistered.Replace);
5763
Container.RegisterInstance(this, IfAlreadyRegistered.Replace);
@@ -74,6 +80,15 @@ public static SquidStdBootstrap Create()
7480
public static SquidStdBootstrap Create(SquidStdOptions options)
7581
=> new(options);
7682

83+
/// <summary>
84+
/// Creates a bootstrapper using an externally owned DryIoc container.
85+
/// </summary>
86+
/// <param name="options">Bootstrap options used to register core services.</param>
87+
/// <param name="container">Externally owned container that receives SquidStd services.</param>
88+
/// <returns>The created bootstrapper.</returns>
89+
public static SquidStdBootstrap Create(SquidStdOptions options, IContainer container)
90+
=> new(options, container, false);
91+
7792
/// <inheritdoc />
7893
public ISquidStdBootstrap ConfigureService(Func<IContainer, IContainer> configure)
7994
=> ConfigureServices(configure);
@@ -323,7 +338,10 @@ public async ValueTask DisposeAsync()
323338
await Log.CloseAndFlushAsync();
324339
}
325340

326-
Container.Dispose();
341+
if (_ownsContainer)
342+
{
343+
Container.Dispose();
344+
}
327345
}
328346
}
329347
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using DryIoc;
2+
using SquidStd.Core.Data.Bootstrap;
3+
using SquidStd.Core.Interfaces.Bootstrap;
4+
5+
namespace SquidStd.Tests.AspNetCore;
6+
7+
internal sealed class FakeSquidStdBootstrap : ISquidStdBootstrap
8+
{
9+
public int StartCount { get; private set; }
10+
11+
public int StopCount { get; private set; }
12+
13+
public SquidStdOptions Options { get; } = new();
14+
15+
public IContainer Container { get; } = new Container();
16+
17+
public ISquidStdBootstrap ConfigureService(Func<IContainer, IContainer> configure)
18+
=> ConfigureServices(configure);
19+
20+
public ISquidStdBootstrap ConfigureServices(Func<IContainer, IContainer> configure)
21+
{
22+
ArgumentNullException.ThrowIfNull(configure);
23+
var configuredContainer = configure(Container);
24+
25+
return ReferenceEquals(configuredContainer, Container)
26+
? this
27+
: throw new InvalidOperationException("ConfigureServices must return the bootstrap container instance.");
28+
}
29+
30+
public TService Resolve<TService>()
31+
=> Container.Resolve<TService>();
32+
33+
public ValueTask StartAsync(CancellationToken cancellationToken = default)
34+
{
35+
cancellationToken.ThrowIfCancellationRequested();
36+
StartCount++;
37+
38+
return ValueTask.CompletedTask;
39+
}
40+
41+
public ValueTask StopAsync(CancellationToken cancellationToken = default)
42+
{
43+
cancellationToken.ThrowIfCancellationRequested();
44+
StopCount++;
45+
46+
return ValueTask.CompletedTask;
47+
}
48+
49+
public async Task RunAsync(CancellationToken cancellationToken = default)
50+
{
51+
await StartAsync(cancellationToken);
52+
}
53+
54+
public ValueTask DisposeAsync()
55+
{
56+
Container.Dispose();
57+
58+
return ValueTask.CompletedTask;
59+
}
60+
}

0 commit comments

Comments
 (0)