Skip to content

Implementing generic dependency injection configuration for consistent and scalable service registration. #29

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
38 changes: 38 additions & 0 deletions SampleWebApiAspNetCore/Common/DependencyInjectionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using SampleWebApiAspNetCore.Common.Interface;

namespace SampleWebApiAspNetCore.Common;

internal static class DependencyInjectionConfig
{
internal static IServiceCollection AddContracts(this IServiceCollection services)
{
services.RegisterImplementations(typeof(ITransientService), ServiceLifetime.Transient);
services.RegisterImplementations(typeof(IScopedService), ServiceLifetime.Scoped);
services.RegisterImplementations(typeof(ISingletonService), ServiceLifetime.Singleton);
return services;
}
private static IServiceCollection RegisterImplementations(this IServiceCollection services, Type interfaceType, ServiceLifetime lifetime)
{
var implementationTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => interfaceType.IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
.Select(type => new { Interface = type.GetInterfaces().FirstOrDefault(), Implementation = type })
.Where(type => type.Interface != null && interfaceType.IsAssignableFrom(type.Interface));

foreach (var implementationType in implementationTypes)
services.RegisterService(implementationType.Interface!, implementationType.Implementation, lifetime);

return services;
}

private static IServiceCollection RegisterService(this IServiceCollection services, Type interfaceType, Type implementationType, ServiceLifetime lifetime)
{
return lifetime switch
{
ServiceLifetime.Transient => services.AddTransient(interfaceType, implementationType),
ServiceLifetime.Scoped => services.AddScoped(interfaceType, implementationType),
ServiceLifetime.Singleton => services.AddSingleton(interfaceType, implementationType),
_ => throw new ArgumentException("Invalid lifetime specified", nameof(lifetime))
};
}
}
12 changes: 12 additions & 0 deletions SampleWebApiAspNetCore/Common/Interface/IScopedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SampleWebApiAspNetCore.Common.Interface;

/// <summary>
/// Interface for a scoped service in dependency injection.
/// Scoped services are created once per request.
///
/// If an interface inherits this interface, it will be automatically registered
/// by a common dependency injection configuration class.
/// </summary>
public interface IScopedService
{
}
12 changes: 12 additions & 0 deletions SampleWebApiAspNetCore/Common/Interface/ISingletonService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SampleWebApiAspNetCore.Common.Interface;

/// <summary>
/// Interface for a singleton service in dependency injection.
/// Singleton services are created once and shared throughout the application.
///
/// If an interface inherits this interface, it will be automatically registered
/// by a common dependency injection configuration class.
/// </summary>
public interface ISingletonService
{
}
13 changes: 13 additions & 0 deletions SampleWebApiAspNetCore/Common/Interface/ITransientService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace SampleWebApiAspNetCore.Common.Interface;

/// <summary>
/// Interface for a transient service in dependency injection.
/// Transient services are created anew each time they are requested.
///
/// If an interface inherits this interface, it will be automatically registered
/// by a common dependency injection configuration class.
/// </summary>
public interface ITransientService
{
}

12 changes: 7 additions & 5 deletions SampleWebApiAspNetCore/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Serialization;
using SampleWebApiAspNetCore;
using SampleWebApiAspNetCore.Common;
using SampleWebApiAspNetCore.Helpers;
using SampleWebApiAspNetCore.MappingProfiles;
using SampleWebApiAspNetCore.Repositories;
Expand All @@ -18,16 +18,18 @@

builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddCustomCors("AllowAllOrigins");

builder.Services.AddSingleton<ISeedDataService, SeedDataService>();
builder.Services.AddScoped<IFoodRepository, FoodSqlRepository>();
//All services of the application will be configured and registered using a generic approach by AddContracts.
builder.Services.AddContracts();
//builder.Services.AddSingleton<ISeedDataService, SeedDataService>();
//builder.Services.AddScoped<IFoodRepository, FoodSqlRepository>();
builder.Services.AddScoped(typeof(ILinkService<>), typeof(LinkService<>));
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();

Expand Down Expand Up @@ -63,7 +65,7 @@
});

app.SeedData();
}
}
else
{
app.AddProductionExceptionHandling(loggerFactory);
Expand Down
5 changes: 3 additions & 2 deletions SampleWebApiAspNetCore/Repositories/IFoodRepository.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using SampleWebApiAspNetCore.Entities;
using SampleWebApiAspNetCore.Common.Interface;
using SampleWebApiAspNetCore.Entities;
using SampleWebApiAspNetCore.Models;

namespace SampleWebApiAspNetCore.Repositories
{
public interface IFoodRepository
public interface IFoodRepository : IScopedService
{
FoodEntity GetSingle(int id);
void Add(FoodEntity item);
Expand Down
5 changes: 3 additions & 2 deletions SampleWebApiAspNetCore/Services/ISeedDataService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using SampleWebApiAspNetCore.Repositories;
using SampleWebApiAspNetCore.Common.Interface;
using SampleWebApiAspNetCore.Repositories;

namespace SampleWebApiAspNetCore.Services
{
public interface ISeedDataService
public interface ISeedDataService : ISingletonService
{
void Initialize(FoodDbContext context);
}
Expand Down