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