diff --git a/src/Bindicate/Bindicate.csproj b/src/Bindicate/Bindicate.csproj index 1463f87..933c4f3 100644 --- a/src/Bindicate/Bindicate.csproj +++ b/src/Bindicate/Bindicate.csproj @@ -13,7 +13,7 @@ di, ioc, service, collection, extensions, attribute Add support for IOptions LICENSE.txt - 1.5 + 1.5.1 diff --git a/src/Bindicate/Configuration/AutowiringBuilder.cs b/src/Bindicate/Configuration/AutowiringBuilder.cs index 02db74e..bf5cf7a 100644 --- a/src/Bindicate/Configuration/AutowiringBuilder.cs +++ b/src/Bindicate/Configuration/AutowiringBuilder.cs @@ -3,6 +3,7 @@ using Bindicate.Attributes.Scoped; using Bindicate.Attributes.Singleton; using Bindicate.Attributes.Transient; +using Bindicate.Configuration; using Bindicate.Lifetime; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -14,16 +15,32 @@ namespace Bindicate; public class AutowiringBuilder { private IServiceCollection _services { get; } + private readonly List _typeMetadatas; - private Assembly _targetAssembly { get; } public AutowiringBuilder(IServiceCollection services, Assembly targetAssembly) { _services = services; - _targetAssembly = targetAssembly; + _typeMetadatas = ScanAssembly(targetAssembly); AddAutowiringForAssembly(); } + private List ScanAssembly(Assembly assembly) + { + var typeMetadatas = new List(); + + foreach (var type in assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract)) + { + var hasRegisterOptionsAttribute = type.GetCustomAttributes(typeof(RegisterOptionsAttribute), false).Any(); + var hasBaseServiceAttribute = type.GetCustomAttributes(typeof(BaseServiceAttribute), false).Any(); + var hasBaseKeyedServiceAttribute = type.GetCustomAttributes(typeof(BaseKeyedServiceAttribute), false).Any(); + + var typeMetadata = new TypeMetadata(type, hasRegisterOptionsAttribute, hasBaseServiceAttribute, hasBaseKeyedServiceAttribute); + typeMetadatas.Add(typeMetadata); + } + + return typeMetadatas; + } /// /// Scans the assembly to automatically wire up services based on the attributes. @@ -31,46 +48,50 @@ public AutowiringBuilder(IServiceCollection services, Assembly targetAssembly) /// A reference to this instance after the operation has completed. public AutowiringBuilder AddAutowiringForAssembly() { - foreach (var type in _targetAssembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract)) + foreach (var typeMetadata in _typeMetadatas) { - var registerAttributes = type.GetCustomAttributes(typeof(BaseServiceAttribute), false) - .Cast(); - - foreach (var attr in registerAttributes) + if (typeMetadata.HasBaseServiceAttribute) { - var serviceType = attr.ServiceType ?? type; - var registrationMethod = GetRegistrationMethod(_services, attr.Lifetime); + var type = typeMetadata.Type; + var registerAttributes = type.GetCustomAttributes(typeof(BaseServiceAttribute), false) + .Cast(); - if (serviceType.IsDefined(typeof(RegisterGenericInterfaceAttribute), false)) + foreach (var attr in registerAttributes) { - if (serviceType.IsGenericType) - { - _services.Add(ServiceDescriptor.Describe( - serviceType.GetGenericTypeDefinition(), - type.GetGenericTypeDefinition(), - attr.Lifetime.ConvertToServiceLifetime()) - ); - } - else + var serviceType = attr.ServiceType ?? type; + var registrationMethod = GetRegistrationMethod(_services, attr.Lifetime); + + if (serviceType.IsDefined(typeof(RegisterGenericInterfaceAttribute), false)) { - // Handle non-generic services with generic interfaces - foreach (var iface in type.GetInterfaces()) + if (serviceType.IsGenericType) { - if (iface.IsGenericType && iface.GetGenericTypeDefinition().IsDefined(typeof(RegisterGenericInterfaceAttribute), false)) + _services.Add(ServiceDescriptor.Describe( + serviceType.GetGenericTypeDefinition(), + type.GetGenericTypeDefinition(), + attr.Lifetime.ConvertToServiceLifetime()) + ); + } + else + { + // Handle non-generic services with generic interfaces + foreach (var iface in type.GetInterfaces()) { - var genericInterface = iface.GetGenericTypeDefinition(); - _services.Add(ServiceDescriptor.Describe(genericInterface, type, attr.Lifetime.ConvertToServiceLifetime())); + if (iface.IsGenericType && iface.GetGenericTypeDefinition().IsDefined(typeof(RegisterGenericInterfaceAttribute), false)) + { + var genericInterface = iface.GetGenericTypeDefinition(); + _services.Add(ServiceDescriptor.Describe(genericInterface, type, attr.Lifetime.ConvertToServiceLifetime())); + } } } } - } - else if (type.GetInterfaces().Contains(serviceType) || type == serviceType) - { - RegisterService(serviceType, type, registrationMethod); - } - else - { - throw new InvalidOperationException($"Type {type.FullName} does not implement {serviceType.FullName}"); + else if (type.GetInterfaces().Contains(serviceType) || type == serviceType) + { + RegisterService(serviceType, type, registrationMethod); + } + else + { + throw new InvalidOperationException($"Type {type.FullName} does not implement {serviceType.FullName}"); + } } } } @@ -86,24 +107,28 @@ public AutowiringBuilder AddAutowiringForAssembly() /// A reference to this instance after the operation has completed. public AutowiringBuilder WithOptions(IConfiguration configuration) { - foreach (var type in _targetAssembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract)) + foreach (var typeMetadata in _typeMetadatas) { - var optionAttributes = type.GetCustomAttributes(typeof(RegisterOptionsAttribute), false) + if (typeMetadata.HasRegisterOptionsAttribute) + { + var type = typeMetadata.Type; + var optionAttributes = type.GetCustomAttributes(typeof(RegisterOptionsAttribute), false) .Cast(); - foreach (var attr in optionAttributes) - { - var configSection = configuration.GetSection(attr.ConfigurationSection); + foreach (var attr in optionAttributes) + { + var configSection = configuration.GetSection(attr.ConfigurationSection); - if (!configSection.Exists()) - throw new InvalidOperationException($"Missing configuration section: {attr.ConfigurationSection}"); + if (!configSection.Exists()) + throw new InvalidOperationException($"Missing configuration section: {attr.ConfigurationSection}"); - var genericOptionsConfigureMethod = typeof(OptionsConfigurationServiceCollectionExtensions) - .GetMethods() - .FirstOrDefault(m => m.Name == "Configure" && m.GetParameters().Length == 2); + var genericOptionsConfigureMethod = typeof(OptionsConfigurationServiceCollectionExtensions) + .GetMethods() + .FirstOrDefault(m => m.Name == "Configure" && m.GetParameters().Length == 2); - var specializedMethod = genericOptionsConfigureMethod.MakeGenericMethod(type); - specializedMethod.Invoke(null, new object[] { _services, configSection }); + var specializedMethod = genericOptionsConfigureMethod.MakeGenericMethod(type); + specializedMethod.Invoke(null, new object[] { _services, configSection }); + } } } @@ -116,19 +141,24 @@ public AutowiringBuilder WithOptions(IConfiguration configuration) /// A reference to this instance after the operation has completed. public AutowiringBuilder ForKeyedServices() { - foreach (var type in _targetAssembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract)) + foreach (var typeMetadata in _typeMetadatas) { - var keyedAttributes = type.GetCustomAttributes(typeof(BaseKeyedServiceAttribute), false) + if (typeMetadata.HasBaseKeyedServiceAttribute) + { + var type = typeMetadata.Type; + + var keyedAttributes = type.GetCustomAttributes(typeof(BaseKeyedServiceAttribute), false) .Cast(); - foreach (var attr in keyedAttributes) - { - var serviceType = attr.ServiceType ?? type; - var key = attr.Key; + foreach (var attr in keyedAttributes) + { + var serviceType = attr.ServiceType ?? type; + var key = attr.Key; - var registrationMethod = GetKeyedRegistrationMethod(_services, attr); + var registrationMethod = GetKeyedRegistrationMethod(_services, attr); - registrationMethod(serviceType, key, type); + registrationMethod(serviceType, key, type); + } } } diff --git a/src/Bindicate/Configuration/TypeMetadata.cs b/src/Bindicate/Configuration/TypeMetadata.cs new file mode 100644 index 0000000..48d5e81 --- /dev/null +++ b/src/Bindicate/Configuration/TypeMetadata.cs @@ -0,0 +1,17 @@ +namespace Bindicate.Configuration; + +public class TypeMetadata +{ + public Type Type { get; } + public bool HasRegisterOptionsAttribute { get; } + public bool HasBaseServiceAttribute { get; } + public bool HasBaseKeyedServiceAttribute { get; } + + public TypeMetadata(Type type, bool hasRegisterOptionsAttribute, bool hasBaseServiceAttribute, bool hasBaseKeyedServiceAttribute) + { + Type = type; + HasRegisterOptionsAttribute = hasRegisterOptionsAttribute; + HasBaseServiceAttribute = hasBaseServiceAttribute; + HasBaseKeyedServiceAttribute = hasBaseKeyedServiceAttribute; + } +} \ No newline at end of file