Skip to content

Commit eebe44e

Browse files
committed
Version 1.1.0 - New feature: scans calling assembly if no assemblies provided
1 parent 216cf32 commit eebe44e

16 files changed

+228
-61
lines changed

NetCore.AutoRegisterDi.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
44
VisualStudioVersion = 15.0.27703.2026
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetCore.AutoRegisterDi", "NetCore.AutoRegisterDi\NetCore.AutoRegisterDi.csproj", "{FC8E0DD3-616E-4B90-9342-754E08593D4C}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.AutoRegisterDi", "NetCore.AutoRegisterDi\NetCore.AutoRegisterDi.csproj", "{FC8E0DD3-616E-4B90-9342-754E08593D4C}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{90B883DD-4329-4A6A-B73D-3D9682DC4B35}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{90B883DD-4329-4A6A-B73D-3D9682DC4B35}"
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{82D01612-D8DE-4C78-8394-19712BF8FCF5}"
1111
ProjectSection(SolutionItems) = preProject
1212
LICENCE.txt = LICENCE.txt
1313
READMe.md = READMe.md
1414
EndProjectSection
1515
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAssembly", "TestAssembly\TestAssembly.csproj", "{2B4E7847-FFF8-472A-987D-6808B1DCE56F}"
17+
EndProject
1618
Global
1719
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1820
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
2729
{90B883DD-4329-4A6A-B73D-3D9682DC4B35}.Debug|Any CPU.Build.0 = Debug|Any CPU
2830
{90B883DD-4329-4A6A-B73D-3D9682DC4B35}.Release|Any CPU.ActiveCfg = Release|Any CPU
2931
{90B883DD-4329-4A6A-B73D-3D9682DC4B35}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{2B4E7847-FFF8-472A-987D-6808B1DCE56F}.Release|Any CPU.Build.0 = Release|Any CPU
3036
EndGlobalSection
3137
GlobalSection(SolutionProperties) = preSolution
3238
HideSolutionNode = FALSE

