Skip to content

Commit ed6940a

Browse files
7
1 parent 8b98492 commit ed6940a

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
$v=true
3+
$p=4
4+
$d=Scope (StateObject)
5+
$h=Demonstrates scoped lifetime with `Hint(Hint.ScopeStrategy, "StateObject")` where scopes are represented by generated `Scope` objects created via `CreateScope()`.
6+
$f=>[!NOTE]
7+
$f=>This approach is useful when you need runtime scope creation without deriving a child composition type.
8+
$r=Shouldly
9+
*/
10+
11+
// ReSharper disable ClassNeverInstantiated.Local
12+
// ReSharper disable CheckNamespace
13+
// ReSharper disable ClassNeverInstantiated.Global
14+
// ReSharper disable ArrangeTypeModifiers
15+
// ReSharper disable UnusedMember.Local
16+
// ReSharper disable ArrangeTypeMemberModifiers
17+
// ReSharper disable PartialTypeWithSinglePart
18+
// ReSharper disable UnusedMember.Global
19+
#pragma warning disable CS9113 // Parameter is unread.
20+
namespace Pure.DI.UsageTests.Lifetimes.ScopeStateObjectScenario;
21+
22+
using Xunit;
23+
using static Lifetime;
24+
25+
// {
26+
//# using Pure.DI;
27+
//# using static Pure.DI.Lifetime;
28+
// }
29+
30+
public class Scenario
31+
{
32+
[Fact]
33+
public void Run()
34+
{
35+
// {
36+
var composition = new Composition();
37+
IRequestContext ctx1;
38+
IRequestContext ctx2;
39+
40+
// Request #1
41+
using (var request1 = composition.CreateScope())
42+
{
43+
var checkout11 = request1.RequestRoot;
44+
var checkout12 = request1.RequestRoot;
45+
ctx1 = checkout11.Context;
46+
47+
// Same request => same scoped instance
48+
ctx1.ShouldBe(checkout12.Context);
49+
ctx1.IsDisposed.ShouldBeFalse();
50+
}
51+
52+
// End of request #1 => scoped instance is disposed
53+
ctx1.IsDisposed.ShouldBeTrue();
54+
55+
// Request #2
56+
using (var request2 = composition.CreateScope())
57+
{
58+
var checkout2 = request2.RequestRoot;
59+
ctx2 = checkout2.Context;
60+
}
61+
62+
// Different request => different scoped instance
63+
ctx1.ShouldNotBe(ctx2);
64+
// End of request #2 => scoped instance is disposed
65+
ctx2.IsDisposed.ShouldBeTrue();
66+
// }
67+
composition.SaveClassDiagram();
68+
}
69+
}
70+
71+
// {
72+
interface IRequestContext
73+
{
74+
Guid CorrelationId { get; }
75+
76+
bool IsDisposed { get; }
77+
}
78+
79+
// Typically: DbContext / UnitOfWork / RequestTelemetry / Activity, etc.
80+
sealed class RequestContext : IRequestContext, IDisposable
81+
{
82+
public Guid CorrelationId { get; } = Guid.NewGuid();
83+
84+
public bool IsDisposed { get; private set; }
85+
86+
public void Dispose() => IsDisposed = true;
87+
}
88+
89+
interface ICheckoutService
90+
{
91+
IRequestContext Context { get; }
92+
}
93+
94+
// "Controller/service" that participates in request processing.
95+
// It depends on a scoped context (per-request resource).
96+
sealed class CheckoutService(IRequestContext context) : ICheckoutService
97+
{
98+
public IRequestContext Context => context;
99+
}
100+
101+
partial class Composition
102+
{
103+
static void Setup() =>
104+
// }
105+
// Disable Resolve methods to keep the public API minimal
106+
// Resolve = Off
107+
// {
108+
DI.Setup()
109+
.Hint(Hint.ScopeStrategy, "StateObject")
110+
// Per-request lifetime
111+
.Bind().As(Scoped).To<RequestContext>()
112+
113+
// Regular service that consumes scoped context
114+
.Bind().To<CheckoutService>()
115+
116+
// "Request root" (what your controller/handler resolves)
117+
.Root<ICheckoutService>("RequestRoot");
118+
}
119+
// }

0 commit comments

Comments
 (0)