Skip to content

Commit 3e1848c

Browse files
committed
🔧 build: 更新目标框架并添加新版本支持
- 在 target.feature.props 文件中添加了对 .NET 9.0、8.0 和 7.0 的支持 - 更新 ServiceCollectionApplicationExtensions.cs 中的配置获取方法 - 在 Bing.Core.Tests.csproj 中更新了目标框架版本
1 parent f25fda4 commit 3e1848c

File tree

4 files changed

+352
-2
lines changed

4 files changed

+352
-2
lines changed

asset/props/target.feature.props

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
<Project>
2+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
3+
<ImplicitUsings>enable</ImplicitUsings>
4+
<Nullable>disable</Nullable>
5+
</PropertyGroup>
6+
7+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<Nullable>disable</Nullable>
10+
</PropertyGroup>
11+
12+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net7.0' ">
13+
<ImplicitUsings>enable</ImplicitUsings>
14+
<Nullable>disable</Nullable>
15+
</PropertyGroup>
16+
217
<PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
318
<ImplicitUsings>enable</ImplicitUsings>
419
<Nullable>disable</Nullable>

framework/src/Bing.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static class ServiceCollectionApplicationExtensions
2020
public static IBingBuilder AddBing(this IServiceCollection services, Action<BingOptions> setupAction = null)
2121
{
2222
Check.NotNull(services, nameof(services));
23-
var configuration = services.GetConfiguration();
23+
var configuration = services.GetConfigurationOrNull();
2424
var options = new BingOptions();
2525

2626
Singleton<IConfiguration>.Instance = configuration;

framework/tests/Bing.Core.Tests/Bing.Core.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<Import Project="..\..\..\common.tests.props" />
33
<PropertyGroup>
4-
<TargetFrameworks>net7.0;net6.0;net5.0;netcoreapp3.1;</TargetFrameworks>
4+
<TargetFrameworks>net9.0;net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;</TargetFrameworks>
55
<RootNamespace>Bing.Tests</RootNamespace>
66
</PropertyGroup>
77
<ItemGroup>
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
using Bing.DependencyInjection;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace Bing.Tests.DependencyInjection;
5+
6+
/// <summary>
7+
/// 依赖注入 测试
8+
/// </summary>
9+
public class DependencyInjectionTest
10+
{
11+
/// <summary>
12+
/// 测试 - 单例(Singleton)解析瞬时(Transient)服务时,不应依赖于当前作用域(Scope)
13+
/// </summary>
14+
[Fact]
15+
public void Singletons_Should_Resolve_Transients_Independent_From_Current_Scope()
16+
{
17+
//Arrange
18+
19+
var services = new ServiceCollection();
20+
21+
services
22+
.AddSingleton<MySingletonServiceUsesTransients>()
23+
.AddTransient<MyTransientServiceUsesSingleton>()
24+
.AddTransient<MyTransientService>();
25+
26+
MySingletonServiceUsesTransients singletonService;
27+
28+
using (var serviceProvider = services.BuildServiceProvider())
29+
{
30+
// Act: 创建多个作用域,并验证 Transient 服务的独立性
31+
using (var scope = serviceProvider.CreateScope())
32+
{
33+
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt();
34+
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt();
35+
}
36+
37+
using (var scope = serviceProvider.CreateScope())
38+
{
39+
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt();
40+
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt();
41+
scope.ServiceProvider.GetRequiredService<MySingletonServiceUsesTransients>().ShouldNotBeDisposed();
42+
}
43+
44+
singletonService = serviceProvider.GetRequiredService<MySingletonServiceUsesTransients>();
45+
singletonService.ShouldNotBeDisposed();
46+
}
47+
48+
// Assert: 确保 Transient 实例在主服务释放时被正确释放
49+
singletonService.ShouldBeDisposed();
50+
}
51+
52+
/// <summary>
53+
/// 测试 - 当主服务被释放时,依赖的瞬时(Transient)服务是否被正确释放。
54+
/// </summary>
55+
[Fact]
56+
public void Should_Release_Resolved_Services_When_Main_Service_Is_Disposed()
57+
{
58+
var services = new ServiceCollection();
59+
60+
services
61+
.AddTransient<MyTransientServiceUsesTransients>()
62+
.AddTransient<MyTransientService>();
63+
64+
using (var serviceProvider = services.BuildServiceProvider())
65+
{
66+
MyTransientServiceUsesTransients myTransientServiceUsesTransients;
67+
68+
using (var scope = serviceProvider.CreateScope())
69+
{
70+
myTransientServiceUsesTransients = scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesTransients>();
71+
72+
myTransientServiceUsesTransients.DoIt();
73+
myTransientServiceUsesTransients.DoIt();
74+
75+
myTransientServiceUsesTransients.ShouldNotBeDisposed();
76+
}
77+
78+
// Assert: 确保在作用域被释放后,Transient 实例被释放
79+
myTransientServiceUsesTransients.ShouldBeDisposed();
80+
}
81+
}
82+
83+
/// <summary>
84+
/// 测试 - 内层作用域(Inner Scope)应解析新的 Scoped 服务实例。
85+
/// </summary>
86+
[Fact]
87+
public void Inner_Scope_Should_Resolve_New_Scoped_Service()
88+
{
89+
var services = new ServiceCollection();
90+
91+
services
92+
.AddScoped<ScopedServiceWithState>();
93+
94+
using (var serviceProvider = services.BuildServiceProvider())
95+
{
96+
using (var scope = serviceProvider.CreateScope())
97+
{
98+
var service1 = scope.ServiceProvider.GetRequiredService<ScopedServiceWithState>();
99+
var service2 = scope.ServiceProvider.GetRequiredService<ScopedServiceWithState>();
100+
101+
// Assert: 在同一作用域内,Scoped 实例应相同
102+
service1.ShouldBe(service2);
103+
104+
using (var innerScope = scope.ServiceProvider.CreateScope())
105+
{
106+
var innserService1 = innerScope.ServiceProvider.GetRequiredService<ScopedServiceWithState>();
107+
var innserService2 = innerScope.ServiceProvider.GetRequiredService<ScopedServiceWithState>();
108+
109+
// Assert: 在新的作用域内,Scoped 实例应不同
110+
innserService1.ShouldBe(innserService2);
111+
innserService1.ShouldNotBe(service1);
112+
}
113+
}
114+
}
115+
}
116+
117+
/// <summary>
118+
/// 测试 - 自动加载 - 作用域
119+
/// </summary>
120+
[Fact]
121+
public void AutoLoad_Scoped()
122+
{
123+
var serviceCollection = new ServiceCollection();
124+
serviceCollection.AddBing();
125+
var serviceProvider = serviceCollection.BuildServiceProvider();
126+
127+
var scope1 = serviceProvider.CreateScope();
128+
var a1= scope1.ServiceProvider.GetService<IA>();
129+
Assert.True(a1 is D);
130+
131+
var id1 = a1.Id;
132+
var a3 = scope1.ServiceProvider.GetRequiredService<IA>();
133+
Assert.True(a3 is D);
134+
135+
var id3 = a3.Id;
136+
scope1.Dispose();
137+
Assert.Equal(id1, id3);
138+
139+
var scope2 = serviceProvider.CreateScope();
140+
var a2 = scope2.ServiceProvider.GetRequiredService<IA>();
141+
Assert.True(a2 is D);
142+
143+
var id2 = a2.Id;
144+
scope1.Dispose();
145+
Assert.NotEqual(id1, id2);
146+
}
147+
148+
/// <summary>
149+
/// 测试 - 自动加载 - 作用域 - 多个注入
150+
/// </summary>
151+
[Fact]
152+
public void AutoLoad_Scoped_MultiInjection()
153+
{
154+
var serviceCollection = new ServiceCollection();
155+
serviceCollection.AddBing();
156+
var serviceProvider = serviceCollection.BuildServiceProvider();
157+
158+
var scope1 = serviceProvider.CreateScope();
159+
var aList = scope1.ServiceProvider.GetServices<IA>();
160+
Assert.Equal(2, aList.Count());
161+
}
162+
163+
/// <summary>
164+
/// 测试 - 自动加载 - 单例
165+
/// </summary>
166+
[Fact]
167+
public void AutoLoad_Singleton()
168+
{
169+
var serviceCollection = new ServiceCollection();
170+
serviceCollection.AddBing();
171+
var serviceProvider = serviceCollection.BuildServiceProvider();
172+
173+
var b1 = serviceProvider.GetRequiredService<IB>();
174+
Assert.True(b1 is B);
175+
176+
var b2 = serviceProvider.GetRequiredService<IB>();
177+
Assert.True(b2 is B);
178+
179+
Assert.Equal(b1.Id, b2.Id);
180+
}
181+
182+
/// <summary>
183+
/// 测试 - 自动加载 - 瞬时
184+
/// </summary>
185+
[Fact]
186+
public void AutoLoad_Transient()
187+
{
188+
var serviceCollection = new ServiceCollection();
189+
serviceCollection.AddBing();
190+
var serviceProvider = serviceCollection.BuildServiceProvider();
191+
192+
var c1 = serviceProvider.GetRequiredService<IC>();
193+
Assert.True(c1 is C);
194+
195+
var c2 = serviceProvider.GetRequiredService<IC>();
196+
Assert.True(c2 is C);
197+
198+
Assert.NotEqual(c1.Id, c2.Id);
199+
}
200+
201+
#region Samples
202+
203+
/// <summary>
204+
/// 依赖单例的瞬时服务。
205+
/// </summary>
206+
private class MyTransientServiceUsesSingleton
207+
{
208+
private readonly MySingletonServiceUsesTransients _singletonService;
209+
210+
public MyTransientServiceUsesSingleton(MySingletonServiceUsesTransients singletonService) => _singletonService = singletonService;
211+
212+
public void DoIt() => _singletonService.DoIt();
213+
}
214+
215+
/// <summary>
216+
/// 依赖多个瞬时服务的单例服务。
217+
/// </summary>
218+
private class MySingletonServiceUsesTransients
219+
{
220+
private readonly IServiceProvider _serviceProvider;
221+
222+
private readonly List<MyTransientService> _instances;
223+
224+
public MySingletonServiceUsesTransients(IServiceProvider serviceProvider)
225+
{
226+
_serviceProvider = serviceProvider;
227+
_instances = new List<MyTransientService>();
228+
}
229+
230+
public void DoIt() => _instances.Add(_serviceProvider.GetRequiredService<MyTransientService>());
231+
232+
public void ShouldNotBeDisposed()
233+
{
234+
foreach (var instance in _instances)
235+
instance.IsDisposed.ShouldBeFalse();
236+
}
237+
238+
public void ShouldBeDisposed()
239+
{
240+
foreach (var instance in _instances)
241+
instance.IsDisposed.ShouldBeTrue();
242+
}
243+
}
244+
245+
/// <summary>
246+
/// 瞬时服务,支持 IDisposable 以便测试是否被释放。
247+
/// </summary>
248+
private class MyTransientService : IDisposable
249+
{
250+
public bool IsDisposed { get; private set; }
251+
252+
public void Dispose() => IsDisposed = true;
253+
}
254+
255+
/// <summary>
256+
/// 依赖多个瞬时服务的瞬时服务。
257+
/// </summary>
258+
private class MyTransientServiceUsesTransients
259+
{
260+
private readonly IServiceProvider _serviceProvider;
261+
262+
private readonly List<MyTransientService> _instances;
263+
264+
public MyTransientServiceUsesTransients(IServiceProvider serviceProvider)
265+
{
266+
_serviceProvider = serviceProvider;
267+
_instances = new List<MyTransientService>();
268+
}
269+
270+
public void DoIt() => _instances.Add(_serviceProvider.GetRequiredService<MyTransientService>());
271+
272+
public void ShouldNotBeDisposed()
273+
{
274+
foreach (var instance in _instances)
275+
instance.IsDisposed.ShouldBeFalse();
276+
}
277+
278+
public void ShouldBeDisposed()
279+
{
280+
foreach (var instance in _instances)
281+
instance.IsDisposed.ShouldBeTrue();
282+
}
283+
}
284+
285+
/// <summary>
286+
/// 具有状态的 Scoped 服务。
287+
/// </summary>
288+
private class ScopedServiceWithState
289+
{
290+
private readonly Dictionary<string, object> _items;
291+
292+
public ScopedServiceWithState() => _items = new Dictionary<string, object>();
293+
294+
public void Set(string name, object value) => _items[name] = value;
295+
296+
public object Get(string name) => _items[name];
297+
}
298+
299+
private interface IA : IScopedDependency
300+
{
301+
string Id { get; }
302+
}
303+
304+
private interface IB : ISingletonDependency
305+
{
306+
string Id { get; }
307+
}
308+
309+
private interface IC : ITransientDependency
310+
{
311+
string Id { get; }
312+
}
313+
314+
private class A : IA
315+
{
316+
public string Id { get; } = Guid.NewGuid().ToString();
317+
}
318+
319+
private class B : IB
320+
{
321+
public string Id { get; } = Guid.NewGuid().ToString();
322+
}
323+
324+
private class C : IC
325+
{
326+
public string Id { get; } = Guid.NewGuid().ToString();
327+
}
328+
329+
private class D : IA
330+
{
331+
public string Id { get; } = Guid.NewGuid().ToString();
332+
}
333+
334+
#endregion
335+
}

0 commit comments

Comments
 (0)