Skip to content

Commit

Permalink
Refactor to improve named service registration
Browse files Browse the repository at this point in the history
- remove need to call CollateNamed before building container.
  • Loading branch information
dazinator committed Jun 9, 2021
1 parent f7bba48 commit b399e63
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 354 deletions.
32 changes: 16 additions & 16 deletions src/DependencyInjection.NamedServices/NamedServiceDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ namespace Dazinator.Extensions.DependencyInjection
using System;
using Microsoft.Extensions.DependencyInjection;

public class NamedServiceDescriptor : ServiceDescriptor
{
public NamedServiceDescriptor(string name, Type serviceType, object instance) : base(serviceType, instance)
{
Name = name;
}
//public class NamedServiceDescriptor : ServiceDescriptor
//{
// public NamedServiceDescriptor(string name, Type serviceType, object instance) : base(serviceType, instance)
// {
// Name = name;
// }

public NamedServiceDescriptor(string name, Type serviceType, Type implementationType, ServiceLifetime lifetime) : base(serviceType, implementationType, lifetime)
{
Name = name;
}
// public NamedServiceDescriptor(string name, Type serviceType, Type implementationType, ServiceLifetime lifetime) : base(serviceType, implementationType, lifetime)
// {
// Name = name;
// }

public NamedServiceDescriptor(string name, Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime) : base(serviceType, factory, lifetime)
{
Name = name;
}
// public NamedServiceDescriptor(string name, Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime) : base(serviceType, factory, lifetime)
// {
// Name = name;
// }

public string Name { get; private set; }
// public string Name { get; private set; }

}
//}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace Dazinator.Extensions.DependencyInjection
using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// A utility class that can be used to constrain named service registrations to a particular service type of <typeparamref name="TService"/>.
/// A utility class that can be used to constrain named service registrations to a particular service type
/// of <typeparamref name="TService"/>.
/// </summary>
/// <typeparam name="TService"></typeparam>
public class NamedServiceRegistrationsBuilder<TService>
Expand Down Expand Up @@ -44,6 +45,7 @@ public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, TSer
/// <param name="name">The name for the job type.</param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddSingleton<TImplementationType>(string name)
where TImplementationType : TService
{
Services.AddSingleton<TService, TImplementationType>(name);
return this;
Expand All @@ -59,7 +61,7 @@ public NamedServiceRegistrationsBuilder<TService> AddSingleton<TImplementationTy
public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, Type implementationType)
{
// todo: use generic overload once dependnecy updated.
Services.AddSingleton(name, typeof(IJob), implementationType);
Services.AddSingleton<TService>(name, implementationType);
return this;
}

Expand All @@ -75,9 +77,9 @@ public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, Type
/// <param name="name">The name for the job type.</param>
/// <param name="factory"></param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, Func<IServiceProvider, IJob> factory)
public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, Func<IServiceProvider, TService> factory)
{
Services.AddSingleton<IJob>(name, (sp) => factory(sp));
Services.AddSingleton<TService>(name, (sp) => factory(sp));
return this;
}

