Das Aufrufen von 'BuildServiceProvider' aus dem Anwendungscode führt zu einer Kopie der Singleton-Warnung. Wie vermeide ich das?

8

Ich habe gerade die 4 Zeilen am Ende eines anderen Projekts eingefügt und es funktioniert, aber ich bekomme eine Warnung. Ich verstehe DI eindeutig nicht gut genug. Was soll ich ändern?

  public void ConfigureServices(IServiceCollection services)
        {
            if (HostingEnvironment.EnvironmentName == "Local")
            {
                services.AddHealthChecksUI()
               .AddHealthChecks()
               .AddCheck<TestWebApiControllerHealthCheck>("HomePageHealthCheck")
               .AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
            }

        services.Configure<PwdrsSettings>(Configuration.GetSection("MySettings"));
        services.AddDbContext<PwdrsContext>(o => o.UseSqlServer(Configuration.GetConnectionString("PwdrsConnectionRoot")));

        services.AddMvc(o =>
        {
            o.Filters.Add<CustomExceptionFilter>();
        });

        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy", b => b
                .SetIsOriginAllowed((host) => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });

        services.AddSwaggerDocument();
        services.AddHttpContextAccessor();

        services.AddAutoMapper(typeof(ObjectMapperProfile));
        services.AddTransient<IEmailSender, EmailSender>();
        services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
        services.AddScoped(typeof(IAsyncRepository<>), typeof(Repository<>));
        services.AddScoped<IRfReportTypeRepository, RfReportTypeRepository>();
        services.AddScoped<IRfReportRepository, RfReportRepository>();
        services.AddScoped<IRfReportLookupsService, RfReportLookupsService>();
        services.AddScoped<IRfReportService, RfReportService>();

        services.Configure<RAFLogging>(Configuration.GetSection("RAFLogging"));
        ServiceProvider serviceProvider = services.BuildServiceProvider(); //WARNING IS HERE
        IOptions<RAFLogging> RAFLogger = serviceProvider.GetRequiredService<IOptions<RAFLogging>>();
        RegisterSerilogLogger logger = new RegisterSerilogLogger(RAFLogger);
    }
Punkouter
quelle
2
Erstens, warum bauen Sie Anbieter? Dies könnte ein XY-Problem sein . Können Sie die Frage neu formatieren, damit wir ein klareres Bild des aktuellen Problems erhalten und was Sie tatsächlich versuchen zu tun?
Nkosi
Ich bin mir nicht sicher. Ich glaube, ich habe bereits einen und vielleicht schafft das einen anderen?
Punkouter
Was meinst du damit WARNING IS HERE? Bitte geben Sie Details zur Warnung an. Zeigen Sie uns den Text der Warnung. Ist das eine Compiler-Warnung? Eine Warnung von einem Code-Analyse-Plugin? Wenn ja, welches? Ist das eine Laufzeitausnahme? Zeigen Sie uns alle relevanten Details der Ausnahme (Nachricht, Typ, Stapelverfolgung, innere Ausnahmen).
Steven
@punkouter "Was soll ich ändern?": Erstellen Sie den Dienstanbieter nicht manuell durch Aufrufen BuildServiceProvider(). Diese Methode sollte vom Host nur einmal aufgerufen werden. Ein doppelter Dienstanbieter kann zu unerwarteten Fehlern führen.
Itminus
Die Warnung ist der Titel. Ich denke, IServiceCollection ist, wo ich diesen Logger irgendwie platzieren sollte? Ich muss IServiceCollection besser verstehen als einen ServiceProvider.
Punkouter

Antworten:

3

Wenn in ConfigureServices BuildServiceProvider () aufgerufen wird, wird die Warnung "Wenn Sie 'BuildServiceProvider' aus dem Anwendungscode aufrufen, wird eine zusätzliche Kopie der Singleton-Dienste erstellt."

Ich habe dieses Problem gelöst:

Erstellen Sie eine weitere Funktion (deren übergebenes Argument IServiceCollection ist) und rufen Sie den Funktionsaufruf BuildServiceProvider () auf.

