.NET Core DI i podklasy

Nov 21 2020

Nowość w .NET Core tutaj. Szukałem innej dokumentacji, wątku lub przewodnika, który zawiera odpowiedzi na moje pytania, ale nie mogę go znaleźć. Jeśli sądzisz, że tak, wskaż go.

Próbuję stworzyć prostą aplikację konsoli .NET 5 z DI i dosłownie utknąłem w tworzeniu struktury klas z rejestrowaniem.

  1. Czy jest to właściwy sposób przekazywania programu rejestrującego (lub dowolnej innej usługi) do klasy podrzędnej przy użyciu funkcji DI w .NET Core? Zgodnie z poniższym kodem, w moim konstruktorze klasy nadrzędnej biorę dodatkowy ILogger dla każdej podklasy, np. ILogger<SubClass>?

    public TestClass(ILogger<TestClass> log, ILogger<SubClass> subClassLog, IConfiguration config)
    
  2. Jak zainicjować rejestrator w moim procesie statycznym StaticProc?

    public static async Task<bool> StaticProc()
    

Program.cs:

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System;
    using System.IO;
    using System.Threading.Tasks;
    
    przestrzeń nazw ConsoleApp1
    {
        Program zajęć
        {
            statyczne asynchroniczne zadanie główne (ciąg [] argumentów)
            {
                var builder = new ConfigurationBuilder ();
                BuildConfig (konstruktor);
    
                var host = Host.CreateDefaultBuilder ()
                    .ConfigureServices ((kontekst, usługi) =>
                    {
                        services.AddTransient <ITestClass, TestClass> ();
                        services.AddTransient <ISubClass, SubClass> ();
                    })
                    .ConfigureLogging (logBuilder =>
                    {
                        logBuilder.SetMinimumLevel (LogLevel.Trace);
                        logBuilder.AddLog4Net ("log4net.config");
                    })
                    .Budować();
    
                var log = host.Services.GetService <ILoggerFactory> () .CreateLogger <Program> ();
    
                log.LogInformation ($"Application Started");
    
                var svc = ActivatorUtilities.CreateInstance<TestClass>(host.Services);
                await svc.Run();
    
                log.LogInformation($„Aplikacja zakończona”);
            }
    
            static void BuildConfig (konstruktor IConfigurationBuilder)
            {
                builder.SetBasePath (Directory.GetCurrentDirectory ())
                    .AddJsonFile ("appsettings.json", opcjonalnie: false, reloadOnChange: true)
                    .AddJsonFile ($ "appsettings. {Environment.GetEnvironmentVariable (" ASPNETCORE_ENVIRONMENT ") ??" Development "}. Json", opcjonalnie: true)
                    .AddEnvironmentVariables ();
            }
        }
    }