Expand All @@ -98,8 +100,9 @@ public NamedServiceRegistrationsBuilder<TService> AddSingleton(string name, Func
/// <param name="name">The name for the job type.</param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddScoped<TImplementationType>(string name)
where TImplementationType : TService
{
Services.AddScoped<IJob, TImplementationType>(name);
Services.AddScoped<TService, TImplementationType>(name);
return this;
}

Expand All @@ -112,7 +115,7 @@ public NamedServiceRegistrationsBuilder<TService> AddScoped<TImplementationType>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddScoped(string name, Type implementationType)
{
Services.AddScoped(name, typeof(IJob), implementationType);
Services.AddScoped<TService>(name, implementationType);
return this;

}
Expand All @@ -129,9 +132,9 @@ public NamedServiceRegistrationsBuilder<TService> AddScoped(string name, Type im
/// <param name="name">The name for the named service registration.</param>
/// <param name="factory">Factory method that returns implementation instance.</param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddScoped(string name, Func<IServiceProvider, IJob> factory)
public NamedServiceRegistrationsBuilder<TService> AddScoped(string name, Func<IServiceProvider, TService> factory)
{
Services.AddScoped<IJob>(name, (sp) => factory(sp));
Services.AddScoped<TService>(name, (sp) => factory(sp));
return this;
}

Expand All @@ -151,8 +154,9 @@ public NamedServiceRegistrationsBuilder<TService> AddScoped(string name, Func<IS
/// <param name="name">The name for the job type.</param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddTransient<TImplementationType>(string name)
where TImplementationType : TService
{
Services.AddTransient<IJob, TImplementationType>(name);
Services.AddTransient<TService, TImplementationType>(name);
return this;

}
Expand All @@ -166,7 +170,7 @@ public NamedServiceRegistrationsBuilder<TService> AddTransient<TImplementationTy
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddTransient(string name, Type implementationType)
{
Services.AddTransient(name, typeof(IJob), implementationType);
Services.AddTransient<TService>(name, implementationType);
return this;
}

Expand All @@ -182,20 +186,15 @@ public NamedServiceRegistrationsBuilder<TService> AddTransient(string name, Type
/// <param name="name">The name for the named service registration.</param>
/// <param name="factory">Factory method that returns implementation instance.</param>
/// <returns></returns>
public NamedServiceRegistrationsBuilder<TService> AddTransient(string name, Func<IServiceProvider, IJob> factory)
public NamedServiceRegistrationsBuilder<TService> AddTransient(string name, Func<IServiceProvider, TService> factory)
{
Services.AddTransient<IJob>(name, (sp) => factory(sp));
Services.AddTransient<TService>(name, (sp) => factory(sp));
return this;
}

#endregion

#endregion

/// <summary>
/// Must call this before the container is built.
/// </summary>
public IServiceCollection Build() => Services.CollateNamed<TService>();
#endregion

}

Expand Down
62 changes: 15 additions & 47 deletions src/DependencyInjection.NamedServices/NamedServiceRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ public NamedServiceRegistry(IServiceCollection services = null)
_namedRegistrations = new Dictionary<string, NamedServiceRegistration<TService>>();
_services = services;
_namedRegistrationFactory = new NamedServiceRegistrationFactory<TService>(GetServiceProvider);
// hydrate the registry from any existing named registrations in the IServiceCollection
LoadFromServices(services);
}

public IServiceProvider ServiceProvider { get; set; }
Expand Down Expand Up @@ -208,6 +206,8 @@ public void Add(ServiceLifetime lifetime, string name, Func<IServiceProvider, TS
public void AddSingleton<TConcreteType>()
where TConcreteType : TService => AddSingleton<TConcreteType>(string.Empty);

public void AddSingleton(Type implementationType) => AddSingleton(string.Empty, implementationType);

public void AddSingleton(Func<IServiceProvider, TService> factoryFunc) => AddSingleton(string.Empty, factoryFunc);

public void AddSingleton<TImplementationType>(Func<IServiceProvider, TImplementationType> factoryFunc)
Expand All @@ -220,6 +220,8 @@ public void AddSingleton<TImplementationType>(Func<IServiceProvider, TImplementa
public void AddSingleton<TConcreteType>(string name)
where TConcreteType : TService => _ = AddRegistration(name, typeof(TConcreteType), ServiceLifetime.Singleton);

public void AddSingleton(string name, Type implementationType) => AddRegistration(name, implementationType, ServiceLifetime.Singleton);

public void AddSingleton(string name, Func<IServiceProvider, TService> factoryFunc) => _ = AddRegistration(name, factoryFunc, ServiceLifetime.Singleton);

private IServiceProvider GetServiceProvider()
Expand All @@ -242,6 +244,9 @@ public void AddSingleton<TImplementationType>(string name, Func<IServiceProvider
/// <param name="registrationOwnsInstance">true means if this instance implements IDisposable, Dispose will be called on this instance when the underlying registration is Disposed. false means you take care of disposal of this instance using your own mechanism, or perhaps its managed by a container already etc.</param>
public void AddSingleton(string name, TService instance, bool registrationOwnsInstance = false) => _ = AddRegistration(name, instance, registrationOwnsInstance);




#endregion

#region Transient
Expand All @@ -253,6 +258,8 @@ public void AddSingleton<TImplementationType>(string name, Func<IServiceProvider
public void AddTransient<TConcreteType>()
where TConcreteType : TService => AddTransient<TConcreteType>(string.Empty);

public void AddTransient(Type implementationType) => AddTransient(string.Empty, implementationType);

public void AddTransient(Func<IServiceProvider, TService> factoryFunc) => AddTransient(string.Empty, factoryFunc);

public void AddTransient<TImplementationType>(Func<IServiceProvider, TImplementationType> factoryFunc)
Expand All @@ -265,6 +272,8 @@ public void AddTransient<TImplementationType>(Func<IServiceProvider, TImplementa
public void AddTransient<TConcreteType>(string name)
where TConcreteType : TService => _ = AddRegistration(name, typeof(TConcreteType), ServiceLifetime.Transient);

public void AddTransient(string name, Type implementationType) => AddRegistration(name, implementationType, ServiceLifetime.Transient);

public void AddTransient(string name, Func<IServiceProvider, TService> factoryFunc) => _ = AddRegistration(name, factoryFunc, ServiceLifetime.Transient);

public void AddTransient<TImplementationType>(string name, Func<IServiceProvider, TImplementationType> factoryFunc)
Expand All @@ -280,6 +289,8 @@ public void AddTransient<TImplementationType>(string name, Func<IServiceProvider
public void AddScoped<TConcreteType>()
where TConcreteType : TService => AddScoped<TConcreteType>(string.Empty);

public void AddScoped(Type implementationType) => AddScoped(string.Empty, implementationType);

public void AddScoped(Func<IServiceProvider, TService> factoryFunc) => AddScoped(string.Empty, factoryFunc);

public void AddScoped<TImplementationType>(Func<IServiceProvider, TImplementationType> factoryFunc)
Expand All @@ -292,6 +303,8 @@ public void AddScoped<TImplementationType>(Func<IServiceProvider, TImplementatio
public void AddScoped<TConcreteType>(string name)
where TConcreteType : TService => _ = AddRegistration(name, typeof(TConcreteType), ServiceLifetime.Scoped);

public void AddScoped(string name, Type implementationType) => AddRegistration(name, implementationType, ServiceLifetime.Scoped);

public void AddScoped(string name, Func<IServiceProvider, TService> factoryFunc) => _ = AddRegistration(name, factoryFunc, ServiceLifetime.Scoped);

public void AddScoped<TImplementationType>(string name, Func<IServiceProvider, TImplementationType> factoryFunc)
Expand Down Expand Up @@ -381,51 +394,6 @@ private void AddServiceDescriptor(ServiceLifetime lifetime, Type implementationT

}


/// <summary>
/// Load existing named service registrations in the IServiceCollection, into this registry, removing them in the process from the IServiceCollection.
/// </summary>
public void LoadFromServices(IServiceCollection services)
{
if (services == null)
{
return;
}

var namedDescriptorType = typeof(NamedServiceDescriptor);
var namedServiceRegistrations = services.Where(s => s.GetType() == namedDescriptorType && s.ServiceType == typeof(TService)).Cast<NamedServiceDescriptor>().ToList();

foreach (var item in namedServiceRegistrations)
{
//Todo: don't like having to do casts here..
if (!_services.Remove(item))
{
throw new InvalidOperationException($"Could not remove registration {item?.Name} from service collection when promoting to named service registry.");
}

if (item.ImplementationInstance != null)
{
AddRegistration(item.Name, (TService)item.ImplementationInstance, false);
continue;
}

if (item.ImplementationFactory != null)
{
AddRegistration(item.Name, (sp) => (TService)item.ImplementationFactory(sp), item.Lifetime);
continue;
}

if (item.ImplementationType != null)
{
AddRegistration(item.Name, item.ImplementationType, item.Lifetime);
continue;
}

throw new InvalidOperationException($"Could not process named registration {item.Name}");
}
}


#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls

Expand Down
Loading

0 comments on commit b399e63

Please sign in to comment.