From c74b0aa5b53ea56677e7a13db9193a5cecb6de69 Mon Sep 17 00:00:00 2001 From: Marco Tello Date: Wed, 10 Apr 2024 09:23:17 -0600 Subject: [PATCH] Migrate Autofac DI to Vanilla DI in the sample project (#723) (#728) --- .../CoreServiceExtensions.cs | 19 +++ .../DefaultCoreModule.cs | 20 --- .../AutofacInfrastructureModule.cs | 153 ------------------ .../InfrastructureServiceExtensions.cs | 58 +++++++ .../src/NimblePros.SampleToDo.Web/Program.cs | 80 ++++++--- 5 files changed, 132 insertions(+), 198 deletions(-) create mode 100644 sample/src/NimblePros.SampleToDo.Core/CoreServiceExtensions.cs delete mode 100644 sample/src/NimblePros.SampleToDo.Core/DefaultCoreModule.cs delete mode 100644 sample/src/NimblePros.SampleToDo.Infrastructure/AutofacInfrastructureModule.cs create mode 100644 sample/src/NimblePros.SampleToDo.Infrastructure/InfrastructureServiceExtensions.cs diff --git a/sample/src/NimblePros.SampleToDo.Core/CoreServiceExtensions.cs b/sample/src/NimblePros.SampleToDo.Core/CoreServiceExtensions.cs new file mode 100644 index 000000000..97219a95e --- /dev/null +++ b/sample/src/NimblePros.SampleToDo.Core/CoreServiceExtensions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NimblePros.SampleToDo.Core.Interfaces; +using NimblePros.SampleToDo.Core.Services; + +namespace NimblePros.SampleToDo.Core; + +public static class CoreServiceExtensions +{ + public static IServiceCollection AddCoreServices(this IServiceCollection services, ILogger logger) + { + services.AddScoped(); + services.AddScoped(); + + logger.LogInformation("{Project} services registered", "Core"); + + return services; + } +} diff --git a/sample/src/NimblePros.SampleToDo.Core/DefaultCoreModule.cs b/sample/src/NimblePros.SampleToDo.Core/DefaultCoreModule.cs deleted file mode 100644 index 1183e4b21..000000000 --- a/sample/src/NimblePros.SampleToDo.Core/DefaultCoreModule.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Autofac; -using NimblePros.SampleToDo.Core.Interfaces; -using NimblePros.SampleToDo.Core.Services; - -namespace NimblePros.SampleToDo.Core; - -/// -/// An Autofac module that is responsible for wiring up services defined in the Core project. -/// -public class DefaultCoreModule : Module -{ - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType() - .As().InstancePerLifetimeScope(); - - builder.RegisterType() - .As().InstancePerLifetimeScope(); - } -} diff --git a/sample/src/NimblePros.SampleToDo.Infrastructure/AutofacInfrastructureModule.cs b/sample/src/NimblePros.SampleToDo.Infrastructure/AutofacInfrastructureModule.cs deleted file mode 100644 index f56d50554..000000000 --- a/sample/src/NimblePros.SampleToDo.Infrastructure/AutofacInfrastructureModule.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System.Reflection; -using Autofac; -using NimblePros.SampleToDo.Core.Interfaces; -using NimblePros.SampleToDo.Core.ProjectAggregate; -using NimblePros.SampleToDo.Infrastructure.Data; -using Ardalis.SharedKernel; -using MediatR; -using MediatR.Pipeline; -using Module = Autofac.Module; -using NimblePros.SampleToDo.Infrastructure.Data.Queries; -using NimblePros.SampleToDo.Infrastructure.Email; -using NimblePros.SampleToDo.UseCases.Contributors.Create; -using NimblePros.SampleToDo.UseCases.Contributors.List; -using NimblePros.SampleToDo.UseCases.Projects.ListIncompleteItems; -using NimblePros.SampleToDo.UseCases.Projects.ListShallow; - -namespace NimblePros.SampleToDo.Infrastructure; - -/// -/// An Autofac module responsible for wiring up services defined in Infrastructure. -/// Mainly responsible for setting up EF and MediatR, as well as other one-off services. -/// -public class AutofacInfrastructureModule : Module -{ - private readonly bool _isDevelopment = false; - private readonly List _assemblies = []; - - public AutofacInfrastructureModule(bool isDevelopment, Assembly? callingAssembly = null) - { - _isDevelopment = isDevelopment; - AddToAssembliesIfNotNull(callingAssembly); - } - - private void AddToAssembliesIfNotNull(Assembly? assembly) - { - if(assembly != null) - { - _assemblies.Add(assembly); - } - } - - private void LoadAssemblies() - { - // TODO: Replace these types with any type in the appropriate assembly/project - var coreAssembly = Assembly.GetAssembly(typeof(Project)); - var infrastructureAssembly = Assembly.GetAssembly(typeof(AutofacInfrastructureModule)); - var useCasesAssembly = Assembly.GetAssembly(typeof(CreateContributorCommand)); - - AddToAssembliesIfNotNull(coreAssembly); - AddToAssembliesIfNotNull(infrastructureAssembly); - AddToAssembliesIfNotNull(useCasesAssembly); - } - - protected override void Load(ContainerBuilder builder) - { - LoadAssemblies(); - if (_isDevelopment) - { - RegisterDevelopmentOnlyDependencies(builder); - } - else - { - RegisterProductionOnlyDependencies(builder); - } - RegisterEF(builder); - RegisterMediatR(builder); - } - - private void RegisterEF(ContainerBuilder builder) - { - builder.RegisterGeneric(typeof(EfRepository<>)) - .As(typeof(IRepository<>)) - .As(typeof(IReadRepository<>)) - .InstancePerLifetimeScope(); - } - - private void RegisterMediatR(ContainerBuilder builder) - { - builder - .RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder - .RegisterGeneric(typeof(LoggingBehavior<,>)) - .As(typeof(IPipelineBehavior<,>)) - .InstancePerLifetimeScope(); - - builder - .RegisterType() - .As() - .InstancePerLifetimeScope(); - - var mediatrOpenTypes = new[] - { - typeof(IRequestHandler<,>), - typeof(IRequestExceptionHandler<,,>), - typeof(IRequestExceptionAction<,>), - typeof(INotificationHandler<>), - }; - - foreach (var mediatrOpenType in mediatrOpenTypes) - { - builder - .RegisterAssemblyTypes([.. _assemblies]) - .AsClosedTypesOf(mediatrOpenType) - .AsImplementedInterfaces(); - } - } - - private void RegisterDevelopmentOnlyDependencies(ContainerBuilder builder) - { - // NOTE: Add any development only services here - //builder.RegisterType().As() - // .InstancePerLifetimeScope(); - - // NOTE: Add any production only (real) services here - builder.RegisterType().As() - .InstancePerLifetimeScope(); - - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - } - - private void RegisterProductionOnlyDependencies(ContainerBuilder builder) - { - // NOTE: Add any production only (real) services here - builder.RegisterType().As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - } -} diff --git a/sample/src/NimblePros.SampleToDo.Infrastructure/InfrastructureServiceExtensions.cs b/sample/src/NimblePros.SampleToDo.Infrastructure/InfrastructureServiceExtensions.cs new file mode 100644 index 000000000..ac5f1fa23 --- /dev/null +++ b/sample/src/NimblePros.SampleToDo.Infrastructure/InfrastructureServiceExtensions.cs @@ -0,0 +1,58 @@ +using Ardalis.SharedKernel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NimblePros.SampleToDo.Core.Interfaces; +using NimblePros.SampleToDo.Infrastructure.Data; +using NimblePros.SampleToDo.Infrastructure.Data.Queries; +using NimblePros.SampleToDo.Infrastructure.Email; +using NimblePros.SampleToDo.UseCases.Contributors.List; +using NimblePros.SampleToDo.UseCases.Projects.ListIncompleteItems; +using NimblePros.SampleToDo.UseCases.Projects.ListShallow; + +namespace NimblePros.SampleToDo.Infrastructure; + +public static class InfrastructureServiceExtensions +{ + public static IServiceCollection AddInfrastructureServices( + this IServiceCollection services, + ILogger logger, + bool isDevelopment) + { + if (isDevelopment) + { + RegisterDevelopmentOnlyDependencies(services); + } + else + { + RegisterProductionOnlyDependencies(services); + } + + RegisterEF(services); + + logger.LogInformation("{Project} services registered", "Infrastructure"); + + return services; + } + + private static void RegisterDevelopmentOnlyDependencies(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + + private static void RegisterProductionOnlyDependencies(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + + private static void RegisterEF(IServiceCollection services) + { + services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); + services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); + } +} diff --git a/sample/src/NimblePros.SampleToDo.Web/Program.cs b/sample/src/NimblePros.SampleToDo.Web/Program.cs index dc1b3f111..5c59de0d7 100644 --- a/sample/src/NimblePros.SampleToDo.Web/Program.cs +++ b/sample/src/NimblePros.SampleToDo.Web/Program.cs @@ -1,6 +1,6 @@ -using Ardalis.ListStartupServices; -using Autofac; -using Autofac.Extensions.DependencyInjection; +using System.Reflection; +using Ardalis.ListStartupServices; +using Ardalis.SharedKernel; using NimblePros.SampleToDo.Core; using NimblePros.SampleToDo.Infrastructure; using NimblePros.SampleToDo.Infrastructure.Data; @@ -8,14 +8,25 @@ using FastEndpoints; using FastEndpoints.Swagger; using FastEndpoints.ApiExplorer; +using MediatR; using Serilog; using Microsoft.EntityFrameworkCore; +using NimblePros.SampleToDo.Core.ProjectAggregate; +using NimblePros.SampleToDo.UseCases.Contributors.Create; +using Serilog.Extensions.Logging; -var builder = WebApplication.CreateBuilder(args); +var logger = Log.Logger = new LoggerConfiguration() + .Enrich.FromLogContext() + .WriteTo.Console() + .CreateLogger(); + +logger.Information("Starting web host"); -builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog((_, config) => config.ReadFrom.Configuration(builder.Configuration)); +var microsoftLogger = new SerilogLoggerFactory(logger) + .CreateLogger(); builder.Services.Configure(options => { @@ -43,6 +54,11 @@ // c.OperationFilter(); //}); +builder.Services.AddCoreServices(microsoftLogger); +builder.Services.AddInfrastructureServices(microsoftLogger, builder.Environment.IsDevelopment()); + +ConfigureMediatR(); + // add list services for diagnostic purposes - see https://github.com/ardalis/AspNetCoreStartupServices builder.Services.Configure(config => { @@ -53,12 +69,6 @@ }); -builder.Host.ConfigureContainer(containerBuilder => -{ - containerBuilder.RegisterModule(new DefaultCoreModule()); - containerBuilder.RegisterModule(new AutofacInfrastructureModule(builder.Environment.IsDevelopment())); -}); - var app = builder.Build(); if (app.Environment.IsDevelopment()) @@ -80,26 +90,46 @@ //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1")); // Seed Database -using (var scope = app.Services.CreateScope()) -{ - var services = scope.ServiceProvider; +SeedDatabase(app); - try +app.Run(); + + +void ConfigureMediatR() +{ + var mediatRAssemblies = new[] { - var context = services.GetRequiredService(); - // context.Database.Migrate(); - context.Database.EnsureCreated(); - SeedData.Initialize(services); - } - catch (Exception ex) + Assembly.GetAssembly(typeof(Project)), // Core + Assembly.GetAssembly(typeof(CreateContributorCommand)), // UseCases + Assembly.GetAssembly(typeof(InfrastructureServiceExtensions)) // Infrastructure + }; + + builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(mediatRAssemblies!)); + builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); + builder.Services.AddScoped(); +} + +void SeedDatabase(WebApplication app) +{ + using (var scope = app.Services.CreateScope()) { - var logger = services.GetRequiredService>(); - logger.LogError(ex, "An error occurred seeding the DB. {exceptionMessage}", ex.Message); + var services = scope.ServiceProvider; + + try + { + var context = services.GetRequiredService(); + // context.Database.Migrate(); + context.Database.EnsureCreated(); + SeedData.Initialize(services); + } + catch (Exception ex) + { + var logger = services.GetRequiredService>(); + logger.LogError(ex, "An error occurred seeding the DB. {exceptionMessage}", ex.Message); + } } } -app.Run(); - // Make the implicit Program.cs class public, so integration tests can reference the correct assembly for host building public partial class Program {