TestClass.cs:

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    using System.Threading.Tasks;
    
    przestrzeń nazw ConsoleApp1
    {
        public class TestClass: ITestClass
        {
            prywatny tylko do odczytu ILogger <TestClass> _log;
            private readonly ILogger <SubClass> _subClassLog;
            private readonly IConfiguration _config;
    
            public TestClass (dziennik ILogger <TestClass>, ILogger <SubClass> subClassLog, konfiguracja IConfiguration)
            {
                _log = log;
                _subClassLog = subClassLog;
                _config = config;
            }
    
            public async Task Run ()
            {
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogDebug ("Debugowanie w pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogInformation ("Informacje o pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogWarning ("Ostrzeżenie o pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogError ("Błąd pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogCritical ("Pętla krytyczna {loopNumber}", i);
    
                var subClass = new SubClass (_subClassLog, _config);
                await subClass.AnotherProc ();
    
                await SubClass.StaticProc ();
            }
        }
    }

SubClass.cs:

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Threading.Tasks;
    
    przestrzeń nazw ConsoleApp1
    {
        klasa publiczna SubClass: ISubClass
        {
            prywatny tylko do odczytu ILogger <SubClass> _log;
            private readonly IConfiguration _config;
    
            public SubClass (dziennik ILogger <SubClass>, konfiguracja IConfiguration)
            {
                _log = log;
                _config = config;
            }
    
            publiczne zadanie asynchroniczne AnotherProc ()
            {
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogDebug ("Debugowanie w pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogInformation ("Informacje o pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogWarning ("Ostrzeżenie o pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogError ("Błąd pętli {loopNumber}", i);
    
                for (int i = 0; i <_config.GetValue <int> ("Loop"); i ++)
                    _log.LogCritical ("Pętla krytyczna {loopNumber}", i);
            }
    
            public static async Task <bool> StaticProc ()
            {
                var returnBool = true;
    
                próbować
                {
                    zgłoś nowy wyjątek („”);
                }
                catch (wyjątek z wyjątkiem)
                {
                    returnBool = false;
    
                    // Brak instancji, więc brak wyjątku _log.
                    // Jak stworzyć samodzielny ILogger?
                }
    
                return returnBool;
            }
        }
    }

appsettings.json:

    {
      „Pętla”: 15
    }

log4net.config:

    <? xml version = "1.0" encoding = "utf-8"?>
    <log4net>
        <appender name = "Info" type = "log4net.Appender.RollingFileAppender">
            <wartość progowa = "DEBUG" />
            <parametr name = "File" value = "App_Data \\ Log \\ Info.log" />
            <param name = "AppendToFile" value = "true" />
            <maximumFileSize value = "5120 KB" />
            <lockModel type = "log4net.Appender.FileAppender + MinimalLock" />
            <maxSizeRollBackups value = "1000" />
            <layout type = "log4net.Layout.PatternLayout">
                <conversionPattern value = "% d {yyyy-MM-dd HH: mm: ss} - [% t]% -5p% c% x -% m% n" />
            </layout>
        </appender>
        <appender name = "Error" type = "log4net.Appender.RollingFileAppender">
            <wartość progowa = "Błąd" />
            <param name = "File" value = "App_Data \\ Log \\ Error.log" />
            <param name = "AppendToFile" value = "true" />
            <maximumFileSize value = "5120 KB" />
            <lockModel type = "log4net.Appender.FileAppender + MinimalLock" />
            <maxSizeRollBackups value = "1000" />
            <layout type = "log4net.Layout.PatternLayout">
                <conversionPattern value = "% d {yyyy-MM-dd HH: mm: ss} - [% t]% -5p% c% x -% m% n" />
            </layout>
        </appender>
        <root>
            <appender-ref ref = "Info" />
            <appender-ref ref = "Błąd" />
        </root>
    </log4net>

Odpowiedzi

Martin Nov 21 2020 at 00:13

Podsumowanie

Możesz również wstrzyknąć ILoggerFactoryzamiast ILogger:

public TestClass(ILoggerFactory loggerFactory, IConfiguration config)
{
     // create a class logger
     _log = loggerFactory.CreateLogger<TestClass>();

     // and whenever you need a new instance of a special class logger use this:
     _subClassLog = loggerFactory.Create<SubTestClass>();
     _config = config;
}

Należy jednak pamiętać, że powoduje to utworzenie nowego wystąpienia ILogger. Logowanie do asp net core W twoim przypadku, jeśli naprawdę potrzebujesz metody statycznej z loggerem, utwórz ją tylko wtedy, gdy już istnieje:

private static readonly ILogger<SubClass> _log;
private readonly IConfiguration _config;

public SubClass(ILoggerFactory loggerFactory, IConfiguration config)
{
     _log = _log ??= loggerFactory.CreateLogger<SubClass>();
     _config = config;
}

Ale wolę lub lepiej doradzę, abyś zrobił to bez statyki i po prostu zarejestrował usługę jako singleton.

Przykład 1

Dodałem tutaj pełny przykład: dotnet fiddle Przykład 1 Również z działającym DI dla aplikacji konsolowych, jak wspomniano w komentarzach.

Przykład 2

Imo, nie powinieneś go używać z metodami statycznymi. Spójrz tylko na mój drugi przykład tutaj skrzypce dotnet Przykład 2