diff --git a/SampleWebApiAspNetCore/Common/DependencyInjectionConfig.cs b/SampleWebApiAspNetCore/Common/DependencyInjectionConfig.cs new file mode 100644 index 0000000..cb8a28e --- /dev/null +++ b/SampleWebApiAspNetCore/Common/DependencyInjectionConfig.cs @@ -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)) + }; + } +} diff --git a/SampleWebApiAspNetCore/Common/Interface/IScopedService.cs b/SampleWebApiAspNetCore/Common/Interface/IScopedService.cs new file mode 100644 index 0000000..7d06580 --- /dev/null +++ b/SampleWebApiAspNetCore/Common/Interface/IScopedService.cs @@ -0,0 +1,12 @@ +namespace SampleWebApiAspNetCore.Common.Interface; + +/// +/// 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. +/// +public interface IScopedService +{ +} diff --git a/SampleWebApiAspNetCore/Common/Interface/ISingletonService.cs b/SampleWebApiAspNetCore/Common/Interface/ISingletonService.cs new file mode 100644 index 0000000..eded058 --- /dev/null +++ b/SampleWebApiAspNetCore/Common/Interface/ISingletonService.cs @@ -0,0 +1,12 @@ +namespace SampleWebApiAspNetCore.Common.Interface; + +/// +/// 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. +/// +public interface ISingletonService +{ +} diff --git a/SampleWebApiAspNetCore/Common/Interface/ITransientService.cs b/SampleWebApiAspNetCore/Common/Interface/ITransientService.cs new file mode 100644 index 0000000..df04e64 --- /dev/null +++ b/SampleWebApiAspNetCore/Common/Interface/ITransientService.cs @@ -0,0 +1,13 @@ +namespace SampleWebApiAspNetCore.Common.Interface; + +/// +/// 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. +/// +public interface ITransientService +{ +} + diff --git a/SampleWebApiAspNetCore/Program.cs b/SampleWebApiAspNetCore/Program.cs index 9e890c6..87097de 100644 --- a/SampleWebApiAspNetCore/Program.cs +++ b/SampleWebApiAspNetCore/Program.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; @@ -6,6 +5,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json.Serialization; using SampleWebApiAspNetCore; +using SampleWebApiAspNetCore.Common; using SampleWebApiAspNetCore.Helpers; using SampleWebApiAspNetCore.MappingProfiles; using SampleWebApiAspNetCore.Repositories; @@ -18,7 +18,7 @@ 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(); @@ -26,8 +26,10 @@ builder.Services.AddCustomCors("AllowAllOrigins"); -builder.Services.AddSingleton(); -builder.Services.AddScoped(); +//All services of the application will be configured and registered using a generic approach by AddContracts. +builder.Services.AddContracts(); +//builder.Services.AddSingleton(); +//builder.Services.AddScoped(); builder.Services.AddScoped(typeof(ILinkService<>), typeof(LinkService<>)); builder.Services.AddTransient, ConfigureSwaggerOptions>(); @@ -63,7 +65,7 @@ }); app.SeedData(); -} +} else { app.AddProductionExceptionHandling(loggerFactory); diff --git a/SampleWebApiAspNetCore/Repositories/IFoodRepository.cs b/SampleWebApiAspNetCore/Repositories/IFoodRepository.cs index 26574b2..db76664 100644 --- a/SampleWebApiAspNetCore/Repositories/IFoodRepository.cs +++ b/SampleWebApiAspNetCore/Repositories/IFoodRepository.cs @@ -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); diff --git a/SampleWebApiAspNetCore/Services/ISeedDataService.cs b/SampleWebApiAspNetCore/Services/ISeedDataService.cs index c649c8e..a8e5874 100644 --- a/SampleWebApiAspNetCore/Services/ISeedDataService.cs +++ b/SampleWebApiAspNetCore/Services/ISeedDataService.cs @@ -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); }