Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improve performance #22

Merged
merged 3 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Bindicate/Bindicate.csproj
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 @@
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 @@
/// <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 @@
/// <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
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading