Skip to content

Commit e32811f

Browse files
committed
Looks good by tests aren't right
1 parent 55ae61b commit e32811f

File tree

7 files changed

+202
-23
lines changed

7 files changed

+202
-23
lines changed

NetCore.AutoRegisterDi/AutoRegisterData.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Reflection;
68
using System.Runtime.Serialization;
79
using Microsoft.Extensions.DependencyInjection;
810

@@ -38,10 +40,55 @@ internal AutoRegisterData(IServiceCollection services, IEnumerable<Type> typesTo
3840
///
3941
/// </summary>
4042
internal List<Type> InterfacesToIgnore { get; set; }
43+
= new List<Type>
44+
{
45+
typeof(IDisposable),
46+
typeof(ISerializable),
47+
typeof(IEquatable<>) //added for records
48+
};
49+
50+
51+
internal List<Type> FullyDefinedInterfacesToIgnore { get; set; }
4152
= new List<Type>
4253
{
4354
typeof(IDisposable),
4455
typeof(ISerializable)
4556
};
57+
58+
internal List<Type> NotFullyDefinedInterfacesToIgnore { get; set; }
59+
= new List<Type>
60+
{
61+
//typeof(IEquatable<>)
62+
};
63+
64+
/// <summary>
65+
/// This returns the Interfaces that will be ignored when registering the classes
66+
/// </summary>
67+
/// <returns></returns>
68+
public List<Type> InterfacesIgnored()
69+
{
70+
FullyDefinedInterfacesToIgnore.AddRange(NotFullyDefinedInterfacesToIgnore);
71+
return FullyDefinedInterfacesToIgnore;
72+
}
73+
74+
/// <summary>
75+
/// This returns the interfaces that the class will be registered to the DI provider.
76+
/// - It only looks at interfaces that are public and not nested
77+
/// - Then it uses the two ignore interface lists
78+
/// - Removes a interface that exactly matches the class's interface
79+
/// - Removes generic interface that by its name, eg. IList{}
80+
/// </summary>
81+
/// <param name="classType">This class is tested to find any </param>
82+
/// <returns></returns>
83+
internal IEnumerable<Type> AllInterfacesForThisType(Type classType)
84+
{
85+
return classType.GetTypeInfo().ImplementedInterfaces
86+
.Where(interfaceType => interfaceType.IsPublic && !interfaceType.IsNested
87+
//This will not register the class with this interface
88+
&& !FullyDefinedInterfacesToIgnore.Contains(interfaceType)
89+
//This will not register the class where a generic type with any type arguments
90+
//adding the type `IList<>` would stop any `IList<>`, eg. `IList<int>`, `IList<string>`, `IList<???>` etc.
91+
&& NotFullyDefinedInterfacesToIgnore.All(x => x.Name != interfaceType.Name)); //will not register the class
92+
}
4693
}
4794
}

NetCore.AutoRegisterDi/AutoRegisterHelpers.cs

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
// Code added/updated by Fedor Zhekov, GitHub: @ZFi88
44

