Skip to content

Commit 2e98718

Browse files
authored
Merge pull request #3 from ZFi88/LifeTimeAttributes
Added new features using Attributes to define if class is registered and with what ServiceLifetime
2 parents b3179d0 + e348617 commit 2e98718

24 files changed

+425
-46
lines changed

NetCore.AutoRegisterDi.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1515
EndProject
1616
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAssembly", "TestAssembly\TestAssembly.csproj", "{2B4E7847-FFF8-472A-987D-6808B1DCE56F}"
1717
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBadAssembly", "TestBadAssembly\TestBadAssembly.csproj", "{0E090B35-D105-49C1-95BE-EDC2655D6399}"
19+
EndProject
1820
Global
1921
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2022
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
3335
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Debug|Any CPU.Build.0 = Debug|Any CPU
3436
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Release|Any CPU.ActiveCfg = Release|Any CPU
3537
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{0E090B35-D105-49C1-95BE-EDC2655D6399}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{0E090B35-D105-49C1-95BE-EDC2655D6399}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{0E090B35-D105-49C1-95BE-EDC2655D6399}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{0E090B35-D105-49C1-95BE-EDC2655D6399}.Release|Any CPU.Build.0 = Release|Any CPU
3642
EndGlobalSection
3743
GlobalSection(SolutionProperties) = preSolution
3844
HideSolutionNode = FALSE
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace NetCore.AutoRegisterDi.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Attribute for marking classes which no need to register in container
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Class)]
9+
public class DoNotAutoRegisterAttribute : Attribute
10+
{
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace NetCore.AutoRegisterDi.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Attribute for marking classes which need to register with Scope lifetime
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Class)]
9+
public class RegisterAsScopedAttribute : Attribute
10+
{
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace NetCore.AutoRegisterDi.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Attribute for marking classes which need to register with Singleton lifetime
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Class)]
9+
public class RegisterAsSingletonAttribute : Attribute
10+
{
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace NetCore.AutoRegisterDi.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Attribute for marking classes which need to register with Transient lifetime
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Class)]
9+
public class RegisterAsTransientAttribute : Attribute
10+
{
11+
}
12+
}

NetCore.AutoRegisterDi/AutoRegisterHelpers.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ public static class AutoRegisterHelpers
2020
/// <param name="services">the NET Core dependency injection service</param>
2121
/// <param name="assemblies">Each assembly you want scanned. If null then scans the the assembly that called the method</param>
2222
/// <returns></returns>
23-
public static AutoRegisterData RegisterAssemblyPublicNonGenericClasses(this IServiceCollection services, params Assembly[] assemblies)
23+
public static AutoRegisterData RegisterAssemblyPublicNonGenericClasses(this IServiceCollection services,
24+
params Assembly[] assemblies)
2425
{
2526
if (assemblies.Length == 0)
2627
assemblies = new[] {Assembly.GetCallingAssembly()};
2728

2829
var allPublicTypes = assemblies.SelectMany(x => x.GetExportedTypes()
29-
.Where(y => y.IsClass && !y.IsAbstract && !y.IsGenericType && !y.IsNested));
30+
.Where(y => y.IsClass && !y.IsAbstract && !y.IsGenericType && !y.IsNested && !y.IsIgnoredType()));
3031
return new AutoRegisterData(services, allPublicTypes);
3132
}
3233