Geben Sie hier die Bildbeschreibung ein

Zum Beispiel sollte Ihr Code sein:

public void ConfigureServices(IServiceCollection services)
    {
        if (HostingEnvironment.EnvironmentName == "Local")
        {
            services.AddHealthChecksUI()
           .AddHealthChecks()
           .AddCheck<TestWebApiControllerHealthCheck>("HomePageHealthCheck")
           .AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
        }

    services.Configure<PwdrsSettings>(Configuration.GetSection("MySettings"));
    services.AddDbContext<PwdrsContext>(o => o.UseSqlServer(Configuration.GetConnectionString("PwdrsConnectionRoot")));

    services.AddMvc(o =>
    {
        o.Filters.Add<CustomExceptionFilter>();
    });

    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy", b => b
            .SetIsOriginAllowed((host) => true)
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials());
    });

    services.AddSwaggerDocument();
    services.AddHttpContextAccessor();

    services.AddAutoMapper(typeof(ObjectMapperProfile));
    services.AddTransient<IEmailSender, EmailSender>();
    services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
    services.AddScoped(typeof(IAsyncRepository<>), typeof(Repository<>));
    services.AddScoped<IRfReportTypeRepository, RfReportTypeRepository>();
    services.AddScoped<IRfReportRepository, RfReportRepository>();
    services.AddScoped<IRfReportLookupsService, RfReportLookupsService>();
    services.AddScoped<IRfReportService, RfReportService>();

    RegisterSerilogLogger logger = CreateRegisterSerilogLogger(services);
}

private RegisterSerilogLogger CreateRegisterSerilogLogger(IServiceCollection services){
        services.Configure<RAFLogging>(Configuration.GetSection("RAFLogging"));
        ServiceProvider serviceProvider = services.BuildServiceProvider(); //No warning here ))
        IOptions<RAFLogging> RAFLogger = serviceProvider.GetRequiredService<IOptions<RAFLogging>>();
        RegisterSerilogLogger logger = new RegisterSerilogLogger(RAFLogger);
    return logger;
}

Oder verwenden Sie ApplicationServices von IApplicationBuilder. Der Typ von ApplicationSerivces ist IServiceProvider.

AKTUALISIERT :

Ich erwähnte, dass diese Lösung eine Warnung zum Entfernen ist .

Ich denke, die richtige Version besteht darin, die ApplicationServices-Eigenschaft der App zu verwenden. Diese App ist IApplicationBuilder im Parameter der Configure-Methode. Der Typ von ApplicationServices ist IServiceProvider.

Geben Sie hier die Bildbeschreibung ein

Ramil Aliyev
quelle
Ich werde es in meiner Freizeit versuchen
Nijat Aliyev
1
Schöne Lösung! Ich war besorgt über diese Warnung (Y)
Samra
7
Das Aufrufen dieser Methode von einer anderen Funktion aus ist nicht die richtige Lösung. Sie sollten vermeiden, es von ÜBERALL in Ihrem Code aufzurufen. Dies entfernt nur die Warnung.
Adys
@Adys Ich stimme dir zu, mein Freund, ich erwähnte, dass diese Lösung eine Warnung zum Entfernen ist :) Ich denke, die richtige Version besteht darin, den ServiceProvider der App aufzurufen. Diese App ist IApplicationBuilder in der Konfigurationsmethode
Ramil Aliyev
-1

Der EINZIGE Zweck des Aufrufs von 'BuildServiceProvider' besteht darin, eine Dienstanbieterinstanz abzurufen.

Ändern Sie die Configure- Methode, um diesen Aufruf zu entfernen und IServiceProvider weiterhin verwenden zu können , um ihn als Parameter abzurufen :

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
Adys
quelle
Sie müssen IServiceProvider nicht einfügen, da app.ApplicationServices IServiceProvider ist. - Ramil Aliyev 24. Februar um 11:48 Uhr
Ramil Aliyev