Skip to content

Commit

Permalink
Merge pull request #22 from Tim-Maes/feature/improve-performance
Browse files Browse the repository at this point in the history
Feature/improve performance
  • Loading branch information
Tim-Maes committed Jan 3, 2024
2 parents 2fa8c90 + 06e6cc4 commit 0ce62bc
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 53 deletions.
2 changes: 1 addition & 1 deletion src/Bindicate/Bindicate.csproj
Expand Up @@ -13,7 +13,7 @@
<PackageTags>di, ioc, service, collection, extensions, attribute</PackageTags>
<PackageReleaseNotes>Add support for IOptions</PackageReleaseNotes>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<Version>1.5</Version>
<Version>1.5.1</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
134 changes: 82 additions & 52 deletions src/Bindicate/Configuration/AutowiringBuilder.cs
Expand Up @@ -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;
Expand All @@ -14,63 +15,83 @@ namespace Bindicate;
public class AutowiringBuilder
{
private IServiceCollection _services { get; }
private readonly List<TypeMetadata> _typeMetadatas;

private Assembly _targetAssembly { get; }

public AutowiringBuilder(IServiceCollection services, Assembly targetAssembly)
{
_services = services;
_targetAssembly = targetAssembly;
_typeMetadatas = ScanAssembly(targetAssembly);

AddAutowiringForAssembly();
}
private List<TypeMetadata> ScanAssembly(Assembly assembly)
{
var typeMetadatas = new List<TypeMetadata>();

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;
}

/// <summary>
/// Scans the assembly to automatically wire up services based on the attributes.
/// </summary>
/// <returns>A reference to this instance after the operation has completed.</returns>
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<BaseServiceAttribute>();

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<BaseServiceAttribute>();

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}");
}
}
}
}
Expand All @@ -86,24 +107,28 @@ public AutowiringBuilder AddAutowiringForAssembly()
/// <returns>A reference to this instance after the operation has completed.</returns>
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<RegisterOptionsAttribute>();

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);

Check warning on line 129 in src/Bindicate/Configuration/AutowiringBuilder.cs

View workflow job for this annotation

GitHub Actions / run_test

Dereference of a possibly null reference.

Check warning on line 129 in src/Bindicate/Configuration/AutowiringBuilder.cs

View workflow job for this annotation

GitHub Actions / create_nuget

Dereference of a possibly null reference.
specializedMethod.Invoke(null, new object[] { _services, configSection });
}
}
}

Expand All @@ -116,19 +141,24 @@ public AutowiringBuilder WithOptions(IConfiguration configuration)
/// <returns>A reference to this instance after the operation has completed.</returns>
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<BaseKeyedServiceAttribute>();

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);
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions 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;
}
}

0 comments on commit 0ce62bc

Please sign in to comment.