Skip to content

Commit 0acd776

Browse files
committed
Add ScopedAttribute and update service collection extensions for scoped bindings
1 parent 8fa033e commit 0acd776

File tree

4 files changed

+99
-6
lines changed

4 files changed

+99
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Cratis. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace Cratis.DependencyInjection.for_ServiceCollectionExtensions;
7+
8+
public class when_adding_bindings_by_convention_with_scoped_implementation : Specification
9+
{
10+
IServiceCollection _services;
11+
ServiceDescriptor _serviceDescriptor;
12+
13+
void Establish() => _services = new ServiceCollection();
14+
15+
void Because()
16+
{
17+
_services.AddBindingsByConvention();
18+
_serviceDescriptor = _services.Single(_ => _.ServiceType == typeof(IMyScopedBinding));
19+
}
20+
21+
[Fact] void should_register_the_implementation() => _serviceDescriptor.ImplementationType.ShouldEqual(typeof(MyScopedBinding));
22+
[Fact] void should_register_with_scoped_lifetime() => _serviceDescriptor.Lifetime.ShouldEqual(ServiceLifetime.Scoped);
23+
}
24+
25+
public interface IMyScopedBinding;
26+
27+
[Scoped]
28+
public class MyScopedBinding : IMyScopedBinding;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Cratis. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace Cratis.DependencyInjection.for_ServiceCollectionExtensions;
7+
8+
public class when_adding_self_bindings_with_scoped_attribute : Specification
9+
{
10+
IServiceCollection _services;
11+
ServiceDescriptor _serviceDescriptor;
12+
13+
void Establish()
14+
{
15+
_services = new ServiceCollection();
16+
_services.AddSingleton<IExistingService, ExistingService>();
17+
}
18+
19+
void Because()
20+
{
21+
_services.AddSelfBindings();
22+
_serviceDescriptor = _services.FirstOrDefault(_ => _.ServiceType == typeof(MyScopedSelfBinding));
23+
}
24+
25+
[Fact] void should_register_the_implementation_type() => _serviceDescriptor.ImplementationType.ShouldEqual(typeof(MyScopedSelfBinding));
26+
[Fact] void should_register_with_scoped_lifetime() => _serviceDescriptor.Lifetime.ShouldEqual(ServiceLifetime.Scoped);
27+
}
28+
29+
public interface IExistingService;
30+
31+
public class ExistingService : IExistingService;
32+
33+
[Scoped]
34+
public class MyScopedSelfBinding;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Cratis. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace Cratis.DependencyInjection;
5+
6+
/// <summary>
7+
/// Attribute to adorn types for the IoC hookup to recognize it as Scoped.
8+
/// </summary>
9+
[AttributeUsage(AttributeTargets.Class)]
10+
public sealed class ScopedAttribute : Attribute;

Source/DotNET/Fundamentals/DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ public static IServiceCollection AddBindingsByConvention(this IServiceCollection
5353
continue;
5454
}
5555

56-
_ = conventionBasedType.HasAttribute<SingletonAttribute>() ?
57-
services.AddSingleton(interfaceToBind, conventionBasedType) :
58-
services.AddTransient(interfaceToBind, conventionBasedType);
56+
_ = GetServiceLifetime(conventionBasedType) switch
57+
{
58+
ServiceLifetime.Singleton => services.AddSingleton(interfaceToBind, conventionBasedType),
59+
ServiceLifetime.Scoped => services.AddScoped(interfaceToBind, conventionBasedType),
60+
_ => services.AddTransient(interfaceToBind, conventionBasedType)
61+
};
5962
}
6063

6164
return services;
@@ -82,9 +85,12 @@ public static IServiceCollection AddSelfBindings(this IServiceCollection service
8285
services.Any(s => s.ServiceType != _)).ToList().ForEach(_ =>
8386
{
8487
#pragma warning disable SA1312 // Variable names should begin with lower-case letter
85-
var __ = _.HasAttribute<SingletonAttribute>() ?
86-
services.AddSingleton(_, _) :
87-
services.AddTransient(_, _);
88+
var __ = GetServiceLifetime(_) switch
89+
{
90+
ServiceLifetime.Singleton => services.AddSingleton(_, _),
91+
ServiceLifetime.Scoped => services.AddScoped(_, _),
92+
_ => services.AddTransient(_, _)
93+
};
8894
#pragma warning restore SA1312 // Variable names should begin with lower-case letter
8995
});
9096

@@ -102,4 +108,19 @@ static bool HasConstructorWithUnresolvableParameters(Type type) =>
102108

103109
static bool HasConstructorWithRecordTypes(Type type) =>
104110
type.GetConstructors().Any(_ => _.GetParameters().Any(p => p.ParameterType.IsRecord()));
111+
112+
static ServiceLifetime GetServiceLifetime(Type type)
113+
{
114+
if (type.HasAttribute<SingletonAttribute>())
115+
{
116+
return ServiceLifetime.Singleton;
117+
}
118+
119+
if (type.HasAttribute<ScopedAttribute>())
120+
{
121+
return ServiceLifetime.Scoped;
122+
}
123+
124+
return ServiceLifetime.Transient;
125+
}
105126
}

0 commit comments

Comments
 (0)