diff --git a/Dependify.sln b/Dependify.sln
new file mode 100644
index 0000000..72f3193
--- /dev/null
+++ b/Dependify.sln
@@ -0,0 +1,54 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{782FBD9E-6CF9-4521-ADE7-DCEC2D916FDF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dependify", "src\Dependify\Dependify.csproj", "{011819CE-229A-48D1-99A7-25A6D5DD5CA4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dependify.Test", "src\Dependify.Test\Dependify.Test.csproj", "{BD19041B-FC13-456A-9F3E-7823B9EF5E70}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|x64.ActiveCfg = Debug|x64
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|x64.Build.0 = Debug|x64
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|x86.ActiveCfg = Debug|x86
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Debug|x86.Build.0 = Debug|x86
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|x64.ActiveCfg = Release|x64
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|x64.Build.0 = Release|x64
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|x86.ActiveCfg = Release|x86
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4}.Release|x86.Build.0 = Release|x86
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|x64.ActiveCfg = Debug|x64
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|x64.Build.0 = Debug|x64
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|x86.ActiveCfg = Debug|x86
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Debug|x86.Build.0 = Debug|x86
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|x64.ActiveCfg = Release|x64
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|x64.Build.0 = Release|x64
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|x86.ActiveCfg = Release|x86
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {011819CE-229A-48D1-99A7-25A6D5DD5CA4} = {782FBD9E-6CF9-4521-ADE7-DCEC2D916FDF}
+ {BD19041B-FC13-456A-9F3E-7823B9EF5E70} = {782FBD9E-6CF9-4521-ADE7-DCEC2D916FDF}
+ EndGlobalSection
+EndGlobal
diff --git a/Dependify.sln.DotSettings b/Dependify.sln.DotSettings
new file mode 100644
index 0000000..a3e5085
--- /dev/null
+++ b/Dependify.sln.DotSettings
@@ -0,0 +1,209 @@
+
+ True
+ True
+
+ <?xml version="1.0" encoding="utf-16"?><Profile name="FlexiSpooler"><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments></Profile>
+ FlexiSpooler
+
+ END_OF_LINE
+ END_OF_LINE
+ False
+ True
+ END_OF_LINE
+ END_OF_LINE
+ TOGETHER_SAME_LINE
+ ALWAYS_REMOVE
+ ALWAYS_REMOVE
+ ALWAYS_REMOVE
+ ALWAYS_REMOVE
+ ALWAYS_REMOVE
+ ALWAYS_REMOVE
+ True
+ True
+ END_OF_LINE
+ END_OF_LINE
+ 1
+ 1
+ END_OF_LINE
+ False
+ False
+ False
+ False
+ False
+ LINE_BREAK
+ False
+ True
+ True
+ True
+ False
+ False
+ True
+ False
+ END_OF_LINE
+ WRAP_IF_LONG
+ WRAP_IF_LONG
+ 150
+ False
+ WRAP_IF_LONG
+ WRAP_IF_LONG
+ WRAP_IF_LONG
+ WRAP_IF_LONG
+ WRAP_IF_LONG
+ RemoveIndent
+ RemoveIndent
+ False
+ False
+ False
+ <?xml version="1.0" encoding="utf-16"?>
+<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
+ <TypePattern DisplayName="COM interfaces or structs">
+ <TypePattern.Match>
+ <Or>
+ <And>
+ <Kind Is="Interface" />
+ <Or>
+ <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
+ <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
+ </Or>
+ </And>
+ <Kind Is="Struct" />
+ </Or>
+ </TypePattern.Match>
+ </TypePattern>
+ <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
+ <TypePattern.Match>
+ <And>
+ <Kind Is="Class" />
+ <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" />
+ </And>
+ </TypePattern.Match>
+ <Entry DisplayName="Setup/Teardown Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <Or>
+ <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" />
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="All other members" />
+ <Entry Priority="100" DisplayName="Test Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="NUnit.Framework.TestAttribute" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ </TypePattern>
+ <TypePattern DisplayName="Default Pattern">
+ <Entry DisplayName="Nested Types">
+ <Entry.Match>
+ <Kind Is="Type" />
+ </Entry.Match>
+ </Entry>
+ <Entry Priority="100" DisplayName="Public Delegates">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Delegate" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry Priority="100" DisplayName="Public Enums">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Enum" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Static Fields and Constants">
+ <Entry.Match>
+ <Or>
+ <Kind Is="Constant" />
+ <And>
+ <Kind Is="Field" />
+ <Static />
+ </And>
+ </Or>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Kind Order="Constant Field" />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Fields">
+ <Entry.Match>
+ <And>
+ <Kind Is="Field" />
+ <Not>
+ <Static />
+ </Not>
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Readonly />
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Properties, Indexers">
+ <Entry.Match>
+ <Or>
+ <Kind Is="Property" />
+ <Kind Is="Indexer" />
+ </Or>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Constructors">
+ <Entry.Match>
+ <Kind Is="Constructor" />
+ </Entry.Match>
+ <Entry.SortBy>
+ <Static />
+ </Entry.SortBy>
+ </Entry>
+ <Entry Priority="100" DisplayName="Interface Implementations">
+ <Entry.Match>
+ <And>
+ <Kind Is="Member" />
+ <ImplementsInterface />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <ImplementsInterface Immediate="True" />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="All other members" />
+ </TypePattern>
+</Patterns>
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected" Description=""><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ False
+ False
+ x64
+ v4_5
\ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..bf3c88b
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+ "sdk": {
+ "version": "2.0.0"
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify.Test/AutoRegisterTestCases.cs b/src/Dependify.Test/AutoRegisterTestCases.cs
new file mode 100644
index 0000000..62a1374
--- /dev/null
+++ b/src/Dependify.Test/AutoRegisterTestCases.cs
@@ -0,0 +1,67 @@
+using System;
+using Dependify;
+using Dependify.Attributes;
+using ShouldRegisterScoped;
+using ShouldRegisterSingleton;
+using ShouldRegisterTransient;
+
+public interface IInterface { }
+
+public interface IInterface2 { }
+
+namespace ShouldRegisterTransient {
+ [RegisterTransient]
+ public class ImplementationTransient : IInterface { }
+}
+
+namespace ShouldRegisterScoped {
+ [RegisterScoped]
+ public class ImplementationScoped : IInterface { }
+}
+
+namespace ShouldRegisterSingleton {
+ [RegisterSingleton]
+ public class ImplementationSingleton : IInterface { }
+}
+
+namespace ShouldRegisterOneTransient {
+ [RegisterTransient(typeof(IInterface2))]
+ public class ImplementationTransientOneInterface : IInterface, IInterface2 { }
+}
+
+namespace ShouldRegisterOneScoped {
+ [RegisterScoped(typeof(IInterface2))]
+ public class ImplementationScopedOneInterface : IInterface, IInterface2 { }
+}
+
+namespace ShouldRegisterOneSingleton {
+ [RegisterSingleton(typeof(IInterface2))]
+ public class ImplementationSingletonOneInterface : IInterface, IInterface2 { }
+}
+
+namespace ShouldRegisterFactoryTransient {
+ public class TransientFactory {
+ [RegisterTransientFactory(typeof(IInterface))]
+ public static ImplementationTransient CreateImplementationTransient(IServiceProvider provider) {
+ return new ImplementationTransient();
+ }
+ }
+}
+
+namespace ShouldRegisterFactoryScoped {
+ public class ScopedFactory {
+ [RegisterScopedFactory(typeof(IInterface))]
+ public static ImplementationScoped CreateImplementationTransient(IServiceProvider provider) {
+ return new ImplementationScoped();
+ }
+ }
+}
+
+namespace ShouldRegisterFactorySingleton {
+ public class SingletonFactory {
+ [RegisterSingletonFactory(typeof(IInterface))]
+ public static ImplementationSingleton CreateImplementationTransient(IServiceProvider provider) {
+ return new ImplementationSingleton();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify.Test/AutoRegisterTests.cs b/src/Dependify.Test/AutoRegisterTests.cs
new file mode 100644
index 0000000..c31bca3
--- /dev/null
+++ b/src/Dependify.Test/AutoRegisterTests.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using NUnit.Framework;
+using ShouldRegisterOneScoped;
+using ShouldRegisterOneSingleton;
+using ShouldRegisterOneTransient;
+using ShouldRegisterScoped;
+using ShouldRegisterSingleton;
+using ShouldRegisterTransient;
+
+namespace Dependify.Test {
+ [TestFixture]
+ public class RegisterAttributeTests {
+ [TestCase(nameof(ShouldRegisterTransient), typeof(ImplementationTransient), typeof(IInterface), ServiceLifetime.Transient)]
+ [TestCase(nameof(ShouldRegisterSingleton), typeof(ImplementationSingleton), typeof(IInterface), ServiceLifetime.Singleton)]
+ [TestCase(nameof(ShouldRegisterScoped), typeof(ImplementationScoped), typeof(IInterface), ServiceLifetime.Scoped)]
+ [TestCase(nameof(ShouldRegisterOneTransient), typeof(ImplementationTransientOneInterface), typeof(IInterface2), ServiceLifetime.Transient)]
+ [TestCase(nameof(ShouldRegisterOneSingleton), typeof(ImplementationSingletonOneInterface), typeof(IInterface2), ServiceLifetime.Singleton)]
+ [TestCase(nameof(ShouldRegisterOneScoped), typeof(ImplementationScopedOneInterface), typeof(IInterface2), ServiceLifetime.Scoped)]
+ public void RegisterAttribute_RegistersClass_WhenDefined(string @namespace, Type classType, Type interfaceType, ServiceLifetime serviceLifetime) {
+ IServiceCollection services = new ServiceCollection();
+ services.AutoRegister(@namespace);
+ var service = services.First();
+ Assert.AreEqual(1, services.Count);
+ Assert.AreEqual(interfaceType, service.ServiceType);
+ Assert.AreEqual(classType, service.ImplementationType);
+ Assert.AreEqual(serviceLifetime, service.Lifetime);
+ }
+
+
+ [TestCase(nameof(ShouldRegisterFactoryTransient), typeof(ImplementationTransient), typeof(IInterface), ServiceLifetime.Transient)]
+ [TestCase(nameof(ShouldRegisterFactorySingleton), typeof(ImplementationSingleton), typeof(IInterface), ServiceLifetime.Singleton)]
+ [TestCase(nameof(ShouldRegisterFactoryScoped), typeof(ImplementationScoped), typeof(IInterface), ServiceLifetime.Scoped)]
+ public void RegisterFactoryAttribute_RegistersFactoryForInterface_WhenDefined(string @namespace, Type classType, Type interfaceType, ServiceLifetime serviceLifetime) {
+ IServiceCollection services = new ServiceCollection();
+ services.AutoRegister(@namespace);
+ var service = services.First();
+ Assert.AreEqual(1, services.Count);
+ Assert.AreEqual(interfaceType, service.ServiceType);
+ Assert.NotNull(service.ImplementationFactory);
+ Assert.AreEqual(serviceLifetime, service.Lifetime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify.Test/Dependify.Test.csproj b/src/Dependify.Test/Dependify.Test.csproj
new file mode 100644
index 0000000..631e0c9
--- /dev/null
+++ b/src/Dependify.Test/Dependify.Test.csproj
@@ -0,0 +1,16 @@
+
+
+ netcoreapp2.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Dependify/Attributes/Register.cs b/src/Dependify/Attributes/Register.cs
new file mode 100644
index 0000000..5c0d1cf
--- /dev/null
+++ b/src/Dependify/Attributes/Register.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Class)]
+ public abstract class Register : Attribute {
+ public IEnumerable InterfaceTypes { get; }
+
+ protected Register() { }
+
+ protected Register(params Type[] interfaceTypes) {
+ InterfaceTypes = interfaceTypes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterFactory.cs b/src/Dependify/Attributes/RegisterFactory.cs
new file mode 100644
index 0000000..d36836b
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterFactory.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Method)]
+ public abstract class RegisterFactory : Attribute {
+ public Type ReturnType { get; }
+
+ protected RegisterFactory(Type returnType) {
+ ReturnType = returnType;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterScoped.cs b/src/Dependify/Attributes/RegisterScoped.cs
new file mode 100644
index 0000000..e91c665
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterScoped.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Class)]
+ public class RegisterScoped : Register {
+ public RegisterScoped() { }
+
+ public RegisterScoped(params Type[] interfaceTypes) : base(interfaceTypes) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterScopedFactory.cs b/src/Dependify/Attributes/RegisterScopedFactory.cs
new file mode 100644
index 0000000..9dfa737
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterScopedFactory.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RegisterScopedFactory : RegisterFactory {
+ public RegisterScopedFactory(Type returnType) : base(returnType) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterSingleton.cs b/src/Dependify/Attributes/RegisterSingleton.cs
new file mode 100644
index 0000000..e3d2a25
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterSingleton.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Class)]
+ public class RegisterSingleton : Register {
+ public RegisterSingleton() { }
+
+ public RegisterSingleton(params Type[] interfaceTypes) : base(interfaceTypes) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterSingletonFactory.cs b/src/Dependify/Attributes/RegisterSingletonFactory.cs
new file mode 100644
index 0000000..ff68f4a
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterSingletonFactory.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RegisterSingletonFactory : RegisterFactory {
+ public RegisterSingletonFactory(Type returnType) : base(returnType) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterTransient.cs b/src/Dependify/Attributes/RegisterTransient.cs
new file mode 100644
index 0000000..bbd2e07
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterTransient.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Class)]
+ public class RegisterTransient : Register {
+ public RegisterTransient() { }
+
+ public RegisterTransient(params Type[] interfaceTypes) : base(interfaceTypes) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Attributes/RegisterTransientFactory.cs b/src/Dependify/Attributes/RegisterTransientFactory.cs
new file mode 100644
index 0000000..410a157
--- /dev/null
+++ b/src/Dependify/Attributes/RegisterTransientFactory.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Dependify.Attributes {
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RegisterTransientFactory : RegisterFactory {
+ public RegisterTransientFactory(Type returnType) : base(returnType) { }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Dependify.csproj b/src/Dependify/Dependify.csproj
new file mode 100644
index 0000000..e676743
--- /dev/null
+++ b/src/Dependify/Dependify.csproj
@@ -0,0 +1,11 @@
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Dependify/IServiceCollectionExtensions.cs b/src/Dependify/IServiceCollectionExtensions.cs
new file mode 100644
index 0000000..f4df242
--- /dev/null
+++ b/src/Dependify/IServiceCollectionExtensions.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Dependify.Attributes;
+using Dependify.Utilities;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Dependify {
+ // ReSharper disable once InconsistentNaming
+ public static class IServiceCollectionExtensions {
+ public static IServiceCollection AutoRegister(this IServiceCollection services) {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ services.AddFactories(DependifyUtils.GetFactoryMethods(assemblies));
+ services.AddClasses(DependifyUtils.GetClassTypes(assemblies));
+ return services;
+ }
+
+ public static IServiceCollection AutoRegister(this IServiceCollection services, params Assembly[] assemblies) {
+ services.AddFactories(DependifyUtils.GetFactoryMethods(assemblies));
+ services.AddClasses(DependifyUtils.GetClassTypes(assemblies));
+ return services;
+ }
+
+ public static IServiceCollection AutoRegister(this IServiceCollection services, string @namespace) {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ services.AddFactories(DependifyUtils.GetFactoryMethodsFromNamespace(assemblies, @namespace));
+ services.AddClasses(DependifyUtils.GetClassTypesFromNamespace(assemblies, @namespace));
+ return services;
+ }
+
+ internal static IServiceCollection AddFactories(this IServiceCollection services, IEnumerable methodInfos) {
+ foreach (var methodInfo in methodInfos) {
+ var factoryAttribute = methodInfo.GetCustomAttributes(true).First();
+ var factoryAttributeType = factoryAttribute.GetType();
+ var factoryReturnType = factoryAttribute.ReturnType;
+
+ if (factoryAttributeType == typeof(RegisterTransientFactory))
+ services.AddTransient(factoryReturnType, DependifyUtils.GetFactoryMethod(methodInfo));
+ if (factoryAttributeType == typeof(RegisterScopedFactory))
+ services.AddScoped(factoryReturnType, DependifyUtils.GetFactoryMethod(methodInfo));
+ if (factoryAttributeType == typeof(RegisterSingletonFactory))
+ services.AddSingleton(factoryReturnType, DependifyUtils.GetFactoryMethod(methodInfo));
+ }
+ return services;
+ }
+
+ internal static void AddClasses(this IServiceCollection services, IEnumerable classTypes) {
+ foreach (var classType in classTypes) {
+ var classAttributes = classType.GetCustomAttributes(true);
+ foreach (var classAttribute in classAttributes) {
+ var registrationType = classAttribute.GetType();
+ var interfaceTypes = classAttribute.InterfaceTypes == null || !classAttribute.InterfaceTypes.Any() ? classType.GetInterfaces() : classAttribute.InterfaceTypes;
+
+ foreach (var interfaceType in interfaceTypes) {
+ if (registrationType == typeof(RegisterTransient))
+ services.AddTransient(interfaceType, classType);
+ if (registrationType == typeof(RegisterScoped))
+ services.AddScoped(interfaceType, classType);
+ if (registrationType == typeof(RegisterSingleton))
+ services.AddSingleton(interfaceType, classType);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Dependify/Utilities/DependifyUtils.cs b/src/Dependify/Utilities/DependifyUtils.cs
new file mode 100644
index 0000000..c9c8345
--- /dev/null
+++ b/src/Dependify/Utilities/DependifyUtils.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Dependify.Attributes;
+
+namespace Dependify.Utilities {
+ internal static class DependifyUtils {
+ internal static IEnumerable GetFactoryMethods(IEnumerable assemblies) {
+ return assemblies
+ .SelectMany(assembly => assembly.GetTypes())
+ .Where(type => type.IsClass)
+ .SelectMany(MethodsWithAttribute);
+ }
+
+ internal static IEnumerable GetFactoryMethodsFromNamespace(IEnumerable assemblies, string @namespace) {
+ return assemblies
+ .SelectMany(assembly => assembly.GetTypes())
+ .Where(type => type.IsClass && (type.Namespace?.StartsWith(@namespace)).GetValueOrDefault())
+ .SelectMany(MethodsWithAttribute);
+ }
+
+ private static IEnumerable MethodsWithAttribute(Type type) {
+ return type.GetMethods(BindingFlags.Static | BindingFlags.Public)
+ .Where(methodInfo => methodInfo.GetCustomAttributes(true).Any());
+ }
+
+ internal static Func GetFactoryMethod(MethodInfo factoryMethodInfo) {
+ if (!IsFactoryMethod(factoryMethodInfo))
+ throw new ArgumentException($"{factoryMethodInfo.Name} is not a factory method. ");
+ return (Func)Delegate.CreateDelegate(typeof(Func), factoryMethodInfo);
+
+ bool IsFactoryMethod(MethodInfo methodInfo) {
+ var methodParameters = methodInfo.GetParameters();
+ return methodParameters.Length == 1 && methodParameters.First().ParameterType == typeof(IServiceProvider);
+ }
+ }
+
+ internal static IEnumerable GetClassTypes(IEnumerable assemblies) {
+ return assemblies.SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsClass);
+ }
+
+ internal static IEnumerable GetClassTypesFromNamespace(IEnumerable assemblies, string @namespace) {
+ return assemblies.SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsClass && (type.Namespace?.StartsWith(@namespace)).GetValueOrDefault());
+ }
+ }
+}
\ No newline at end of file