Skip to content

Commit dcd0639

Browse files
author
Nikolay Pianikov
committed
Add new bindings for TaskCompletionSource, CultureInfo, IFormatProvider, CompareInfo, RandomNumberGenerator, and IReadOnlySet with default configurations and conditions based on target frameworks.
1 parent 2d05a47 commit dcd0639

4 files changed

Lines changed: 296 additions & 0 deletions

File tree

src/Pure.DI.Core/Features/Default.g.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ private static void Setup()
413413
.To(_ => global::System.Threading.Tasks.TaskScheduler.Default)
414414
.Bind<global::System.Threading.Tasks.TaskCreationOptions>()
415415
.To(_ => global::System.Threading.Tasks.TaskCreationOptions.None)
416+
.Bind<global::System.Threading.Tasks.TaskCreationOptions>("TaskCompletionSource")
417+
.To(_ => global::System.Threading.Tasks.TaskCreationOptions.RunContinuationsAsynchronously)
416418
.Bind<global::System.Threading.Tasks.TaskContinuationOptions>()
417419
.To(_ => global::System.Threading.Tasks.TaskContinuationOptions.None)
418420
.Bind<global::System.Threading.Tasks.TaskFactory>().As(Lifetime.PerBlock)
@@ -421,6 +423,15 @@ private static void Setup()
421423
.Bind<global::System.Threading.Tasks.TaskFactory<TT>>().As(Lifetime.PerBlock)
422424
.To((global::System.Threading.CancellationToken cancellationToken, global::System.Threading.Tasks.TaskCreationOptions taskCreationOptions, global::System.Threading.Tasks.TaskContinuationOptions taskContinuationOptions, global::System.Threading.Tasks.TaskScheduler taskScheduler) =>
423425
new global::System.Threading.Tasks.TaskFactory<TT>(cancellationToken, taskCreationOptions, taskContinuationOptions, taskScheduler))
426+
#if NETSTANDARD || NET || NETCOREAPP || NET46_OR_GREATER
427+
.Bind<global::System.Threading.Tasks.TaskCompletionSource<TT>>().As(Lifetime.PerBlock)
428+
.To(ctx =>
429+
{
430+
ctx.Inject<global::System.Threading.Tasks.TaskCreationOptions>("TaskCompletionSource", out var taskCreationOptions);
431+
// Creates an async completion source
432+
return new global::System.Threading.Tasks.TaskCompletionSource<TT>(taskCreationOptions);
433+
})
434+
#endif
424435
.Bind<global::System.Threading.Tasks.Task<TT>>()
425436
.To(ctx =>
426437
{
@@ -541,6 +552,9 @@ private static void Setup()
541552
#if NETSTANDARD || NET || NETCOREAPP || NET40_OR_GREATER
542553
.Bind<global::System.Collections.Generic.ISet<TT>>()
543554
#endif
555+
#if NET5_0_OR_GREATER
556+
.Bind<global::System.Collections.Generic.IReadOnlySet<TT>>()
557+
#endif
544558
#if NETSTANDARD || NET || NETCOREAPP || NET35_OR_GREATER
545559
.Bind<global::System.Collections.Generic.HashSet<TT>>()
546560
.To((TT[] arr, global::System.Collections.Generic.IEqualityComparer<TT> comparer) => new global::System.Collections.Generic.HashSet<TT>(arr, comparer))
@@ -599,6 +613,17 @@ private static void Setup()
599613
#endif
600614
#endif
601615
// System services
616+
.Bind<global::System.Globalization.CultureInfo>().To(_ =>
617+
{
618+
// Provides the current culture
619+
return global::System.Globalization.CultureInfo.CurrentCulture;
620+
})
621+
.Bind<global::System.IFormatProvider>().To((global::System.Globalization.CultureInfo culture) => culture)
622+
.Bind<global::System.Globalization.CompareInfo>().To((global::System.Globalization.CultureInfo culture) =>
623+
{
624+
// Provides culture-sensitive string comparison
625+
return culture.CompareInfo;
626+
})
602627
#if NET8_0_OR_GREATER
603628
.Bind<global::System.TimeProvider>().To(_ =>
604629
{
@@ -611,6 +636,12 @@ private static void Setup()
611636
// Provides ordinal string comparison
612637
return global::System.StringComparer.Ordinal;
613638
})
639+
.Bind<global::System.StringComparison>().To(_ => global::System.StringComparison.Ordinal)
640+
.Bind<global::System.Security.Cryptography.RandomNumberGenerator>().As(Lifetime.PerBlock).To(_ =>
641+
{
642+
// Creates a cryptographic random number generator
643+
return global::System.Security.Cryptography.RandomNumberGenerator.Create();
644+
})
614645
#if NET6_0_OR_GREATER
615646
.Bind<global::System.Random>().To(_ =>
616647
{

tests/Pure.DI.IntegrationTests/BclInjectionTests.cs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,169 @@ namespace Pure.DI.IntegrationTests;
55
/// </summary>
66
public class BclInjectionTests
77
{
8+
[Fact]
9+
public async Task ShouldSupportDefaultBclServiceBindings()
10+
{
11+
// Given
12+
13+
// When
14+
var result = await """
15+
using System;
16+
using System.Collections.Generic;
17+
using System.Globalization;
18+
using System.Security.Cryptography;
19+
using System.Text.Json;
20+
using System.Threading.Tasks;
21+
using Pure.DI;
22+
23+
namespace Sample
24+
{
25+
class Service
26+
{
27+
private readonly TaskCompletionSource<int> _taskCompletionSource;
28+
private readonly IReadOnlySet<IDependency> _dependencies;
29+
private readonly JsonSerializerOptions _jsonSerializerOptions;
30+
private readonly CultureInfo _cultureInfo;
31+
private readonly IFormatProvider _formatProvider;
32+
private readonly CompareInfo _compareInfo;
33+
private readonly StringComparer _stringComparer;
34+
private readonly StringComparison _stringComparison;
35+
private readonly RandomNumberGenerator _randomNumberGenerator;
36+
37+
public Service(
38+
TaskCompletionSource<int> taskCompletionSource,
39+
IReadOnlySet<IDependency> dependencies,
40+
JsonSerializerOptions jsonSerializerOptions,
41+
CultureInfo cultureInfo,
42+
IFormatProvider formatProvider,
43+
CompareInfo compareInfo,
44+
StringComparer stringComparer,
45+
StringComparison stringComparison,
46+
RandomNumberGenerator randomNumberGenerator)
47+
{
48+
_taskCompletionSource = taskCompletionSource;
49+
_dependencies = dependencies;
50+
_jsonSerializerOptions = jsonSerializerOptions;
51+
_cultureInfo = cultureInfo;
52+
_formatProvider = formatProvider;
53+
_compareInfo = compareInfo;
54+
_stringComparer = stringComparer;
55+
_stringComparison = stringComparison;
56+
_randomNumberGenerator = randomNumberGenerator;
57+
}
58+
59+
public void Run()
60+
{
61+
Console.WriteLine(_taskCompletionSource.Task.CreationOptions.HasFlag(TaskCreationOptions.RunContinuationsAsynchronously));
62+
Console.WriteLine(_dependencies.Count);
63+
Console.WriteLine(ReferenceEquals(_jsonSerializerOptions, JsonSerializerOptions.Default));
64+
Console.WriteLine(ReferenceEquals(_cultureInfo, CultureInfo.CurrentCulture));
65+
Console.WriteLine(ReferenceEquals(_formatProvider, _cultureInfo));
66+
Console.WriteLine(ReferenceEquals(_compareInfo, CultureInfo.CurrentCulture.CompareInfo));
67+
Console.WriteLine(ReferenceEquals(_stringComparer, StringComparer.Ordinal));
68+
Console.WriteLine(_stringComparison == StringComparison.Ordinal);
69+
Console.WriteLine(_randomNumberGenerator.GetType().Name.Length > 0);
70+
}
71+
}
72+
73+
interface IDependency {}
74+
75+
class Dependency1 : IDependency {}
76+
77+
class Dependency2 : IDependency {}
78+
79+
static class Setup
80+
{
81+
private static void SetupComposition()
82+
{
83+
DI.Setup("Composition")
84+
.Bind<IDependency>(Tag.Unique).To<Dependency1>()
85+
.Bind<IDependency>(Tag.Unique).To<Dependency2>()
86+
.Root<Service>("Service");
87+
}
88+
}
89+
90+
public class Program
91+
{
92+
public static void Main()
93+
{
94+
var composition = new Composition();
95+
composition.Service.Run();
96+
}
97+
}
98+
}
99+
""".RunAsync();
100+
101+
// Then
102+
result.Success.ShouldBeTrue(result);
103+
result.StdOut.ShouldBe([
104+
"True",
105+
"2",
106+
"True",
107+
"True",
108+
"True",
109+
"True",
110+
"True",
111+
"True",
112+
"True"], result);
113+
}
114+
115+
[Fact]
116+
public async Task ShouldSupportDefaultTimeProviderBinding()
117+
{
118+
// Given
119+
120+
// When
121+
var result = await """
122+
using System;
123+
using Pure.DI;
124+
125+
namespace Sample
126+
{
127+
class Service
128+
{
129+
private readonly TimeProvider _timeProvider;
130+
131+
public Service(TimeProvider timeProvider)
132+
{
133+
_timeProvider = timeProvider;
134+
}
135+
136+
public void Run()
137+
{
138+
Console.WriteLine(ReferenceEquals(_timeProvider, TimeProvider.System));
139+
}
140+
}
141+
142+
static class Setup
143+
{
144+
private static void SetupComposition()
145+
{
146+
DI.Setup("Composition")
147+
.Root<Service>("Service");
148+
}
149+
}
150+
151+
public class Program
152+
{
153+
public static void Main()
154+
{
155+
var composition = new Composition();
156+
composition.Service.Run();
157+
}
158+
}
159+
}
160+
""".RunAsync(
161+
new Options
162+
{
163+
PreprocessorSymbols = ["NET", "NET8_0_OR_GREATER", "NET6_0_OR_GREATER", "NET5_0_OR_GREATER"]
164+
});
165+
166+
// Then
167+
result.Success.ShouldBeTrue(result);
168+
result.StdOut.ShouldBe(["True"], result);
169+
}
170+
8171
[Theory]
9172
[InlineData("System.Collections.Generic.IList")]
10173
[InlineData("System.Collections.Immutable.ImmutableArray")]

tests/Pure.DI.IntegrationTests/TestExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ private static CSharpCompilation CreateCompilation() =>
294294
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
295295
MetadataReference.CreateFromFile(typeof(Uri).Assembly.Location),
296296
MetadataReference.CreateFromFile(typeof(Regex).Assembly.Location),
297+
MetadataReference.CreateFromFile(typeof(System.Security.Cryptography.RandomNumberGenerator).Assembly.Location),
298+
MetadataReference.CreateFromFile(typeof(System.Text.Json.JsonSerializerOptions).Assembly.Location),
299+
MetadataReference.CreateFromFile(typeof(TimeProvider).Assembly.Location),
297300
MetadataReference.CreateFromFile(typeof(IAsyncEnumerable<>).Assembly.Location),
298301
MetadataReference.CreateFromFile(typeof(INotifyPropertyChanged).Assembly.Location));
299302

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
$v=true
3+
$p=101
4+
$d=Default BCL bindings
5+
$h=Pure.DI provides default bindings for commonly used .NET BCL types, so they can be injected without extra setup code.
6+
$f=>[!NOTE]
7+
$f=>Default BCL bindings can still be overridden in the composition when an application needs a different policy.
8+
$r=Shouldly
9+
*/
10+
11+
// ReSharper disable ClassNeverInstantiated.Local
12+
// ReSharper disable CheckNamespace
13+
// ReSharper disable ArrangeTypeModifiers
14+
15+
namespace Pure.DI.UsageTests.BCL.DefaultBclBindingsScenario;
16+
17+
using System.Globalization;
18+
using System.Security.Cryptography;
19+
using System.Text.Json;
20+
using Shouldly;
21+
using Xunit;
22+
23+
// {
24+
//# using Pure.DI;
25+
//# using System.Globalization;
26+
//# using System.Security.Cryptography;
27+
//# using System.Text.Json;
28+
// }
29+
30+
public class Scenario
31+
{
32+
[Fact]
33+
public void Run()
34+
{
35+
// Disable Resolve methods to keep the public API minimal
36+
// Resolve = Off
37+
// {
38+
DI.Setup(nameof(Composition))
39+
.Root<ReportService>("ReportService");
40+
41+
var composition = new Composition();
42+
var reportService = composition.ReportService;
43+
44+
reportService.JsonOptions.ShouldBe(JsonSerializerOptions.Default);
45+
reportService.Culture.ShouldBe(CultureInfo.CurrentCulture);
46+
reportService.FormatProvider.ShouldBe(reportService.Culture);
47+
reportService.CompareInfo.ShouldBe(CultureInfo.CurrentCulture.CompareInfo);
48+
reportService.StringComparer.ShouldBe(StringComparer.Ordinal);
49+
reportService.StringComparison.ShouldBe(StringComparison.Ordinal);
50+
reportService.TimeProvider.ShouldBe(TimeProvider.System);
51+
reportService.Completion.Task.CreationOptions
52+
.HasFlag(TaskCreationOptions.RunContinuationsAsynchronously)
53+
.ShouldBeTrue();
54+
55+
var bytes = new byte[4];
56+
reportService.RandomNumberGenerator.GetBytes(bytes);
57+
bytes.Length.ShouldBe(4);
58+
// }
59+
composition.SaveClassDiagram();
60+
}
61+
}
62+
63+
// {
64+
interface IPlugin;
65+
66+
class SearchPlugin : IPlugin;
67+
68+
class ExportPlugin : IPlugin;
69+
70+
class ReportService(
71+
JsonSerializerOptions jsonOptions,
72+
CultureInfo culture,
73+
IFormatProvider formatProvider,
74+
CompareInfo compareInfo,
75+
StringComparer stringComparer,
76+
StringComparison stringComparison,
77+
TimeProvider timeProvider,
78+
TaskCompletionSource<string> completion,
79+
RandomNumberGenerator randomNumberGenerator)
80+
{
81+
public JsonSerializerOptions JsonOptions { get; } = jsonOptions;
82+
83+
public CultureInfo Culture { get; } = culture;
84+
85+
public IFormatProvider FormatProvider { get; } = formatProvider;
86+
87+
public CompareInfo CompareInfo { get; } = compareInfo;
88+
89+
public StringComparer StringComparer { get; } = stringComparer;
90+
91+
public StringComparison StringComparison { get; } = stringComparison;
92+
93+
public TimeProvider TimeProvider { get; } = timeProvider;
94+
95+
public TaskCompletionSource<string> Completion { get; } = completion;
96+
97+
public RandomNumberGenerator RandomNumberGenerator { get; } = randomNumberGenerator;
98+
}
99+
// }

0 commit comments

Comments
 (0)