@@ -48,17 +49,19 @@ public static AutoRegisterData Where(this AutoRegisterData autoRegData, Func<Typ
4849
/// This registers the classes against any public interfaces (other than IDisposable) implemented by the class
4950
/// </summary>
5051
/// <param name="autoRegData">AutoRegister data produced by <see cref="RegisterAssemblyPublicNonGenericClasses"/></param> method
51-
/// <param name="lifetime">Allows you to define the lifetime of the service - defaults to ServiceLifetime.Transient</param>
5252
/// <returns></returns>
53-
public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegisterData autoRegData,
54-
ServiceLifetime lifetime = ServiceLifetime.Transient)
53+
public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegisterData autoRegData)
5554
{
5655
if (autoRegData == null) throw new ArgumentNullException(nameof(autoRegData));
57-
foreach (var classType in (autoRegData.TypeFilter == null
58-
? autoRegData.TypesToConsider
56+
foreach (var classType in (autoRegData.TypeFilter == null
57+
? autoRegData.TypesToConsider
5958
: autoRegData.TypesToConsider.Where(autoRegData.TypeFilter)))
6059
{
60+
if (classType.IsMultipleLifetime())
61+
throw new ArgumentException($"Class {classType.FullName} has multiple life time attributes");
62+
6163
var interfaces = classType.GetTypeInfo().ImplementedInterfaces;
64+
var lifetime = classType.GetTypeLiteTime();
6265
foreach (var infc in interfaces.Where(i => i != typeof(IDisposable) && i.IsPublic && !i.IsNested))
6366
{
6467
autoRegData.Services.Add(new ServiceDescriptor(infc, classType, lifetime));
@@ -68,4 +71,4 @@ public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegister
6871
return autoRegData.Services;
6972
}
7073
}
71-
}
74+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("Test")]
4+
5+
namespace NetCore.AutoRegisterDi
6+
{
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using System.Reflection;
11+
using Attributes;
12+
using Microsoft.Extensions.DependencyInjection;
13+
14+
15+
/// <summary>
16+
/// Extensions for <see cref="Type"/>
17+
/// </summary>
18+
internal static class TypeExtensions
19+
{
20+
/// <summary>
21+
/// Check if type marked by <see cref="DoNotAutoRegisterAttribute"/>
22+
/// </summary>
23+
/// <param name="type">type</param>
24+
public static bool IsIgnoredType(this Type type)
25+
{
26+
var doNotAutoRegisterAttribute = type.GetCustomAttribute<DoNotAutoRegisterAttribute>();
27+
return doNotAutoRegisterAttribute != null;
28+
}
29+
30+
/// <summary>
31+
/// Check if class marked by multiple lifetime attributes
32+
/// </summary>
33+
/// <param name="type">type</param>
34+
/// <returns></returns>
35+
public static bool IsMultipleLifetime(this Type type)
36+
{
37+
var attributes = type.GetCustomAttributes(true)
38+
.Select(x => x.GetType())
39+
.ToList();
40+
if (attributes.Any())
41+
{
42+
return new List<bool>
43+
{
44+
attributes.Contains(typeof(RegisterAsTransientAttribute)),
45+
attributes.Contains(typeof(RegisterAsScopedAttribute)),
46+
attributes.Contains(typeof(RegisterAsSingletonAttribute))
47+
}.Count(x => x) > 1;
48+
}
49+
50+
return false;
51+
}
52+
53+
/// <summary>
54+
/// Returns service lifetime
55+
/// </summary>
56+
/// <param name="type">type</param>
57+
/// <returns></returns>
58+
public static ServiceLifetime GetTypeLiteTime(this Type type)
59+
{
60+
if (type.GetCustomAttribute<RegisterAsTransientAttribute>() != null)
61+
{
62+
return ServiceLifetime.Transient;
63+
}
64+
65+
if (type.GetCustomAttribute<RegisterAsScopedAttribute>() != null)
66+
{
67+
return ServiceLifetime.Scoped;
68+
}
69+
70+
if (type.GetCustomAttribute<RegisterAsSingletonAttribute>() != null)
71+
{
72+
return ServiceLifetime.Singleton;
73+
}
74+
75+
return ServiceLifetime.Transient;
76+
}
77+
}
78+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Test.Extensions.AssertExtensions
2+
{
3+
using System;
4+
using Xunit;
5+
6+
/// <summary>
7+
/// Extensions which provide assertions to classes derived from <see cref="Action"/>.
8+
/// </summary>
9+
public static class ActionAssertionsExtensions
10+
{
11+
/// <summary>
12+
/// Verifies that the action throws exception.
13+
/// </summary>
14+
/// <param name="action">The action to be tested</param>
15+
/// <exception cref="ThrowsException">Thrown if the action is throw exception</exception>
16+
public static void ShouldThrow<T>(this Action action)
17+
where T : Exception
18+
{
19+
Assert.Throws<T>(action);
20+
}
21+
}
22+
}

Test/LocalIgnoredService.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Test
2+
{
3+
using NetCore.AutoRegisterDi.Attributes;
4+
5+
[DoNotAutoRegister]
6+
public class LocalIgnoredService : ILocalService
7+
{
8+
public bool IsPositive(int i)
9+
{
10+
return i > 1;
11+
}
12+
}
13+
}

Test/LocalScopeService.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Test
2+
{
3+
using NetCore.AutoRegisterDi.Attributes;
4+
5+
[RegisterAsScoped]
6+
public class LocalScopeService : ILocalService
7+
{
8+
public bool IsPositive(int i)
9+
{
10+
return i > 1;
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)