Search code examples
c#logging.net-core.net-core-2.2ilogger

Net Core: ILogger in Startup Class: Encapsulate in Extension Method


I wrote this logging code in Net Core, and someone had mentioned "this can be encapsulated in extension method". Is this a easier way to conduct this, write it optimally in Net Core 2.2?

Startup.cs

services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddLogging(builder => builder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace));

Solution

  • While this could not be a pure stackoverflow question, I think I can provide an opinion:

    The problem with the method ConfigureServices is that you need to define all of your dependencies, for larger systems this could mean a painful long configure method, which can be really hard and boring to maintain.

    To fix this, you can simplify your startup by divide all of your configurations, to achieve this, you can use the extension methods of C#, that methods allow to "extend" a class (add more methods) without modify the source code of the class.

    Extension methods are simple, just take an static class with a static method:

    public static class CommonServicesExtension
    {
    
        public static IServiceCollection AddCommonServices(IServiceCollection services)
        {
            services.AddHttpClient();
            services.AddNodaDateTime();
            services.AddMemoryCache();
            services.AddCurrencyExchange();
            // Add and configure many things
    
            return services;
        }
    }
    

    Now you can use it like this:

    CommonServicesExtension.AddCommonServices(serviceColletion);
    

    But this is somehow ugly and hard to read, so, simply add this to your extension method:

    public static IServiceCollection AddCommonServices(this IServiceCollection services)
    

    With this change, you can call your configurations like net core does!

    services.AddCommonServices();
    

    In your configure services, this way, your configure method changes from

    public IServiceCollection ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
    
        services.AddDbContext<CoreContext>(options => options.UseMySql(Configuration.GetConnectionString("Core")));
        services.AddDbContext<CoreReadOnlyContext>(options => options.UseMySql(Configuration.GetConnectionString("CORE_READONLY")));
        services.AddDbContext<StatsContext>(options =>
            options.UseMySql(Configuration.GetConnectionString("Stats")));
    
    
        services.AddScoped<ProfileTokenGenerator>();
        services.AddTransient<MailNotificationService>();
        services.AddTransient<RegisterControllerService>();
    
        services.AddScoped<OtherClass>();
        services.AddTransient<MoreTransient>();
        services.AddTransient<ThingsService>();
    
        return services;
    }
    

    To

    public IServiceCollection ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        services.AddContexts();
        services.AddCommonServices();
        services.AddOtherServices();
        return services;
    }
    

    As you see, more clean and easier to read!