NetCore.AutoRegisterDi/AutoRegisterHelpers.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ namespace NetCore.AutoRegisterDi
1414
public static class AutoRegisterHelpers
1515
{
1616
/// <summary>
17-
/// This finds all the public, non-generic, non-nested classes in an assembly
18-
/// in the provided assemblies
17+
/// This finds all the public, non-generic, non-nested classes in an assembly in the provided assemblies.
18+
/// If no assemblies provided then it scans the assembly that called the method
1919
/// </summary>
2020
/// <param name="services">the NET Core dependency injection service</param>
21-
/// <param name="assemblies">Each assembly you want scanned </param>
21+
/// <param name="assemblies">Each assembly you want scanned. If null then scans the the assembly that called the method</param>
2222
/// <returns></returns>
2323
public static AutoRegisterData RegisterAssemblyPublicNonGenericClasses(this IServiceCollection services, params Assembly[] assemblies)
2424
{
25+
if (assemblies.Length == 0)
26+
assemblies = new[] {Assembly.GetCallingAssembly()};
27+
2528
var allPublicTypes = assemblies.SelectMany(x => x.GetExportedTypes()
2629
.Where(y => y.IsClass && !y.IsAbstract && !y.IsGenericType && !y.IsNested));
2730
return new AutoRegisterData(services, allPublicTypes);
@@ -45,7 +48,7 @@ public static AutoRegisterData Where(this AutoRegisterData autoRegData, Func<Typ
4548
/// This registers the classes against any public interfaces (other than IDisposable) implemented by the class
4649
/// </summary>
4750
/// <param name="autoRegData">AutoRegister data produced by <see cref="RegisterAssemblyPublicNonGenericClasses"/></param> method
48-
/// <param name="lifetime">Allows you to define the timetime of the service - defaults to ServiceLifetime.Transient</param>
51+
/// <param name="lifetime">Allows you to define the scope of the service - defaults to ServiceLifetime.Transient</param>
4952
/// <returns></returns>
5053
public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegisterData autoRegData,
5154
ServiceLifetime lifetime = ServiceLifetime.Transient)
@@ -55,9 +58,8 @@ public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegister
5558
? autoRegData.TypesToConsider
5659
: autoRegData.TypesToConsider.Where(autoRegData.TypeFilter)))
5760
{
58-
var interfaces = classType.GetTypeInfo().ImplementedInterfaces
59-
.Where(i => i != typeof(IDisposable) && (i.IsPublic));
60-
foreach (var infc in interfaces)
61+
var interfaces = classType.GetTypeInfo().ImplementedInterfaces;
62+
foreach (var infc in interfaces.Where(i => i != typeof(IDisposable) && i.IsPublic && !i.IsNested))
6163
{
6264
autoRegData.Services.Add(new ServiceDescriptor(infc, classType, lifetime));
6365
}

NetCore.AutoRegisterDi/NetCore.AutoRegisterDi.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6-
<PackageVersion>1.0.0</PackageVersion>
7-
<Version>1.0.0</Version>
8-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
9-
<FileVersion>1.0.0.0</FileVersion>
6+
<PackageVersion>1.1.0</PackageVersion>
7+
<Version>1.1.0</Version>
8+
<AssemblyVersion>1.1.0.0</AssemblyVersion>
9+
<FileVersion>1.1.0.0</FileVersion>
1010
<PackageId>NetCore.AutoRegisterDi</PackageId>
1111
<PackageProjectUrl>https://github.com/JonPSmith/NetCore.AutoRegisterDi</PackageProjectUrl>
1212
<PackageLicenseUrl>https://raw.githubusercontent.com/JonPSmith/NetCore.AutoRegisterDi/master/LICENCE.txt</PackageLicenseUrl>
@@ -17,7 +17,7 @@
1717
<Copyright>Copyright (c) 2018 Inventory Innovations, Inc. and Selective Analytics Ltd.</Copyright>
1818
<Company>Selective Analytics</Company>
1919
<Description>Extension method to find/register classes in an assembly into Microsoft.Extensions.DependencyInjection</Description>
20-
<PackageReleaseNotes>First release</PackageReleaseNotes>
20+
<PackageReleaseNotes>New Feature: if no assembly is provided it scans the assembly that called the method.</PackageReleaseNotes>
2121
<PackageIconUrl>https://raw.githubusercontent.com/JonPSmith/NetCore.AutoRegisterDi/master/AutoRegisterDiIcon128.png</PackageIconUrl>
2222
</PropertyGroup>
2323

READMe.md

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,41 @@ Microsoft.Extensions.DependencyInjection dependency injection provider.
77

88
I have written a simple version of AutoFac's `RegisterAssemblyTypes`
99
method that works directly with Microsoft's DI provider.
10-
Here is an example of me using this with ASP.NET Core
10+
Here are an example of me using this with ASP.NET Core
11+
12+
#### Example 1 - scan the calling assembly
1113

1214
```c#
1315
public void ConfigureServices(IServiceCollection services)
1416
{
1517
//... other configure code removed
1618
17-
var assemblyToScan = Assembly.GetExecutingAssembly(); //..or whatever assembly you need
18-
19-
service.RegisterAssemblyPublicNonGenericClasses(assemblyToScan)
19+
service.RegisterAssemblyPublicNonGenericClasses()
2020
.Where(c => c.Name.EndsWith("Service"))
2121
.AsPublicImplementedInterfaces();
2222
```
2323

24+
25+
#### Example 2 - scaning multiple assemblies
26+
27+
```c#
28+
public void ConfigureServices(IServiceCollection services)
29+
{
30+
//... other configure code removed
31+
32+
var assembliesToScan = new []
33+
{
34+
Assembly.GetExecutingAssembly(),
35+
Assembly.GetAssembly(typeof(MyServiceInAssembly1)),
36+
Assembly.GetAssembly(typeof(MyServiceInAssembly2))
37+
};
38+
39+
service.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
40+
.Where(c => c.Name.EndsWith("Service"))
41+
.AsPublicImplementedInterfaces();
42+
```
43+
44+
2445
Licence: MIT.
2546

2647
**See [this article](https://www.thereformedprogrammer.net/asp-net-core-fast-and-automatic-dependency-injection-setup/)
@@ -52,29 +73,24 @@ registers those interfaces as pointing to the class.
5273

5374
#### 1. The `RegisterAssemblyPublicNonGenericClasses` method
5475

55-
The `RegisterAssemblyPublicNonGenericClasses` method will find all the classes
56-
in the assembly that I referenced that are considered useful for registering.
57-
The exact criteria I use are:
76+
The `RegisterAssemblyPublicNonGenericClasses` method will find all the classes in
77+
78+
1. If no assemblies are provided then it scans the assembly that called this method.
79+
2. You can provide one or more assemblies to be scanned. The easiest way to reference an assembly is to use something like this `Assembly.GetAssembly(typeof(MyService))`, which gets the assembly that `MyService` was defined in.
80+
81+
I only consider classes which match ALL of the critera below:
5882

5983
- Public access
6084
- Not nested, e.g. It won't look at classes defined inside other classes
6185
- Not Generic, e.g. MyClass\<T\>
6286
- Not Abstract
6387

64-
The method takes a list/array of assemblies to scan. Two typical ways of providing an assembly are:
65-
66-
- `Assembly.GetExecutingAssembly()`, which does what is says
67-
- `Assembly.GetAssembly(typeof(YourClass))`, which gets the assembly that `YourClass` was defined in.
6888

6989
#### 2. The `Where` method
7090

71-
Pretty straightforward - you are provided with the `Type` of each class and
72-
you can filter by any of the `Type` properties etc. This allows you to
73-
do things like only registering certain classes,
74-
e.g `Where(c => c.Name.EndsWith("Service"))`
91+
Pretty straightforward - you are provided with the `Type` of each class and you can filter by any of the `Type` properties etc. This allows you to do things like only registering certain classes, e.g `Where(c => c.Name.EndsWith("Service"))`
7592

76-
*NOTE: Useful also if you want to register some classes with a different timetime scope -
77-
See next section.*
93+
*NOTE: Useful also if you want to register some classes with a different timetime scope - See next section.*
7894

7995
#### 3. The `AsPublicImplementedInterfaces` method
8096

Test/ILocalService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) 2019 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
2+
// Licensed under MIT license. See License.txt in the project root for license information.
3+
namespace Test
4+
{
5+
public interface ILocalService
6+
{
7+
bool IsPositive(int i);
8+
}
9+
}

Test/LocalService.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2019 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
2+
// Licensed under MIT license. See License.txt in the project root for license information.
3+
4+
namespace Test
5+
{
6+
public class LocalService : ILocalService
7+
{
8+
public bool IsPositive(int i)
9+
{
10+
return i >= 0;
11+
}
12+
13+
}
14+
}

Test/Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
<ItemGroup>
1818
<ProjectReference Include="..\NetCore.AutoRegisterDi\NetCore.AutoRegisterDi.csproj" />
19+
<ProjectReference Include="..\TestAssembly\TestAssembly.csproj" />
1920
</ItemGroup>
2021

2122
</Project>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) 2018 Inventory Innovations, Inc. - build by Jon P Smith (GitHub JonPSmith)
2+
// Licensed under MIT licence. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using NetCore.AutoRegisterDi;
9+
using TestAssembly;
10+
using Xunit;
11+
using Xunit.Extensions.AssertExtensions;
12+
13+
namespace Test
14+
{
15+
public class TestAutoRegisterDiCallingAssembly
16+
{
17+
[Fact]
18+
public void TestRegisterAssemblyPublicNonGenericClasses()
19+
{
20+
//SETUP
21+
var service = new ServiceCollection();
22+
23+
//ATTEMPT
24+
var autoRegData = service.RegisterAssemblyPublicNonGenericClasses();
25+
26+
//VERIFY
27+
//does not contain nested class
28+
autoRegData.TypesToConsider.ShouldEqual(
29+
new []{typeof(LocalService), typeof(TestAutoRegisterDiCallingAssembly), typeof(TestAutoRegisterDiDifferentAssembly)});
30+
}
31+
32+
[Fact]
33+
public void TestAsPublicImplementedInterfacesMyService()
34+
{
35+
//SETUP
36+
var service = new ServiceCollection();
37+
38+
//ATTEMPT
39+
service.RegisterAssemblyPublicNonGenericClasses()
40+
.AsPublicImplementedInterfaces();
41+
42+
//VERIFY
43+
service.Count.ShouldEqual(1);
44+
service.Contains(new ServiceDescriptor(typeof(ILocalService), typeof(LocalService), ServiceLifetime.Transient), new CheckDescriptor()).ShouldBeTrue();
45+
}
46+
47+
[Fact]
48+
public void TestAsPublicImplementedInterfacesMyServiceSetLifetime()
49+
{
50+
//SETUP
51+
var service = new ServiceCollection();
52+
53+
//ATTEMPT
54+
service.RegisterAssemblyPublicNonGenericClasses()
55+
.AsPublicImplementedInterfaces(ServiceLifetime.Scoped);
56+
57+
//VERIFY
58+
service.Count.ShouldEqual(1);
59+
service.Contains(new ServiceDescriptor(typeof(ILocalService), typeof(LocalService), ServiceLifetime.Scoped), new CheckDescriptor()).ShouldBeTrue();
60+
}
61+
62+
[Fact]
63+
public void TestWhere()
64+
{
65+
//SETUP
66+
var service = new ServiceCollection();
67+
68+
//ATTEMPT
69+
service.RegisterAssemblyPublicNonGenericClasses()
70+
.Where(x => x.Name == nameof(MyService))
71+
.AsPublicImplementedInterfaces();
72+
73+
//VERIFY
74+
service.Count.ShouldEqual(0);
75+
}
76+
77+
78+
79+
80+
//-------------------------------------------------------------------------
81+
//private methods
82+
83+
private class CheckDescriptor : IEqualityComparer<ServiceDescriptor>
84+
{
85+
public bool Equals(ServiceDescriptor x, ServiceDescriptor y)
86+
{
87+
return x.ServiceType == y.ServiceType
88+
&& x.ImplementationType == y.ImplementationType
89+
&& x.Lifetime == y.Lifetime;
90+
}
91+
92+
public int GetHashCode(ServiceDescriptor obj)
93+
{
94+
throw new NotImplementedException();
95+
}
96+
}
97+
98+
}
99+
}