55
using System;
6-
using System.Collections;
76
using System.Collections.Generic;
87
using System.Linq;
9-
using System.Net.Http.Headers;
108
using System.Reflection;
119
using System.Runtime.Serialization;
1210
using Microsoft.Extensions.DependencyInjection;
@@ -52,20 +50,49 @@ public static AutoRegisterData Where(this AutoRegisterData autoRegData, Func<Typ
5250

5351
/// <summary>
5452
/// This allows you to state that the given interface will not be registered against a class.
55-
/// Useful if a class has a interface that you don't want registered against a class.
53+
/// Useful if a class has an interface that you don't want registered against a class
54+
/// fully defined interfaces, e.g. MyInterface, IList{MyClass}, to be ignored
5655
/// NOTE: the <see cref="IDisposable"/> and <see cref="ISerializable"/> interfaces are automatically ignored
5756
/// </summary>
5857
/// <typeparam name="TInterface">interface to be ignored</typeparam>
5958
/// <param name="autoRegData"></param>
6059
/// <returns></returns>
6160
public static AutoRegisterData IgnoreThisInterface<TInterface>(this AutoRegisterData autoRegData)
6261
{
63-
if (!typeof(TInterface).IsInterface)
64-
throw new InvalidOperationException($"The provided {typeof(TInterface).Name} mus be an interface");
65-
autoRegData.InterfacesToIgnore.Add(typeof(TInterface));
62+
var interfaceType = typeof(TInterface);
63+
if (!interfaceType.IsInterface)
64+
throw new InvalidOperationException($"The provided {interfaceType.Name} must be an interface.");
65+
66+
autoRegData.FullyDefinedInterfacesToIgnore.Add(interfaceType);
6667
return autoRegData;
6768
}
6869

70+
/// <summary>
71+
/// This allows you to state that the given interface will not be registered against a class.
72+
/// Useful if a class has an interface that you don't want registered against a class.
73+
/// This method handles generic interface where the inner generic type arguments aren't defined, which will stop all interfaces
74+
/// that uses the main type, e.g. IList{} would stop IList{int}, IList{string}, IList{AnotherClass}, etc.
75+
/// </summary>
76+
/// <param name="autoRegData"></param>
77+
/// <param name="interfaceType"></param>
78+
/// <returns></returns>
79+
public static AutoRegisterData IgnoreThisGenericInterface(this AutoRegisterData autoRegData, Type interfaceType)
80+
{
81+
if (!interfaceType.IsInterface)
82+
throw new InvalidOperationException($"The provided {interfaceType.Name} must be an interface.");
83+
if (interfaceType.IsGenericType && !interfaceType.GenericTypeArguments.Any())
84+
{
85+
//This is Generic Type and it hasn't defined the inner generic type arguments, e.g. List<>
86+
autoRegData.NotFullyDefinedInterfacesToIgnore.Add(interfaceType);
87+
}
88+
else
89+
{
90+
throw new InvalidOperationException($"The provided {interfaceType.Name} isn't a generic type or it is fully defined.");
91+
}
92+
return autoRegData;
93+
}
94+
95+
6996
/// <summary>
7097
/// This registers the classes against any public interfaces (other than InterfacesToIgnore) implemented by the class
7198
/// </summary>
@@ -78,7 +105,7 @@ public static IList<AutoRegisteredResult> AsPublicImplementedInterfaces(this Aut
78105
if (autoRegData == null) throw new ArgumentNullException(nameof(autoRegData));
79106

80107
//This lists all the ignored interfaces
81-
var result = autoRegData.InterfacesToIgnore.Select(x =>
108+
var result = autoRegData.InterfacesIgnored().Select(x =>
82109
new AutoRegisteredResult(null, x, ServiceLifetime.Singleton))
83110
.ToList();
84111

@@ -87,10 +114,8 @@ public static IList<AutoRegisteredResult> AsPublicImplementedInterfaces(this Aut
87114
if (classType.IsMultipleLifetime())
88115
throw new ArgumentException($"Class {classType.FullName} has multiple life time attributes");
89116

90-
var interfaces = classType.GetTypeInfo().ImplementedInterfaces;
91-
foreach (var infc in interfaces.Where(i =>
92-
!autoRegData.InterfacesToIgnore.Contains(i) //This will not register the class with this interface
93-
&& i.IsPublic && !i.IsNested))
117+
var interfaces = autoRegData.AllInterfacesForThisType(classType);
118+
foreach (var infc in interfaces)
94119
{
95120
var lifetimeForClass = classType.GetLifetimeForClass(lifetime);
96121
autoRegData.Services.Add(new ServiceDescriptor(infc, classType, lifetimeForClass));

READMe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# NetCore.AutoRegisterDi
22

3-
I have written a simple version of AutoFac's `RegisterAssemblyTypes` method that works directly with Microsoft's DI provider, i.e this library to scan an assemby (or assemblies) on your application and register all the public normal classes (i.e not [generic classes](https://www.tutorialspoint.com/Generics-vs-non-generics-in-Chash)) that have an interface into the Microsoft NET's [Dependency injection provider]https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection (DI for short).
3+
I have written a simple version of AutoFac's `RegisterAssemblyTypes` method that works directly with Microsoft's DI provider, i.e this library to scan an assemby (or assemblies) on your application and register all the public normal classes (i.e not [generic classes](https://www.tutorialspoint.com/Generics-vs-non-generics-in-Chash)) that have an interface into the Microsoft NET's [Dependency injection provider]https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) (DI for short).
44

55
The NetCore.AutoRegisterDi is available on [NuGet as EfCore.SchemaCompare](https://www.nuget.org/packages/NetCore.AutoRegisterDi) and is an open-source library under the MIT license. See [ReleaseNotes](https://github.com/JonPSmith/NetCore.AutoRegisterDi/blob/masterReleaseNotes.md) for details of changes and information for each release.
66

Test/TestAutoRegisterDiCallingAssembly.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.Extensions.DependencyInjection;
88
using NetCore.AutoRegisterDi;
99
using Test.DifferentServices;
10-
using TestAssembly;
1110
using Xunit;
1211
using Xunit.Extensions.AssertExtensions;
1312

Test/TestAutoRegisterDiDifferentAssembly.cs

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) 2018 Inventory Innovations, Inc. - build by Jon P Smith (GitHub JonPSmith)
22
// Licensed under MIT licence. See License.txt in the project root for license information.
33

4+
using Xunit.Abstractions;
5+
46
namespace Test
57
{
68
using System;
@@ -17,6 +19,13 @@ namespace Test
1719

1820
public class TestAutoRegisterDiDifferentAssembly
1921
{
22+
private readonly ITestOutputHelper _output;
23+
24+
public TestAutoRegisterDiDifferentAssembly(ITestOutputHelper output)
25+
{
26+
_output = output;
27+
}
28+
2029
[Fact]
2130
public void TestRegisterAssemblyPublicNonGenericClasses()
2231
{
@@ -34,45 +43,117 @@ public void TestRegisterAssemblyPublicNonGenericClasses()
3443
typeof(ClassWithNestedService),
3544
typeof(MyOtherScopeService), typeof(MyOtherService),
3645
typeof(MyScopeService), typeof(MyService),
46+
typeof(RecordNoInterface), typeof(RecordWithInterface),
3747
typeof(UseService)
3848
});
3949
}
4050

4151
[Fact]
42-
public void TestAsPublicImplementedInterfacesMyService()
52+
public void TestAsPublicImplementedInterfaces_Default()
4353
{
4454
//SETUP
4555
var service = new ServiceCollection();
4656

4757
//ATTEMPT
48-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
58+
var logs= service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
4959
.AsPublicImplementedInterfaces();
5060

5161
//VERIFY
52-
service.Count.ShouldEqual(4);
53-
service.Contains(new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Transient),
62+
foreach (var log in logs)
63+
{
64+
_output.WriteLine(log.ToString());
65+
}
66+
service.Count.ShouldEqual(7);
67+
service.Contains(
68+
new ServiceDescriptor(typeof(IMyOtherService), typeof(MyOtherScopeService), ServiceLifetime.Transient),
5469
new CheckDescriptor()).ShouldBeTrue();
5570
service.Contains(
5671
new ServiceDescriptor(typeof(IMyOtherService), typeof(MyOtherService), ServiceLifetime.Transient),
5772
new CheckDescriptor()).ShouldBeTrue();
73+
service.Contains(
74+
new ServiceDescriptor(typeof(IMyService), typeof(MyScopeService), ServiceLifetime.Scoped),
75+
new CheckDescriptor()).ShouldBeTrue();
76+
service.Contains(
77+
new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Transient),
78+
new CheckDescriptor()).ShouldBeTrue();
79+
service.Contains(
80+
new ServiceDescriptor(typeof(IRecordWithInterface), typeof(RecordWithInterface), ServiceLifetime.Transient),
81+
new CheckDescriptor()).ShouldBeTrue();
82+
//records
83+
service.Contains(
84+
new ServiceDescriptor(typeof(IEquatable<RecordWithInterface>), typeof(RecordWithInterface), ServiceLifetime.Transient),
85+
new CheckDescriptor()).ShouldBeTrue();
86+
service.Contains(
87+
new ServiceDescriptor(typeof(IEquatable<RecordNoInterface>), typeof(RecordNoInterface), ServiceLifetime.Transient),
88+
new CheckDescriptor()).ShouldBeTrue();
89+
}
90+
91+
[Fact]
92+
public void TestAsPublicImplementedInterfaces_IgnoreIMyOtherService()
93+
{
94+
//SETUP
95+
var service = new ServiceCollection();
96+
97+
//ATTEMPT
98+
var logs = service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
99+
.IgnoreThisInterface<IMyOtherService>()
100+
.AsPublicImplementedInterfaces();
101+
102+
//VERIFY
103+
foreach (var log in logs)
104+
{
105+
_output.WriteLine(log.ToString());
106+
}
107+
service.Count.ShouldEqual(5);
108+
service.Contains(
109+
new ServiceDescriptor(typeof(IMyService), typeof(MyScopeService), ServiceLifetime.Scoped),
110+
new CheckDescriptor()).ShouldBeTrue();
111+
service.Contains(
112+
new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Transient),
113+
new CheckDescriptor()).ShouldBeTrue();
114+
service.Contains(
115+
new ServiceDescriptor(typeof(IRecordWithInterface), typeof(RecordWithInterface), ServiceLifetime.Transient),
116+
new CheckDescriptor()).ShouldBeTrue();
117+
//records
118+
service.Contains(
119+
new ServiceDescriptor(typeof(IEquatable<RecordWithInterface>), typeof(RecordWithInterface), ServiceLifetime.Transient),
120+
new CheckDescriptor()).ShouldBeTrue();
121+
service.Contains(
122+
new ServiceDescriptor(typeof(IEquatable<RecordNoInterface>), typeof(RecordNoInterface), ServiceLifetime.Transient),
123+
new CheckDescriptor()).ShouldBeTrue();
58124
}
59125

60126
[Fact]
61-
public void TestAsPublicImplementedInterfacesMyServiceSetLifetime()
127+
public void TestAsPublicImplementedInterfaces_IgnoreIEquatable()
62128
{
63129
//SETUP
64130
var service = new ServiceCollection();
65131

66132
//ATTEMPT
67-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
133+
var logs = service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
134+
.IgnoreThisGenericInterface(typeof(IEquatable<>))
68135
.AsPublicImplementedInterfaces();
69136

70137
//VERIFY
71-
service.Count.ShouldEqual(4);
72-
service.Contains(new ServiceDescriptor(typeof(IMyService), typeof(MyScopeService), ServiceLifetime.Scoped),
138+
foreach (var log in logs)
139+
{
140+
_output.WriteLine(log.ToString());
141+
}
142+
service.Count.ShouldEqual(5);
143+
service.Contains(
144+
new ServiceDescriptor(typeof(IMyOtherService), typeof(MyOtherScopeService), ServiceLifetime.Transient),
145+
new CheckDescriptor()).ShouldBeTrue();
146+
service.Contains(
147+
new ServiceDescriptor(typeof(IMyOtherService), typeof(MyOtherService), ServiceLifetime.Transient),
73148
new CheckDescriptor()).ShouldBeTrue();
74149
service.Contains(
75-
new ServiceDescriptor(typeof(IMyOtherService), typeof(MyOtherScopeService), ServiceLifetime.Scoped),
150+
new ServiceDescriptor(typeof(IMyService), typeof(MyScopeService), ServiceLifetime.Scoped),
151+
new CheckDescriptor()).ShouldBeTrue();
152+
service.Contains(
153+
new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Transient),
154+
new CheckDescriptor()).ShouldBeTrue();
155+
service.Contains(
156+
new ServiceDescriptor(typeof(IRecordWithInterface), typeof(RecordWithInterface), ServiceLifetime.Transient),
76157
new CheckDescriptor()).ShouldBeTrue();
77158
}
78159

@@ -83,11 +164,15 @@ public void TestWhere()
83164
var service = new ServiceCollection();
84165

85166
//ATTEMPT
86-
service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
167+
var logs = service.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(MyService)))
87168
.Where(x => x.Name == nameof(MyService))
88169
.AsPublicImplementedInterfaces();
89170

90171
//VERIFY
172+
foreach (var log in logs)
173+
{
174+
_output.WriteLine(log.ToString());
175+
}
91176
service.Count.ShouldEqual(1);
92177
service.Contains(new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Transient),
93178
new CheckDescriptor()).ShouldBeTrue();

TestAssembly/RecordTypes.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2023 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 TestAssembly;
5+
6+
public record RecordNoInterface(string FirstName, string LastName)
7+
{
8+
public string FirstName { get; set; }
9+
public string LastName { get; set; }
10+
}
11+
12+
public record RecordWithInterface(string FirstName, string LastName) : IRecordWithInterface
13+
{
14+
public string FirstName { get; set; }
15+
public string LastName { get; set; }
16+
}
17+
18+
public interface IRecordWithInterface
19+
{
20+
public string FirstName { get; set; }
21+
public string LastName { get; set; }
22+
}

TestAssembly/TestAssembly.csproj

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

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>preview</LangVersion>
56
</PropertyGroup>
67

78
<ItemGroup>

0 commit comments

Comments
 (0)