Test/TestAutoRegisterDi.cs renamed to Test/TestAutoRegisterDiDifferentAssembly.cs

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,27 @@
77
using System.Reflection;
88
using Microsoft.Extensions.DependencyInjection;
99
using NetCore.AutoRegisterDi;
10+
using TestAssembly;
1011
using Xunit;
1112
using Xunit.Extensions.AssertExtensions;
1213

1314
namespace Test
1415
{
15-
public class TestAutoRegisterDi
16+
public class TestAutoRegisterDiDifferentAssembly
1617
{
17-
public interface INestedMyService
18-
{
19-
string IntToString(int num);
20-
}
21-
22-
public class NestedMyService : INestedMyService
23-
{
24-
public string IntToString(int num)
25-
{
26-
return num.ToString();
27-
}
28-
}
29-
3018
[Fact]
3119
public void TestRegisterAssemblyPublicNonGenericClasses()
3220
{
3321
//SETUP
3422
var service = new ServiceCollection();
3523

36-
//ATTEMP
37-
var autoRegData = service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetExecutingAssembly());
24+
//ATTEMPT
25+
var autoRegData = service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)));
3826

3927
//VERIFY
40-
//does not cotain nested class
41-
autoRegData.TypesToConsider.ShouldEqual( new []{typeof(MyOtherService), typeof(MyService), typeof(UseService), typeof(TestAutoRegisterDi) });
28+
//does not contain nested class
29+
autoRegData.TypesToConsider.ShouldEqual(
30+
new []{typeof(ClassWithNestedService), typeof(MyOtherService), typeof(MyService), typeof(UseService)});
4231
}
4332

4433
[Fact]
@@ -47,8 +36,8 @@ public void TestAsPublicImplementedInterfacesMyService()
4736
//SETUP
4837
var service = new ServiceCollection();
4938

50-
//ATTEMP
51-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetExecutingAssembly())
39+
//ATTEMPT
40+
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
5241
.AsPublicImplementedInterfaces();
5342

5443
//VERIFY
@@ -63,8 +52,8 @@ public void TestAsPublicImplementedInterfacesMyServiceSetLifetime()
6352
//SETUP
6453
var service = new ServiceCollection();
6554

66-
//ATTEMP
67-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetExecutingAssembly())
55+
//ATTEMPT
56+
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
6857
.AsPublicImplementedInterfaces(ServiceLifetime.Scoped);
6958

7059
//VERIFY
@@ -79,8 +68,8 @@ public void TestWhere()
7968
//SETUP
8069
var service = new ServiceCollection();
8170

82-
//ATTEMP
83-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetExecutingAssembly())
71+
//ATTEMPT
72+
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
8473
.Where(x => x.Name == nameof(MyService))
8574
.AsPublicImplementedInterfaces();
8675

0 commit comments

Comments
 (0)