Megosztás a következőn keresztül:


Függőséginjektálás a ASP.NET Core-ban

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadást, lásd ennek a cikknek a .NET 9-es verziójában .

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadást, lásd ennek a cikknek a .NET 9-es verziójában .

Fontos

Ezek az információk egy olyan előzetes termékre vonatkoznak, amelyet a kereskedelmi forgalomba kerülés előtt jelentősen módosíthatnak. A Microsoft nem vállal kifejezett vagy hallgatólagos szavatosságot az itt megadott információkra vonatkozóan.

Az aktuális kiadást, lásd ennek a cikknek a .NET 9-es verziójában .

Által Kirk Larkin, Steve Smith, és Brandon Dahler

ASP.NET Core támogatja a függőséginjektálás (DI) szoftvertervezési mintáját, amely az osztályok és függőségeik közötti Vezérlés inverziója (IoC) elérésének technikája.

Az ebben a cikkben szereplő útmutatást kiegészítő vagy felülíró Blazor DI-útmutatásért lásd ASP.NET Core Blazor függőséginjektálási.

Az MVC-vezérlők függőséginjektálására vonatkozó információkért lásd ASP.NET Corevezérlőibe történő függőséginjektálást.

A függőséginjektálás webalkalmazásokon kívüli alkalmazásokban való használatáról további információt a .NET-függőséginjektálásában talál.

A beállítások függőségi injektálásával kapcsolatos információkért lásd az ASP.NET Core beállítások mintát .

Ez a cikk a ASP.NET Core-ban található függőséginjektálással kapcsolatos információkat tartalmazza. A függőséginjektálás használatának elsődleges dokumentációját a .NETFüggőséginjektálás tartalmazza.

Mintakód megtekintése vagy letöltése (hogyan lehet letölteni)

A függőséginjektálás áttekintése

A függőségi egy objektum, amelytől egy másik objektum függ. Vizsgálja meg a következő MyDependency osztályt egy WriteMessage metódussal, amelytől más osztályok függenek:

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

Az osztály létrehozhatja a MyDependency osztály egy példányát a WriteMessage metódusának használatához. Az alábbi példában a MyDependency osztály az IndexModel osztály függősége:


public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet");
    }
}

Az osztály létrejön és közvetlenül a MyDependency osztálytól függ. A kódfüggőségek, például az előző példában, problémásak, és a következő okok miatt kerülendők:

  • A MyDependency másik implementációra való lecseréléséhez módosítani kell a IndexModel osztályt.
  • Ha MyDependency függőségekkel rendelkezik, a IndexModel osztálynak is konfigurálnia kell őket. Egy nagy, MyDependencyfüggően több osztályt tartalmazó projektben a konfigurációs kód szétszóródik az alkalmazásban.
  • Ezt a megvalósítást nehéz egységtesztelni.

A függőséginjektálás a következő módon oldja meg ezeket a problémákat:

  • Egy interfész vagy alaposztály használata a függőségi implementáció absztrakciójára.
  • A függőség regisztrálása egy szolgáltatástárolóban. ASP.NET Core egy beépített szolgáltatástárolót biztosít, IServiceProvider. A szolgáltatások általában az alkalmazás Program.cs fájljában vannak regisztrálva.
  • szolgáltatás injektálása annak az osztálynak a konstruktorába, ahol a szolgáltatást használják. A keretrendszer felelősséget vállal a függőség egy példányának létrehozásáért és eltávolításáért, ha már nincs rá szükség.

A mintaalkalmazása IMyDependency felület határozza meg a WriteMessage metódust:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Ezt az interfészt egy konkrét típus valósítja meg, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

A mintaalkalmazás regisztrálja a IMyDependency szolgáltatást a konkrét MyDependencytípussal. A AddScoped metódus egy hatókörön belüli élettartammal, egyetlen kérelem élettartamával regisztrálja a szolgáltatást. szolgáltatás élettartamát a cikk későbbi részében ismertetjük.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

A mintaalkalmazásban a rendszer a IMyDependency szolgáltatást kéri, és a WriteMessage metódus meghívására használja:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

A DI-minta használatával a vezérlő vagy Razor oldal:

  • Nem használja a konkrét MyDependencytípust, csak a IMyDependency felületet, amelyet implementál. Ez megkönnyíti a implementáció módosítását a vezérlő vagy Razor oldal módosítása nélkül.
  • Nem egy MyDependencypéldányt hoz létre; ezt a DI-tároló hozza létre.

A IMyDependency felület implementációja a beépített naplózási API használatával fejleszthető:

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

A frissített Program.cs regisztrálja az új IMyDependency implementációt:

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency2>();

var app = builder.Build();

Az MyDependency2 függ az ILogger<TCategoryName>-től, amelyet a konstruktorban kér. ILogger<TCategoryName> egy keretrendszer által biztosított szolgáltatás.

Nem szokatlan a függőséginjektálást láncsoltan alkalmazni. Minden kért függőség saját függőségeket kér. A tároló feloldja a gráf függőségeit, és visszaadja a teljes mértékben feloldott szolgáltatást. A feloldandó függőségek együttes készletét általában függőségi fa, függőségi gráfvagy objektumdiagramnevezik.

A tároló a ILogger<TCategoryName>használatával oldja fel a , így nincs szükség minden (általános) létrehozottregisztrálására.

A függőséginjektálás terminológiájában egy szolgáltatás:

  • Általában olyan objektum, amely szolgáltatást nyújt más objektumoknak, például a IMyDependency szolgáltatásnak.
  • Nem kapcsolódik webszolgáltatáshoz, bár előfordulhat, hogy a szolgáltatás webszolgáltatást használ.

A keretrendszer robusztus naplózási rendszert biztosít. Az előző példákban bemutatott IMyDependency implementációk azért lettek megírva, hogy az alapvető DI-t szemléltetik, és ne a naplózás implementálását. A legtöbb alkalmazásnak nem kell naplózókat írnia. Az alábbi kód bemutatja az alapértelmezett naplózás használatát, amely nem követeli meg a szolgáltatások regisztrálását:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; } = string.Empty;

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Az előző kód megfelelően működik anélkül, hogy bármit módosítana Program.cs, mert naplózási a keretrendszer biztosítja.

Szolgáltatáscsoportok regisztrálása bővítménymetelyekkel

A ASP.NET Core-keretrendszer konvenciót használ a kapcsolódó szolgáltatások egy csoportjának regisztrálására. A konvenció egyetlen Add{GROUP_NAME} bővítménymetódus használatával regisztrálja a keretrendszerfunkciók által igényelt összes szolgáltatást. A AddControllers bővítménymetódus például regisztrálja az MVC-vezérlőkhöz szükséges szolgáltatásokat.

A következő kódot a Razor Pages-sablon hozza létre egyéni felhasználói fiókok használatával, és bemutatja, hogyan adhat hozzá további szolgáltatásokat a tárolóhoz a bővítménymetelyek AddDbContext és AddDefaultIdentityhasználatával:

using DependencyInjectionSample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

Vegye figyelembe a következőket, amelyek regisztrálják a szolgáltatásokat, és konfigurálják a beállításokat:

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(
    builder.Configuration.GetSection(ColorOptions.Color));

builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();

var app = builder.Build();

A kapcsolódó regisztrációs csoportok áthelyezhetők egy bővítménymetódusba a szolgáltatások regisztrálásához. A konfigurációs szolgáltatások például a következő osztályhoz lesznek hozzáadva:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }

        public static IServiceCollection AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

A többi szolgáltatás hasonló osztályba van regisztrálva. Az alábbi kód az új bővítménymetelyeket használja a szolgáltatások regisztrálásához:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddConfig(builder.Configuration)
    .AddMyDependencyGroup();

builder.Services.AddRazorPages();

var app = builder.Build();

Megjegyzés: Minden services.Add{GROUP_NAME} bővítménymetódus szolgáltatásokat ad hozzá és konfigurál. Például AddControllersWithViews hozzáadja azokat a szolgáltatásokat, amelyekre az MVC vezérlőknek a nézetekkel szükségük van, és AddRazorPages hozzáadja azokat a szolgáltatásokat, amelyekre a Razor Lapoknak szükségük van.

Szolgáltatási élettartamok

Lásd a szolgáltatások élettartama a .NET függőség-injektálásban

A hatókörön belüli szolgáltatások köztes szoftverben való használatához használja az alábbi módszerek egyikét:

  • Injektálja a szolgáltatást a köztes szoftver Invoke vagy InvokeAsync metódusába. A konstruktorinjektálás használata futásidejű kivételt eredményez, mivel a hatókörön belüli szolgáltatás egyetlentonnként viselkedik. Az Élettartam és a regisztrációs lehetőségek szakaszban található minta bemutatja a InvokeAsync megközelítést.
  • Használja gyár által tervezett köztes szoftver. Az ezzel a megközelítéssel regisztrált köztes szoftver ügyfélkérésenként (kapcsolatonként) aktiválódik, ami lehetővé teszi, hogy a hatókörrel rendelkező szolgáltatások injektálhatók legyenek a köztes szoftver konstruktorába.

További információért lásd: Egyéni ASP.NET Core middleware komponensírása.

Szolgáltatásregisztrációs módszerek

Lásd a szolgáltatásregisztrációs módszereket a .NET függőséginjektálásában.

Gyakori, hogy több implementációt is használnak, ha teszteléséhez használt modelltípusokat.

A szolgáltatás csak implementációs típussal való regisztrálása egyenértékű azzal, ha a szolgáltatást ugyanazzal a megvalósítási és szolgáltatástípussal regisztrálja. Ez az oka annak, hogy egy szolgáltatás több implementációja nem regisztrálható olyan metódusokkal, amelyek nem használnak explicit szolgáltatástípust. Ezek a metódusok több példányt is regisztrálhatnak egy szolgáltatás, de mindegyiknek ugyanaz a implementálási típusa.

A szolgáltatásregisztrációs módszerek bármelyike használható több azonos szolgáltatástípusú szolgáltatáspéldány regisztrálásához. Az alábbi példában a AddSingleton-et kétszer hívják meg a IMyDependency szolgáltatástípussal. A második hívás AddSingleton esetén felülbírálja az előzőt, amikor IMyDependency -ként van feloldva, és hozzáadódik az előzőhöz, ha több szolgáltatás feloldása IEnumerable<IMyDependency>keresztül történik. A szolgáltatások a regisztrációjuk sorrendjében jelennek meg, amikor IEnumerable<{SERVICE}>-n keresztül oldják meg őket.

services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}

Kulcsos szolgáltatások

A kulcsos szolgáltatások kifejezés a függőséginjektálási (DI-) szolgáltatások kulcsokkal történő regisztrálására és lekérésére szolgáló mechanizmusra utal. A szolgáltatás egy kulccsal van társítva, ha meghívja AddKeyedSingleton (vagy AddKeyedScoped vagy AddKeyedTransient) a regisztrációhoz. Egy regisztrált szolgáltatás eléréséhez adja meg a kulcsot a [FromKeyedServices] attribútummal. A következő kód a kulcsos szolgáltatások használatát mutatja be:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();

var app = builder.Build();

app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>
                                                               smallCache.Get("date"));

app.MapControllers();

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

[ApiController]
[Route("/cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big-cache")]
    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

public class MyHub : Hub
{
    public void Method([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Kulcsalapú szolgáltatások a köztes rétegben

A köztes szoftver támogatja a kulcsolt szolgáltatásokat mind a konstruktorban, mind a Invoke/InvokeAsync metódusban.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");

var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();

internal class MyMiddleware
{
    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next,
        [FromKeyedServices("test")] MySingletonClass service)
    {
        _next = next;
    }

    public Task Invoke(HttpContext context,
        [FromKeyedServices("test2")]
            MyScopedClass scopedService) => _next(context);
}

A Köztes szoftver létrehozásával kapcsolatos további információkért lásd: Egyéni ASP.NET Core köztes szoftver írása

Konstruktorinjektálási viselkedés

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

Entity Framework-környezetek

Alapértelmezés szerint az Entity Framework-környezetek a hatókörrel rendelkező élettartam használatával vannak hozzáadva a szolgáltatástárolóhoz, mivel a webalkalmazás-adatbázis műveletei általában az ügyfélkérés hatókörébe tartoznak. Ha másik élettartam-paramétert szeretne használni, adja meg az élettartam-paramétert egy AddDbContext túlterhelés használatával. Egy adott élettartamú szolgáltatások nem használhatnak olyan adatbázis-környezetet, amelynek élettartama rövidebb, mint a szolgáltatás élettartama.

Élettartam és regisztrációs lehetőségek

A szolgáltatási élettartamok és a regisztrációs lehetőségek közötti különbség szemléltetéséhez vegye figyelembe a következő interfészeket, amelyek egy feladatot azonosítóval rendelkező műveletként jelölnek, OperationId. Attól függően, hogy egy művelet szolgáltatásának élettartama hogyan van konfigurálva a következő felületekhez, a tároló a szolgáltatás ugyanazon vagy különböző példányait biztosítja, amikor egy osztály kéri:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Az alábbi Operation osztály az összes előző felületet implementálja. A Operation konstruktor létrehoz egy GUID azonosítót, és az utolsó 4 karaktert a OperationId tulajdonságban tárolja:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

Az alábbi kód több regisztrációt hoz létre a Operation osztályból a megnevezett élettartamoknak megfelelően:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseMyMiddleware();
app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

A mintaalkalmazás bemutatja az objektumok élettartamát a kéréseken belül és között is. A IndexModel és a köztes szoftver minden IOperation típusú kérelmet elküld, és naplózza az egyes OperationId:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

A IndexModelhasonlóan a köztes szoftver ugyanazokat a szolgáltatásokat oldja fel:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationTransient transientOperation, IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + transientOperation.OperationId);
        _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

A hatókörrel és az átmeneti szolgáltatásokkal kapcsolatos problémákat a InvokeAsync metódusban kell feloldani:

public async Task InvokeAsync(HttpContext context,
    IOperationTransient transientOperation, IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + transientOperation.OperationId);
    _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

A naplózó kimenete a következőt jeleníti meg:

  • átmeneti objektumok mindig eltérőek. Az átmeneti OperationId érték eltér a IndexModel-ben és a köztes szoftverben.
  • hatókörrel rendelkező objektumok egy adott kérés esetében azonosak, de minden új kérelemben eltérnek.
  • Singleton objektumai minden kéréshez azonosak.

A naplózási kimenet csökkentéséhez állítsa be a "Naplózás:LogLevel:Microsoft:Error" értéket a appsettings.Development.json fájlban:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Szolgáltatás feloldása az alkalmazás indításakor

Az alábbi kód bemutatja, hogyan oldhat fel korlátozott ideig hatókörrel rendelkező szolgáltatást az alkalmazás indításakor:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var myDependency = services.GetRequiredService<IMyDependency>();
    myDependency.WriteMessage("Call services from main");
}

app.MapGet("/", () => "Hello World!");

app.Run();

Hatókör érvényesítése

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

További információ: Hatókör érvényesítése.

Szolgáltatások kérése

A szolgáltatások és függőségeik egy ASP.NET Core-kérelemben HttpContext.RequestServiceskeresztül érhetők el.

A keretrendszer kérésenként létrehoz egy hatókört, és RequestServices elérhetővé teszi a hatókörbe tartozó szolgáltatót. A hatókörön belüli szolgáltatások mindaddig érvényesek, amíg a kérés aktív.

Jegyzet

A függőségek konstruktorparaméterekként való kérését részesítse előnyben a szolgáltatások RequestServicesvaló feloldása helyett. A függőségek konstruktorparaméterként való kérése könnyebben tesztelhető osztályokat eredményez.

Tervezési szolgáltatások függőséginjektáláshoz

Függőséginjektálási szolgáltatások tervezésekor:

  • Kerülje az állapotalapú, statikus osztályokat és tagokat. Kerülje a globális állapot létrehozását azáltal, hogy az alkalmazásokat a singleton szolgáltatások használatára tervezi.
  • Kerülje a függő osztályok közvetlen példányosítását a szolgáltatásokban. A közvetlen példányosítás egy adott implementációhoz párosítja a kódot.
  • A szolgáltatások kicsi, jól faktorált és könnyen tesztelhetővé tétele.

Ha egy osztály sok injektált függőséget használ, annak jele lehet, hogy az osztály túl sok felelősséget vállal, és megsérti az Egységes felelősség elve (SRP). Próbálja meg újra bontani az osztályt úgy, hogy egyes feladatait új osztályokba helyezi át. Ne feledje, hogy Razor Lapok lapmodellosztályainak és MVC-vezérlőosztályainak a felhasználói felülettel kapcsolatos problémákra kell összpontosítaniuk.

Szolgáltatások megszüntetése

A tároló az általa létrehozott Dispose típusokhoz meghívja a IDisposable-t. A tárolóból feloldott szolgáltatásokat a fejlesztőnek soha nem szabad megsemmisítenie. Ha egy típus vagy gyár singletontként van regisztrálva, a konténer automatikusan megsemmisíti a singletont.

A következő példában a szolgáltatástároló hozza létre a szolgáltatásokat, és automatikusan megsemmisíti:

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
using DIsample2.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();

var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));

var app = builder.Build();
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

A hibakeresési konzol a következő kimenetet jeleníti meg az Index lap minden egyes frissítése után:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose

A szolgáltatástároló által nem létrehozott szolgáltatások

Vegye figyelembe a következő kódot:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());

Az előző kódban:

  • A szolgáltatáspéldányokat nem a szolgáltatástároló hozza létre.
  • A keretrendszer nem szünteti meg automatikusan a szolgáltatásokat.
  • A szolgáltatások eltávolításáért a fejlesztő felel.

IDisposable útmutató átmeneti és megosztott példányokhoz

Lásd .NET- függőséginjektálásának átmeneti és megosztott példányainak IDisposable útmutatóját

Alapértelmezett szolgáltatástároló cseréje

Lásd Alapértelmezett szolgáltatástároló-csere a .NET- függőséginjektálásában

Ajánlások

Lásd .NET- függőséginjektálásának javaslatait

  • Kerülje a szolgáltatáskereső mintahasználatát. Például ne hívjon meg GetService szolgáltatáspéldány beszerzéséhez, ha használhatja a DI-t:

    helytelen:

    helytelen kód

    Helyes:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Egy másik szolgáltatáskereső változat, amely elkerülhető, egy olyan gyár injektálása, amely futásidőben oldja fel a függőségeket. Mindkét gyakorlat ötvözi a és a inverzióját a kontroll stratégiákban.

  • Kerülje a HttpContext statikus elérését (például IHttpContextAccessor.HttpContext).

A DI egy alternatív a statikus/globális objektumhozzáférési mintákhoz. Előfordulhat, hogy nem tudja kihasználni a DI előnyeit, ha statikus objektumhozzáféréssel keveri.

Orchard Core egy olyan alkalmazáskeret, amely moduláris, több-bérlős alkalmazásokat készít ASP.NET Core-on. További információkért lásd az Orchard Core dokumentációt.

Tekintse meg az Orchard Core-példákat, amelyek bemutatják, hogyan hozhat létre moduláris és több-bérlős alkalmazásokat az Orchard Core-keretrendszer CMS-specifikus funkciói használata nélkül.

Keretrendszer által biztosított szolgáltatások

Program.cs regisztrálja az alkalmazás által használt szolgáltatásokat, beleértve a platformfunkciókat, például az Entity Framework Core-t és a ASP.NET Core MVC-t. Kezdetben a IServiceCollection által Program.cs számára biztosított szolgáltatásokat a keretrendszer határozza meg, attól függően, hogy a gazdagép hogyan lett konfigurálva . A ASP.NET Core-sablonokon alapuló alkalmazások esetében a keretrendszer több mint 250 szolgáltatást regisztrál.

Az alábbi táblázat a keretrendszer által regisztrált szolgáltatások egy kis mintáját sorolja fel:

Szolgáltatás típusa Élettartam
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Átmeneti
IHostApplicationLifetime Singleton
IWebHostEnvironment Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Átmeneti
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Http.IHttpContextFactory Átmeneti
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Átmeneti
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton

További erőforrások

Által Kirk Larkin, Steve Smith, és Brandon Dahler

ASP.NET Core támogatja a függőséginjektálás (DI) szoftvertervezési mintáját, amely az osztályok és függőségeik közötti Vezérlés inverziója (IoC) elérésének technikája.

Az MVC-vezérlők függőséginjektálására vonatkozó további információkért lásd ASP.NET Corevezérlőibe történő függőséginjektálást.

A függőséginjektálás webalkalmazásokon kívüli alkalmazásokban való használatáról további információt a .NET-függőséginjektálásában talál.

A lehetőségek függőségi injektálásával kapcsolatos további információkért lásd Beállítások mintát ASP.NET Core.

Ez a témakör a ASP.NET Core-ban található függőséginjektálással kapcsolatos információkat tartalmazza. A függőséginjektálás használatának elsődleges dokumentációját a .NETFüggőséginjektálás tartalmazza.

Mintakód megtekintése vagy letöltése (hogyan lehet letölteni)

A függőséginjektálás áttekintése

A függőségi egy objektum, amelytől egy másik objektum függ. Vizsgálja meg a következő MyDependency osztályt egy WriteMessage metódussal, amelytől más osztályok függenek:

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

Az osztály létrehozhatja a MyDependency osztály egy példányát a WriteMessage metódusának használatához. Az alábbi példában a MyDependency osztály az IndexModel osztály függősége:


public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet");
    }
}

Az osztály létrejön és közvetlenül a MyDependency osztálytól függ. A kódfüggőségek, például az előző példában, problémásak, és a következő okok miatt kerülendők:

  • A MyDependency másik implementációra való lecseréléséhez módosítani kell a IndexModel osztályt.
  • Ha MyDependency függőségekkel rendelkezik, a IndexModel osztálynak is konfigurálnia kell őket. Egy nagy, MyDependencyfüggően több osztályt tartalmazó projektben a konfigurációs kód szétszóródik az alkalmazásban.
  • Ezt a megvalósítást nehéz egységtesztelni.

A függőséginjektálás a következő módon oldja meg ezeket a problémákat:

  • Egy interfész vagy alaposztály használata a függőségi implementáció absztrakciójára.
  • A függőség regisztrálása egy szolgáltatástárolóban. ASP.NET Core egy beépített szolgáltatástárolót biztosít, IServiceProvider. A szolgáltatások általában az alkalmazás Program.cs fájljában vannak regisztrálva.
  • szolgáltatás injektálása annak az osztálynak a konstruktorába, ahol a szolgáltatást használják. A keretrendszer felelősséget vállal a függőség egy példányának létrehozásáért és eltávolításáért, ha már nincs rá szükség.

A mintaalkalmazása IMyDependency felület határozza meg a WriteMessage metódust:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Ezt az interfészt egy konkrét típus valósítja meg, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

A mintaalkalmazás regisztrálja a IMyDependency szolgáltatást a konkrét MyDependencytípussal. A AddScoped metódus egy hatókörön belüli élettartammal, egyetlen kérelem élettartamával regisztrálja a szolgáltatást. szolgáltatás élettartamát a jelen témakör későbbi részében ismertetjük.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

A mintaalkalmazásban a rendszer a IMyDependency szolgáltatást kéri, és a WriteMessage metódus meghívására használja:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

A DI-minta használatával a vezérlő vagy Razor oldal:

  • Nem használja a konkrét MyDependencytípust, csak a IMyDependency felületet, amelyet implementál. Ez megkönnyíti a implementáció módosítását a vezérlő vagy Razor oldal módosítása nélkül.
  • Nem egy MyDependencypéldányt hoz létre; ezt a DI-tároló hozza létre.

A IMyDependency felület implementációja a beépített naplózási API használatával fejleszthető:

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

A frissített Program.cs regisztrálja az új IMyDependency implementációt:

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency2>();

var app = builder.Build();

Az MyDependency2 függ az ILogger<TCategoryName>-től, amelyet a konstruktorban kér. ILogger<TCategoryName> egy keretrendszer által biztosított szolgáltatás.

Nem szokatlan a függőséginjektálást láncsoltan alkalmazni. Minden kért függőség saját függőségeket kér. A tároló feloldja a gráf függőségeit, és visszaadja a teljes mértékben feloldott szolgáltatást. A feloldandó függőségek együttes készletét általában függőségi fa, függőségi gráfvagy objektumdiagramnevezik.

A tároló a ILogger<TCategoryName>használatával oldja fel a , így nincs szükség minden (általános) létrehozottregisztrálására.

A függőséginjektálás terminológiájában egy szolgáltatás:

  • Általában olyan objektum, amely szolgáltatást nyújt más objektumoknak, például a IMyDependency szolgáltatásnak.
  • Nem kapcsolódik webszolgáltatáshoz, bár a szolgáltatás webszolgáltatást is használhat.

A keretrendszer robusztus naplózási rendszert biztosít. Az előző példákban bemutatott IMyDependency implementációk azért lettek megírva, hogy az alapvető DI-t szemléltetik, és ne a naplózás implementálását. A legtöbb alkalmazásnak nem kell naplózókat írnia. Az alábbi kód bemutatja az alapértelmezett naplózás használatát, amely nem követeli meg a szolgáltatások regisztrálását:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; } = string.Empty;

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Az előző kód használatával nincs szükség a Program.csfrissítésére, mert a keretrendszer biztosítja a naplózási.

Szolgáltatáscsoportok regisztrálása bővítménymetelyekkel

A ASP.NET Core-keretrendszer konvenciót használ a kapcsolódó szolgáltatások egy csoportjának regisztrálására. A konvenció egyetlen Add{GROUP_NAME} bővítménymetódus használatával regisztrálja a keretrendszerfunkciók által igényelt összes szolgáltatást. A AddControllers bővítménymetódus például regisztrálja az MVC-vezérlőkhöz szükséges szolgáltatásokat.

A következő kódot a Razor Pages-sablon hozza létre egyéni felhasználói fiókok használatával, és bemutatja, hogyan adhat hozzá további szolgáltatásokat a tárolóhoz a bővítménymetelyek AddDbContext és AddDefaultIdentityhasználatával:

using DependencyInjectionSample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

Vegye figyelembe a következőket, amelyek regisztrálják a szolgáltatásokat, és konfigurálják a beállításokat:

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(
    builder.Configuration.GetSection(ColorOptions.Color));

builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();

var app = builder.Build();

A kapcsolódó regisztrációs csoportok áthelyezhetők egy bővítménymetódusba a szolgáltatások regisztrálásához. A konfigurációs szolgáltatások például a következő osztályhoz lesznek hozzáadva:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }

        public static IServiceCollection AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

A többi szolgáltatás hasonló osztályba van regisztrálva. Az alábbi kód az új bővítménymetelyeket használja a szolgáltatások regisztrálásához:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddConfig(builder.Configuration)
    .AddMyDependencyGroup();

builder.Services.AddRazorPages();

var app = builder.Build();

Megjegyzés: Minden services.Add{GROUP_NAME} bővítménymetódus szolgáltatásokat ad hozzá és konfigurál. Például AddControllersWithViews hozzáadja azokat a szolgáltatásokat, amelyekre az MVC vezérlőknek a nézetekkel szükségük van, és AddRazorPages hozzáadja azokat a szolgáltatásokat, amelyekre a Razor Lapoknak szükségük van.

Szolgáltatási élettartamok

Lásd a szolgáltatások élettartama a .NET függőség-injektálásban

A hatókörön belüli szolgáltatások köztes szoftverben való használatához használja az alábbi módszerek egyikét:

  • Injektálja a szolgáltatást a köztes szoftver Invoke vagy InvokeAsync metódusába. A konstruktorinjektálás használata futásidejű kivételt eredményez, mivel a hatókörön belüli szolgáltatás egyetlentonnként viselkedik. Az Élettartam és a regisztrációs lehetőségek szakaszban található minta bemutatja a InvokeAsync megközelítést.
  • Használja gyár által tervezett köztes szoftver. Az ezzel a megközelítéssel regisztrált köztes szoftver ügyfélkérésenként (kapcsolatonként) aktiválódik, ami lehetővé teszi, hogy a hatókörrel rendelkező szolgáltatások injektálhatók legyenek a köztes szoftver konstruktorába.

További információért lásd: Egyéni ASP.NET Core middleware komponensírása.

Szolgáltatásregisztrációs módszerek

Lásd a szolgáltatásregisztrációs módszereket a .NET függőséginjektálásában.

Gyakori, hogy több implementációt is használnak, ha teszteléséhez használt modelltípusokat.

A szolgáltatás csak implementációs típussal való regisztrálása egyenértékű azzal, ha a szolgáltatást ugyanazzal a megvalósítási és szolgáltatástípussal regisztrálja. Ez az oka annak, hogy egy szolgáltatás több implementációja nem regisztrálható olyan metódusokkal, amelyek nem használnak explicit szolgáltatástípust. Ezek a metódusok több példányt is regisztrálhatnak egy szolgáltatás, de mind ugyanazzal a implementálási típussal rendelkeznek.

A fenti szolgáltatásregisztrációs módszerek bármelyike használható több azonos szolgáltatástípusú szolgáltatáspéldány regisztrálásához. Az alábbi példában a AddSingleton-et kétszer hívják meg a IMyDependency szolgáltatástípussal. A második hívás AddSingleton esetén felülbírálja az előzőt, amikor IMyDependency -ként van feloldva, és hozzáadódik az előzőhöz, ha több szolgáltatás feloldása IEnumerable<IMyDependency>keresztül történik. A szolgáltatások a regisztrációjuk sorrendjében jelennek meg, amikor IEnumerable<{SERVICE}>-n keresztül oldják meg őket.

services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}

Kulcsos szolgáltatások

kulcsos szolgáltatások a függőséginjektálási (DI-) szolgáltatások kulcsokkal történő regisztrálására és lekérésére szolgáló mechanizmusra utal. A szolgáltatás egy kulccsal van társítva, ha meghívja AddKeyedSingleton (vagy AddKeyedScoped vagy AddKeyedTransient) a regisztrációhoz. Egy regisztrált szolgáltatás eléréséhez adja meg a kulcsot a [FromKeyedServices] attribútummal. A következő kód a kulcsos szolgáltatások használatát mutatja be:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();

var app = builder.Build();

app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>
                                                               smallCache.Get("date"));

app.MapControllers();

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

[ApiController]
[Route("/cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big-cache")]
    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

public class MyHub : Hub
{
    public void Method([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Konstruktorinjektálási viselkedés

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

Entity Framework-környezetek

Alapértelmezés szerint az Entity Framework-környezetek a hatókörrel rendelkező élettartam használatával vannak hozzáadva a szolgáltatástárolóhoz, mivel a webalkalmazás-adatbázis műveletei általában az ügyfélkérés hatókörébe tartoznak. Ha másik élettartam-paramétert szeretne használni, adja meg az élettartam-paramétert egy AddDbContext túlterhelés használatával. Egy adott élettartamú szolgáltatások nem használhatnak olyan adatbázis-környezetet, amelynek élettartama rövidebb, mint a szolgáltatás élettartama.

Élettartam és regisztrációs lehetőségek

A szolgáltatási élettartamok és a regisztrációs lehetőségek közötti különbség szemléltetéséhez vegye figyelembe a következő interfészeket, amelyek egy feladatot azonosítóval rendelkező műveletként jelölnek, OperationId. Attól függően, hogy egy művelet szolgáltatásának élettartama hogyan van konfigurálva a következő felületekhez, a tároló a szolgáltatás ugyanazon vagy különböző példányait biztosítja, amikor egy osztály kéri:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Az alábbi Operation osztály az összes előző felületet implementálja. A Operation konstruktor létrehoz egy GUID azonosítót, és az utolsó 4 karaktert a OperationId tulajdonságban tárolja:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

Az alábbi kód több regisztrációt hoz létre a Operation osztályból a megnevezett élettartamoknak megfelelően:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseMyMiddleware();
app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

A mintaalkalmazás bemutatja az objektumok élettartamát a kéréseken belül és között is. A IndexModel és a köztes szoftver minden IOperation típusú kérelmet elküld, és naplózza az egyes OperationId:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

A IndexModelhasonlóan a köztes szoftver ugyanazokat a szolgáltatásokat oldja fel:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationTransient transientOperation, IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + transientOperation.OperationId);
        _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

A hatókörrel és az átmeneti szolgáltatásokkal kapcsolatos problémákat a InvokeAsync metódusban kell feloldani:

public async Task InvokeAsync(HttpContext context,
    IOperationTransient transientOperation, IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + transientOperation.OperationId);
    _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

A naplózó kimenete a következőt jeleníti meg:

  • átmeneti objektumok mindig eltérőek. Az átmeneti OperationId érték eltér a IndexModel-ben és a köztes szoftverben.
  • hatókörrel rendelkező objektumok egy adott kérés esetében azonosak, de minden új kérelemben eltérnek.
  • Singleton objektumai minden kéréshez azonosak.

A naplózási kimenet csökkentéséhez állítsa be a "Naplózás:LogLevel:Microsoft:Error" értéket a appsettings.Development.json fájlban:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Szolgáltatás feloldása az alkalmazás indításakor

Az alábbi kód bemutatja, hogyan oldhat fel korlátozott ideig hatókörrel rendelkező szolgáltatást az alkalmazás indításakor:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var myDependency = services.GetRequiredService<IMyDependency>();
    myDependency.WriteMessage("Call services from main");
}

app.MapGet("/", () => "Hello World!");

app.Run();

Hatókör érvényesítése

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

További információ: Hatókör érvényesítése.

Szolgáltatások kérése

A szolgáltatások és függőségeik egy ASP.NET Core-kérelemben HttpContext.RequestServiceskeresztül érhetők el.

A keretrendszer kérésenként létrehoz egy hatókört, és RequestServices elérhetővé teszi a hatókörbe tartozó szolgáltatót. A hatókörön belüli szolgáltatások mindaddig érvényesek, amíg a kérés aktív.

Jegyzet

A függőségek konstruktorparaméterekként való kérését részesítse előnyben a szolgáltatások RequestServicesvaló feloldása helyett. A függőségek konstruktorparaméterként való kérése könnyebben tesztelhető osztályokat eredményez.

Tervezési szolgáltatások függőséginjektáláshoz

Függőséginjektálási szolgáltatások tervezésekor:

  • Kerülje az állapotalapú, statikus osztályokat és tagokat. Kerülje a globális állapot létrehozását azáltal, hogy az alkalmazásokat a singleton szolgáltatások használatára tervezi.
  • Kerülje a függő osztályok közvetlen példányosítását a szolgáltatásokban. A közvetlen példányosítás egy adott implementációhoz párosítja a kódot.
  • A szolgáltatások kicsi, jól faktorált és könnyen tesztelhetővé tétele.

Ha egy osztály sok injektált függőséget használ, annak jele lehet, hogy az osztály túl sok felelősséget vállal, és megsérti az Egységes felelősség elve (SRP). Próbálja meg újra bontani az osztályt úgy, hogy egyes feladatait új osztályokba helyezi át. Ne feledje, hogy Razor Lapok lapmodellosztályainak és MVC-vezérlőosztályainak a felhasználói felülettel kapcsolatos problémákra kell összpontosítaniuk.

Szolgáltatások megszüntetése

A tároló az általa létrehozott Dispose típusokhoz meghívja a IDisposable-t. A tárolóból feloldott szolgáltatásokat a fejlesztőnek soha nem szabad megsemmisítenie. Ha egy típus vagy gyár singletontként van regisztrálva, a konténer automatikusan megsemmisíti a singletont.

A következő példában a szolgáltatásokat a szolgáltatástároló hozza létre, és automatikusan megsemmisíti: dependency-injection\samples\6.x\DIsample2\DIsample2\Services\Service1.cs

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
using DIsample2.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();

var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));

var app = builder.Build();
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

A hibakeresési konzol a következő kimenetet jeleníti meg az Index lap minden egyes frissítése után:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose

A szolgáltatástároló által nem létrehozott szolgáltatások

Vegye figyelembe a következő kódot:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());

Az előző kódban:

  • A szolgáltatáspéldányokat nem a szolgáltatástároló hozza létre.
  • A keretrendszer nem szünteti meg automatikusan a szolgáltatásokat.
  • A szolgáltatások eltávolításáért a fejlesztő felel.

IDisposable útmutató átmeneti és megosztott példányokhoz

Lásd .NET- függőséginjektálásának átmeneti és megosztott példányainak IDisposable útmutatóját

Alapértelmezett szolgáltatástároló cseréje

Lásd Alapértelmezett szolgáltatástároló-csere a .NET- függőséginjektálásában

Ajánlások

Lásd .NET- függőséginjektálásának javaslatait

  • Kerülje a szolgáltatáskereső mintahasználatát. Például ne hívjon meg GetService szolgáltatáspéldány beszerzéséhez, ha használhatja a DI-t:

    helytelen:

    helytelen kód

    Helyes:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Egy másik szolgáltatáskereső változat, amely elkerülhető, egy olyan gyár injektálása, amely futásidőben oldja fel a függőségeket. Mindkét gyakorlat ötvözi a és a inverzióját a kontroll stratégiákban.

  • Kerülje a HttpContext statikus elérését (például IHttpContextAccessor.HttpContext).

A DI egy alternatív a statikus/globális objektumhozzáférési mintákhoz. Előfordulhat, hogy nem tudja kihasználni a DI előnyeit, ha statikus objektumhozzáféréssel keveri.

Orchard Core egy olyan alkalmazás-keretrendszer, amely moduláris, több-bérlős alkalmazások építésére szolgál az ASP.NET Core-on. További információkért lásd az Orchard Core dokumentációt.

Tekintse meg az Orchard Core-példákat, amelyek bemutatják, hogyan hozhat létre moduláris és több-bérlős alkalmazásokat az Orchard Core-keretrendszer CMS-specifikus funkciói használata nélkül.

Keretrendszer által biztosított szolgáltatások

Program.cs regisztrálja az alkalmazás által használt szolgáltatásokat, beleértve a platformfunkciókat, például az Entity Framework Core-t és a ASP.NET Core MVC-t. Kezdetben a IServiceCollection által Program.cs számára biztosított szolgáltatásokat a keretrendszer határozza meg, attól függően, hogy a gazdagép hogyan lett konfigurálva . A ASP.NET Core-sablonokon alapuló alkalmazások esetében a keretrendszer több mint 250 szolgáltatást regisztrál.

Az alábbi táblázat a keretrendszer által regisztrált szolgáltatások egy kis mintáját sorolja fel:

Szolgáltatás típusa Élettartam
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Átmeneti
IHostApplicationLifetime Singleton
IWebHostEnvironment Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Átmeneti
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Http.IHttpContextFactory Átmeneti
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Átmeneti
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton

További erőforrások

Által Kirk Larkin, Steve Smith, és Brandon Dahler

ASP.NET Core támogatja a függőséginjektálás (DI) szoftvertervezési mintáját, amely az osztályok és függőségeik közötti Vezérlés inverziója (IoC) elérésének technikája.

Az MVC-vezérlők függőséginjektálására vonatkozó további információkért lásd ASP.NET Corevezérlőibe történő függőséginjektálást.

A függőséginjektálás webalkalmazásokon kívüli alkalmazásokban való használatáról további információt a .NET-függőséginjektálásában talál.

A lehetőségek függőségi injektálásával kapcsolatos további információkért lásd Beállítások mintát ASP.NET Core.

Ez a témakör a ASP.NET Core-ban található függőséginjektálással kapcsolatos információkat tartalmazza. A függőséginjektálás használatának elsődleges dokumentációját a .NETFüggőséginjektálás tartalmazza.

Mintakód megtekintése vagy letöltése (hogyan lehet letölteni)

A függőséginjektálás áttekintése

A függőségi egy objektum, amelytől egy másik objektum függ. Vizsgálja meg a következő MyDependency osztályt egy WriteMessage metódussal, amelytől más osztályok függenek:

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

Az osztály létrehozhatja a MyDependency osztály egy példányát a WriteMessage metódusának használatához. Az alábbi példában a MyDependency osztály az IndexModel osztály függősége:


public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet");
    }
}

Az osztály létrejön és közvetlenül a MyDependency osztálytól függ. A kódfüggőségek, például az előző példában, problémásak, és a következő okok miatt kerülendők:

  • A MyDependency másik implementációra való lecseréléséhez módosítani kell a IndexModel osztályt.
  • Ha MyDependency függőségekkel rendelkezik, a IndexModel osztálynak is konfigurálnia kell őket. Egy nagy, MyDependencyfüggően több osztályt tartalmazó projektben a konfigurációs kód szétszóródik az alkalmazásban.
  • Ezt a megvalósítást nehéz egységtesztelni.

A függőséginjektálás a következő módon oldja meg ezeket a problémákat:

  • Egy interfész vagy alaposztály használata a függőségi implementáció absztrakciójára.
  • A függőség regisztrálása egy szolgáltatástárolóban. ASP.NET Core egy beépített szolgáltatástárolót biztosít, IServiceProvider. A szolgáltatások általában az alkalmazás Program.cs fájljában vannak regisztrálva.
  • szolgáltatás injektálása annak az osztálynak a konstruktorába, ahol a szolgáltatást használják. A keretrendszer felelősséget vállal a függőség egy példányának létrehozásáért és eltávolításáért, ha már nincs rá szükség.

A mintaalkalmazása IMyDependency felület határozza meg a WriteMessage metódust:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Ezt az interfészt egy konkrét típus valósítja meg, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

A mintaalkalmazás regisztrálja a IMyDependency szolgáltatást a konkrét MyDependencytípussal. A AddScoped metódus egy hatókörön belüli élettartammal, egyetlen kérelem élettartamával regisztrálja a szolgáltatást. szolgáltatás élettartamát a jelen témakör későbbi részében ismertetjük.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

A mintaalkalmazásban a rendszer a IMyDependency szolgáltatást kéri, és a WriteMessage metódus meghívására használja:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

A DI-minta használatával a vezérlő vagy Razor oldal:

  • Nem használja a konkrét MyDependencytípust, csak a IMyDependency felületet, amelyet implementál. Ez megkönnyíti a implementáció módosítását a vezérlő vagy Razor oldal módosítása nélkül.
  • Nem egy MyDependencypéldányt hoz létre; ezt a DI-tároló hozza létre.

A IMyDependency felület implementációja a beépített naplózási API használatával fejleszthető:

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

A frissített Program.cs regisztrálja az új IMyDependency implementációt:

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency2>();

var app = builder.Build();

Az MyDependency2 függ az ILogger<TCategoryName>-től, amelyet a konstruktorban kér. ILogger<TCategoryName> egy keretrendszer által biztosított szolgáltatás.

Nem szokatlan a függőséginjektálást láncsoltan alkalmazni. Minden kért függőség saját függőségeket kér. A tároló feloldja a gráf függőségeit, és visszaadja a teljes mértékben feloldott szolgáltatást. A feloldandó függőségek együttes készletét általában függőségi fa, függőségi gráfvagy objektumdiagramnevezik.

A tároló a ILogger<TCategoryName>használatával oldja fel a , így nincs szükség minden (általános) létrehozottregisztrálására.

A függőséginjektálás terminológiájában egy szolgáltatás:

  • Általában olyan objektum, amely szolgáltatást nyújt más objektumoknak, például a IMyDependency szolgáltatásnak.
  • Nem kapcsolódik webszolgáltatáshoz, bár a szolgáltatás webszolgáltatást is használhat.

A keretrendszer robusztus naplózási rendszert biztosít. Az előző példákban bemutatott IMyDependency implementációk azért lettek megírva, hogy az alapvető DI-t szemléltetik, és ne a naplózás implementálását. A legtöbb alkalmazásnak nem kell naplózókat írnia. Az alábbi kód bemutatja az alapértelmezett naplózás használatát, amely nem követeli meg a szolgáltatások regisztrálását:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; } = string.Empty;

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Az előző kód használatával nincs szükség a Program.csfrissítésére, mert a keretrendszer biztosítja a naplózási.

Szolgáltatáscsoportok regisztrálása bővítménymetelyekkel

A ASP.NET Core-keretrendszer konvenciót használ a kapcsolódó szolgáltatások egy csoportjának regisztrálására. A konvenció egyetlen Add{GROUP_NAME} bővítménymetódus használatával regisztrálja a keretrendszerfunkciók által igényelt összes szolgáltatást. A AddControllers bővítménymetódus például regisztrálja az MVC-vezérlőkhöz szükséges szolgáltatásokat.

A következő kódot a Razor Pages-sablon hozza létre egyéni felhasználói fiókok használatával, és bemutatja, hogyan adhat hozzá további szolgáltatásokat a tárolóhoz a bővítménymetelyek AddDbContext és AddDefaultIdentityhasználatával:

using DependencyInjectionSample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

Vegye figyelembe a következőket, amelyek regisztrálják a szolgáltatásokat, és konfigurálják a beállításokat:

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(
    builder.Configuration.GetSection(ColorOptions.Color));

builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();

var app = builder.Build();

A kapcsolódó regisztrációs csoportok áthelyezhetők egy bővítménymetódusba a szolgáltatások regisztrálásához. A konfigurációs szolgáltatások például a következő osztályhoz lesznek hozzáadva:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }

        public static IServiceCollection AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

A többi szolgáltatás hasonló osztályba van regisztrálva. Az alábbi kód az új bővítménymetelyeket használja a szolgáltatások regisztrálásához:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddConfig(builder.Configuration)
    .AddMyDependencyGroup();

builder.Services.AddRazorPages();

var app = builder.Build();

Megjegyzés: Minden services.Add{GROUP_NAME} bővítménymetódus szolgáltatásokat ad hozzá és konfigurál. Például AddControllersWithViews hozzáadja azokat a szolgáltatásokat, amelyekre az MVC vezérlőknek a nézetekkel szükségük van, és AddRazorPages hozzáadja azokat a szolgáltatásokat, amelyekre a Razor Lapoknak szükségük van.

Szolgáltatási élettartamok

Lásd a szolgáltatások élettartama a .NET függőség-injektálásban

A hatókörön belüli szolgáltatások köztes szoftverben való használatához használja az alábbi módszerek egyikét:

  • Injektálja a szolgáltatást a köztes szoftver Invoke vagy InvokeAsync metódusába. A konstruktorinjektálás használata futásidejű kivételt eredményez, mivel a hatókörön belüli szolgáltatás egyetlentonnként viselkedik. Az Élettartam és a regisztrációs lehetőségek szakaszban található minta bemutatja a InvokeAsync megközelítést.
  • Használja gyár által tervezett köztes szoftver. Az ezzel a megközelítéssel regisztrált köztes szoftver ügyfélkérésenként (kapcsolatonként) aktiválódik, ami lehetővé teszi, hogy a hatókörrel rendelkező szolgáltatások injektálhatók legyenek a köztes szoftver konstruktorába.

További információért lásd: Egyéni ASP.NET Core middleware komponensírása.

Szolgáltatásregisztrációs módszerek

Lásd a szolgáltatásregisztrációs módszereket a .NET függőséginjektálásában.

Gyakori, hogy több implementációt is használnak, ha teszteléséhez használt modelltípusokat.

A szolgáltatás csak implementációs típussal való regisztrálása egyenértékű azzal, ha a szolgáltatást ugyanazzal a megvalósítási és szolgáltatástípussal regisztrálja. Ez az oka annak, hogy egy szolgáltatás több implementációja nem regisztrálható olyan metódusokkal, amelyek nem használnak explicit szolgáltatástípust. Ezek a metódusok több példányt is regisztrálhatnak egy szolgáltatás, de mind ugyanazzal a implementálási típussal rendelkeznek.

A fenti szolgáltatásregisztrációs módszerek bármelyike használható több azonos szolgáltatástípusú szolgáltatáspéldány regisztrálásához. Az alábbi példában a AddSingleton-et kétszer hívják meg a IMyDependency szolgáltatástípussal. A második hívás AddSingleton esetén felülbírálja az előzőt, amikor IMyDependency -ként van feloldva, és hozzáadódik az előzőhöz, ha több szolgáltatás feloldása IEnumerable<IMyDependency>keresztül történik. A szolgáltatások a regisztrációjuk sorrendjében jelennek meg, amikor IEnumerable<{SERVICE}>-n keresztül oldják meg őket.

services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}

Konstruktorinjektálási viselkedés

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

Entity Framework-környezetek

Alapértelmezés szerint az Entity Framework-környezetek a hatókörrel rendelkező élettartam használatával vannak hozzáadva a szolgáltatástárolóhoz, mivel a webalkalmazás-adatbázis műveletei általában az ügyfélkérés hatókörébe tartoznak. Ha másik élettartam-paramétert szeretne használni, adja meg az élettartam-paramétert egy AddDbContext túlterhelés használatával. Egy adott élettartamú szolgáltatások nem használhatnak olyan adatbázis-környezetet, amelynek élettartama rövidebb, mint a szolgáltatás élettartama.

Élettartam és regisztrációs lehetőségek

A szolgáltatási élettartamok és a regisztrációs lehetőségek közötti különbség szemléltetéséhez vegye figyelembe a következő interfészeket, amelyek egy feladatot azonosítóval rendelkező műveletként jelölnek, OperationId. Attól függően, hogy egy művelet szolgáltatásának élettartama hogyan van konfigurálva a következő felületekhez, a tároló a szolgáltatás ugyanazon vagy különböző példányait biztosítja, amikor egy osztály kéri:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Az alábbi Operation osztály az összes előző felületet implementálja. A Operation konstruktor létrehoz egy GUID azonosítót, és az utolsó 4 karaktert a OperationId tulajdonságban tárolja:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

Az alábbi kód több regisztrációt hoz létre a Operation osztályból a megnevezett élettartamoknak megfelelően:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseMyMiddleware();
app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

A mintaalkalmazás bemutatja az objektumok élettartamát a kéréseken belül és között is. A IndexModel és a köztes szoftver minden IOperation típusú kérelmet elküld, és naplózza az egyes OperationId:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

A IndexModelhasonlóan a köztes szoftver ugyanazokat a szolgáltatásokat oldja fel:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationTransient transientOperation, IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + transientOperation.OperationId);
        _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

A hatókörrel és az átmeneti szolgáltatásokkal kapcsolatos problémákat a InvokeAsync metódusban kell feloldani:

public async Task InvokeAsync(HttpContext context,
    IOperationTransient transientOperation, IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + transientOperation.OperationId);
    _logger.LogInformation("Scoped: " + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

A naplózó kimenete a következőt jeleníti meg:

  • átmeneti objektumok mindig eltérőek. Az átmeneti OperationId érték eltér a IndexModel-ben és a köztes szoftverben.
  • hatókörrel rendelkező objektumok egy adott kérés esetében azonosak, de minden új kérelemben eltérnek.
  • Singleton objektumai minden kéréshez azonosak.

A naplózási kimenet csökkentéséhez állítsa be a "Naplózás:LogLevel:Microsoft:Error" értéket a appsettings.Development.json fájlban:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Szolgáltatás feloldása az alkalmazás indításakor

Az alábbi kód bemutatja, hogyan oldhat fel korlátozott ideig hatókörrel rendelkező szolgáltatást az alkalmazás indításakor:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var myDependency = services.GetRequiredService<IMyDependency>();
    myDependency.WriteMessage("Call services from main");
}

app.MapGet("/", () => "Hello World!");

app.Run();

Hatókör érvényesítése

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

További információ: Hatókör érvényesítése.

Szolgáltatások kérése

A szolgáltatások és függőségeik egy ASP.NET Core-kérelemben HttpContext.RequestServiceskeresztül érhetők el.

A keretrendszer kérésenként létrehoz egy hatókört, és RequestServices elérhetővé teszi a hatókörbe tartozó szolgáltatót. A hatókörön belüli szolgáltatások mindaddig érvényesek, amíg a kérés aktív.

Jegyzet

A függőségek konstruktorparaméterekként való kérését részesítse előnyben a szolgáltatások RequestServicesvaló feloldása helyett. A függőségek konstruktorparaméterként való kérése könnyebben tesztelhető osztályokat eredményez.

Tervezési szolgáltatások függőséginjektáláshoz

Függőséginjektálási szolgáltatások tervezésekor:

  • Kerülje az állapotalapú, statikus osztályokat és tagokat. Kerülje a globális állapot létrehozását azáltal, hogy az alkalmazásokat a singleton szolgáltatások használatára tervezi.
  • Kerülje a függő osztályok közvetlen példányosítását a szolgáltatásokban. A közvetlen példányosítás egy adott implementációhoz párosítja a kódot.
  • A szolgáltatások kicsi, jól faktorált és könnyen tesztelhetővé tétele.

Ha egy osztály sok injektált függőséget használ, annak jele lehet, hogy az osztály túl sok felelősséget vállal, és megsérti az Egységes felelősség elve (SRP). Próbálja meg újra bontani az osztályt úgy, hogy egyes feladatait új osztályokba helyezi át. Ne feledje, hogy Razor Lapok lapmodellosztályainak és MVC-vezérlőosztályainak a felhasználói felülettel kapcsolatos problémákra kell összpontosítaniuk.

Szolgáltatások megszüntetése

A tároló az általa létrehozott Dispose típusokhoz meghívja a IDisposable-t. A tárolóból feloldott szolgáltatásokat a fejlesztőnek soha nem szabad megsemmisítenie. Ha egy típus vagy gyár singletontként van regisztrálva, a konténer automatikusan megsemmisíti a singletont.

A következő példában a szolgáltatásokat a szolgáltatástároló hozza létre, és automatikusan megsemmisíti: dependency-injection\samples\6.x\DIsample2\DIsample2\Services\Service1.cs

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
using DIsample2.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();

var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));

var app = builder.Build();
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

A hibakeresési konzol a következő kimenetet jeleníti meg az Index lap minden egyes frissítése után:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose

A szolgáltatástároló által nem létrehozott szolgáltatások

Vegye figyelembe a következő kódot:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());

Az előző kódban:

  • A szolgáltatáspéldányokat nem a szolgáltatástároló hozza létre.
  • A keretrendszer nem szünteti meg automatikusan a szolgáltatásokat.
  • A szolgáltatások eltávolításáért a fejlesztő felel.

IDisposable útmutató átmeneti és megosztott példányokhoz

Lásd .NET- függőséginjektálásának átmeneti és megosztott példányainak IDisposable útmutatóját

Alapértelmezett szolgáltatástároló cseréje

Lásd Alapértelmezett szolgáltatástároló-csere a .NET- függőséginjektálásában

Ajánlások

Lásd .NET- függőséginjektálásának javaslatait

  • Kerülje a szolgáltatáskereső mintahasználatát. Például ne hívjon meg GetService szolgáltatáspéldány beszerzéséhez, ha használhatja a DI-t:

    helytelen:

    helytelen kód

    Helyes:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Egy másik szolgáltatáskereső változat, amely elkerülhető, egy olyan gyár injektálása, amely futásidőben oldja fel a függőségeket. Mindkét gyakorlat ötvözi a és a inverzióját a kontroll stratégiákban.

  • Kerülje a HttpContext statikus elérését (például IHttpContextAccessor.HttpContext).

A DI egy alternatív a statikus/globális objektumhozzáférési mintákhoz. Előfordulhat, hogy nem tudja kihasználni a DI előnyeit, ha statikus objektumhozzáféréssel keveri.

Orchard Core egy olyan alkalmazás-keretrendszer, amely moduláris, több-bérlős alkalmazások építésére szolgál az ASP.NET Core-on. További információkért lásd az Orchard Core dokumentációt.

Tekintse meg az Orchard Core-példákat, amelyek bemutatják, hogyan hozhat létre moduláris és több-bérlős alkalmazásokat az Orchard Core-keretrendszer CMS-specifikus funkciói használata nélkül.

Keretrendszer által biztosított szolgáltatások

Program.cs regisztrálja az alkalmazás által használt szolgáltatásokat, beleértve a platformfunkciókat, például az Entity Framework Core-t és a ASP.NET Core MVC-t. Kezdetben a IServiceCollection által Program.cs számára biztosított szolgáltatásokat a keretrendszer határozza meg, attól függően, hogy a gazdagép hogyan lett konfigurálva . A ASP.NET Core-sablonokon alapuló alkalmazások esetében a keretrendszer több mint 250 szolgáltatást regisztrál.

Az alábbi táblázat a keretrendszer által regisztrált szolgáltatások egy kis mintáját sorolja fel:

Szolgáltatás típusa Élettartam
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Átmeneti
IHostApplicationLifetime Singleton
IWebHostEnvironment Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Átmeneti
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Http.IHttpContextFactory Átmeneti
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Átmeneti
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton

További erőforrások

Írta Kirk Larkin, Steve Smith, Scott Addie, és Brandon Dahler

ASP.NET Core támogatja a függőséginjektálás (DI) szoftvertervezési mintáját, amely az osztályok és függőségeik közötti Vezérlés inverziója (IoC) elérésének technikája.

Az MVC-vezérlők függőséginjektálására vonatkozó további információkért lásd ASP.NET Corevezérlőibe történő függőséginjektálást.

A függőséginjektálás webalkalmazásokon kívüli alkalmazásokban való használatáról további információt a .NET-függőséginjektálásában talál.

A lehetőségek függőségi injektálásával kapcsolatos további információkért lásd Beállítások mintát ASP.NET Core.

Ez a témakör a ASP.NET Core-ban található függőséginjektálással kapcsolatos információkat tartalmazza. A függőséginjektálás használatának elsődleges dokumentációját a .NETFüggőséginjektálás tartalmazza.

Mintakód megtekintése vagy letöltése (hogyan lehet letölteni)

A függőséginjektálás áttekintése

A függőségi egy objektum, amelytől egy másik objektum függ. Vizsgálja meg a következő MyDependency osztályt egy WriteMessage metódussal, amelytől más osztályok függenek:

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

Az osztály létrehozhatja a MyDependency osztály egy példányát a WriteMessage metódusának használatához. Az alábbi példában a MyDependency osztály az IndexModel osztály függősége:

public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet created this message.");
    }
}

Az osztály létrejön és közvetlenül a MyDependency osztálytól függ. A kódfüggőségek, például az előző példában, problémásak, és a következő okok miatt kerülendők:

  • A MyDependency másik implementációra való lecseréléséhez módosítani kell a IndexModel osztályt.
  • Ha MyDependency függőségekkel rendelkezik, a IndexModel osztálynak is konfigurálnia kell őket. Egy nagy, MyDependencyfüggően több osztályt tartalmazó projektben a konfigurációs kód szétszóródik az alkalmazásban.
  • Ezt a megvalósítást nehéz egységtesztelni. Az alkalmazásnak egy mock vagy stub MyDependency osztályt kell használnia, ami ezzel a megközelítéssel nem lehetséges.

A függőséginjektálás a következő módon oldja meg ezeket a problémákat:

  • Egy interfész vagy alaposztály használata a függőségi implementáció absztrakciójára.
  • A függőség regisztrálása egy szolgáltatástárolóban. ASP.NET Core egy beépített szolgáltatástárolót biztosít, IServiceProvider. A szolgáltatások általában az alkalmazás Startup.ConfigureServices metódusában vannak regisztrálva.
  • szolgáltatás injektálása annak az osztálynak a konstruktorába, ahol a szolgáltatást használják. A keretrendszer felelősséget vállal a függőség egy példányának létrehozásáért és eltávolításáért, ha már nincs rá szükség.

A mintaalkalmazása IMyDependency felület határozza meg a WriteMessage metódust:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Ezt az interfészt egy konkrét típus valósítja meg, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

A mintaalkalmazás regisztrálja a IMyDependency szolgáltatást a konkrét MyDependencytípussal. A AddScoped metódus egy hatókörön belüli élettartammal, egyetlen kérelem élettartamával regisztrálja a szolgáltatást. szolgáltatás élettartamát a jelen témakör későbbi részében ismertetjük.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();

    services.AddRazorPages();
}

A mintaalkalmazásban a rendszer a IMyDependency szolgáltatást kéri, és a WriteMessage metódus meghívására használja:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

A DI-minta használatával a vezérlő:

  • Nem használja a konkrét MyDependencytípust, csak a IMyDependency felületet, amelyet implementál. Ez megkönnyíti a vezérlő által használt implementáció módosítását a vezérlő módosítása nélkül.
  • Nem egy MyDependencypéldányt hoz létre; ezt a DI-tároló hozza létre.

A IMyDependency felület implementációja a beépített naplózási API használatával fejleszthető:

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

A frissített ConfigureServices metódus regisztrálja az új IMyDependency implementációt:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency2>();

    services.AddRazorPages();
}

Az MyDependency2 függ az ILogger<TCategoryName>-től, amelyet a konstruktorban kér. ILogger<TCategoryName> egy keretrendszer által biztosított szolgáltatás.

Nem szokatlan a függőséginjektálást láncsoltan alkalmazni. Minden kért függőség saját függőségeket kér. A tároló feloldja a gráf függőségeit, és visszaadja a teljes mértékben feloldott szolgáltatást. A feloldandó függőségek együttes készletét általában függőségi fa, függőségi gráfvagy objektumdiagramnevezik.

A tároló a ILogger<TCategoryName>használatával oldja fel a , így nincs szükség minden (általános) létrehozottregisztrálására.

A függőséginjektálás terminológiájában egy szolgáltatás:

  • Általában olyan objektum, amely szolgáltatást nyújt más objektumoknak, például a IMyDependency szolgáltatásnak.
  • Nem kapcsolódik webszolgáltatáshoz, bár a szolgáltatás webszolgáltatást is használhat.

A keretrendszer robusztus naplózási rendszert biztosít. Az előző példákban bemutatott IMyDependency implementációk azért lettek megírva, hogy az alapvető DI-t szemléltetik, és ne a naplózás implementálását. A legtöbb alkalmazásnak nem kell naplózókat írnia. Az alábbi kód bemutatja az alapértelmezett naplózás használatát, amely nem követeli meg, hogy a szolgáltatások regisztrálva legyenek ConfigureServices:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Az előző kód használatával nincs szükség a ConfigureServicesfrissítésére, mert a keretrendszer biztosítja a naplózási.

A kiindulási folyamatba integrált szolgáltatások

A szolgáltatások injektálhatók a Startup konstruktorba és a Startup.Configure metódusba.

Csak a következő szolgáltatások injektálhatók a Startup konstruktorba a Generic Host (IHostBuilder) használatakor:

A DI-tárolóban regisztrált bármely szolgáltatás injektálható a Startup.Configure metódusba:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    ...
}

További információ: Alkalmazás indítása ASP.NET Core és Konfiguráció elérése a Startupban.

Szolgáltatáscsoportok regisztrálása bővítménymetelyekkel

A ASP.NET Core-keretrendszer konvenciót használ a kapcsolódó szolgáltatások egy csoportjának regisztrálására. A konvenció egyetlen Add{GROUP_NAME} bővítménymetódus használatával regisztrálja a keretrendszerfunkciók által igényelt összes szolgáltatást. A AddControllers bővítménymetódus például regisztrálja az MVC-vezérlőkhöz szükséges szolgáltatásokat.

A következő kódot a Razor Pages-sablon hozza létre egyéni felhasználói fiókok használatával, és bemutatja, hogyan adhat hozzá további szolgáltatásokat a tárolóhoz a bővítménymetelyek AddDbContext és AddDefaultIdentityhasználatával:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Vegye figyelembe a következő ConfigureServices metódust, amely regisztrálja a szolgáltatásokat és konfigurálja a beállításokat:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
        Configuration.GetSection(PositionOptions.Position));
    services.Configure<ColorOptions>(
        Configuration.GetSection(ColorOptions.Color));

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddScoped<IMyDependency2, MyDependency2>();

    services.AddRazorPages();
}

A kapcsolódó regisztrációs csoportok áthelyezhetők egy bővítménymetódusba a szolgáltatások regisztrálásához. A konfigurációs szolgáltatások például a következő osztályhoz lesznek hozzáadva:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }

        public static IServiceCollection AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

A többi szolgáltatás hasonló osztályba van regisztrálva. Az alábbi ConfigureServices metódus az új bővítménymetódusokat használja a szolgáltatások regisztrálásához:

public void ConfigureServices(IServiceCollection services)
{
    services.AddConfig(Configuration)
            .AddMyDependencyGroup();

    services.AddRazorPages();
}

Megjegyzés: Minden services.Add{GROUP_NAME} bővítménymetódus szolgáltatásokat ad hozzá és konfigurál. Például AddControllersWithViews hozzáadja azokat a szolgáltatásokat, amelyekre az MVC vezérlőknek a nézetekkel szükségük van, és AddRazorPages hozzáadja azokat a szolgáltatásokat, amelyekre a Razor Lapoknak szükségük van. Azt javasoljuk, hogy az alkalmazások kövessék a bővítménymetelyek Microsoft.Extensions.DependencyInjection névtérben való létrehozásának elnevezési konvencióját. Bővítménymetelyek létrehozása a Microsoft.Extensions.DependencyInjection névtérben:

  • A szolgáltatásregisztrációk csoportjait foglalja magában.
  • Kényelmes IntelliSense hozzáférést biztosít a szolgáltatáshoz.

Szolgáltatási élettartamok

Lásd a szolgáltatások élettartama a .NET függőség-injektálásban

A hatókörön belüli szolgáltatások köztes szoftverben való használatához használja az alábbi módszerek egyikét:

  • Injektálja a szolgáltatást a köztes szoftver Invoke vagy InvokeAsync metódusába. A konstruktorinjektálás használata futásidejű kivételt eredményez, mivel a hatókörön belüli szolgáltatás egyetlentonnként viselkedik. Az Élettartam és a regisztrációs lehetőségek szakaszban található minta bemutatja a InvokeAsync megközelítést.
  • Használja gyár által tervezett köztes szoftver. Az ezzel a módszerrel regisztrált köztes szoftver ügyfélkérelemenként (kapcsolatonként) aktiválódik, ami lehetővé teszi, hogy a hatókörrel rendelkező szolgáltatások injektálhatók legyenek a köztes szoftver InvokeAsync metódusába.

További információért lásd: Egyéni ASP.NET Core middleware komponensírása.

Szolgáltatásregisztrációs módszerek

Lásd a szolgáltatásregisztrációs módszereket a .NET függőséginjektálásában.

Gyakori, hogy több implementációt is használnak, ha teszteléséhez használt modelltípusokat.

A szolgáltatás csak implementációs típussal való regisztrálása egyenértékű azzal, ha a szolgáltatást ugyanazzal a megvalósítási és szolgáltatástípussal regisztrálja. Ez az oka annak, hogy egy szolgáltatás több implementációja nem regisztrálható olyan metódusokkal, amelyek nem használnak explicit szolgáltatástípust. Ezek a metódusok több példányt is regisztrálhatnak egy szolgáltatás, de mind ugyanazzal a implementálási típussal rendelkeznek.

A fenti szolgáltatásregisztrációs módszerek bármelyike használható több azonos szolgáltatástípusú szolgáltatáspéldány regisztrálásához. Az alábbi példában a AddSingleton-et kétszer hívják meg a IMyDependency szolgáltatástípussal. A második hívás AddSingleton esetén felülbírálja az előzőt, amikor IMyDependency -ként van feloldva, és hozzáadódik az előzőhöz, ha több szolgáltatás feloldása IEnumerable<IMyDependency>keresztül történik. A szolgáltatások a regisztrációjuk sorrendjében jelennek meg, amikor IEnumerable<{SERVICE}>-n keresztül oldják meg őket.

services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}

Konstruktorinjektálási viselkedés

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

Entity Framework-környezetek

Alapértelmezés szerint az Entity Framework-környezetek a hatókörrel rendelkező élettartam használatával vannak hozzáadva a szolgáltatástárolóhoz, mivel a webalkalmazás-adatbázis műveletei általában az ügyfélkérés hatókörébe tartoznak. Ha másik élettartam-paramétert szeretne használni, adja meg az élettartam-paramétert egy AddDbContext túlterhelés használatával. Egy adott élettartamú szolgáltatások nem használhatnak olyan adatbázis-környezetet, amelynek élettartama rövidebb, mint a szolgáltatás élettartama.

Élettartam és regisztrációs lehetőségek

A szolgáltatási élettartamok és a regisztrációs lehetőségek közötti különbség szemléltetéséhez vegye figyelembe a következő interfészeket, amelyek egy feladatot azonosítóval rendelkező műveletként jelölnek, OperationId. Attól függően, hogy egy művelet szolgáltatásának élettartama hogyan van konfigurálva a következő felületekhez, a tároló a szolgáltatás ugyanazon vagy különböző példányait biztosítja, amikor egy osztály kéri:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Az alábbi Operation osztály az összes előző felületet implementálja. A Operation konstruktor létrehoz egy GUID azonosítót, és az utolsó 4 karaktert a OperationId tulajdonságban tárolja:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

A Startup.ConfigureServices metódus több regisztrációt hoz létre a Operation osztályból az elnevezett élettartamoknak megfelelően:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();

    services.AddRazorPages();
}

A mintaalkalmazás bemutatja az objektumok élettartamát a kéréseken belül és között is. A IndexModel és a köztes szoftver minden IOperation típusú kérelmet elküld, és naplózza az egyes OperationId:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

A IndexModelhasonlóan a köztes szoftver ugyanazokat a szolgáltatásokat oldja fel:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationTransient transientOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

A hatókörön belüli szolgáltatásokat a InvokeAsync metódusban kell feloldani:

public async Task InvokeAsync(HttpContext context,
    IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + _transientOperation.OperationId);
    _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

A naplózó kimenete a következőt jeleníti meg:

  • átmeneti objektumok mindig eltérőek. Az átmeneti OperationId érték eltér a IndexModel-ben és a köztes szoftverben.
  • hatókörrel rendelkező objektumok egy adott kérés esetében azonosak, de minden új kérelemben eltérnek.
  • Singleton objektumai minden kéréshez azonosak.

A naplózási kimenet csökkentéséhez állítsa be a "Naplózás:LogLevel:Microsoft:Error" értéket a appsettings.Development.json fájlban:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Szolgáltatások hívása a fő menüből

Hozzon létre egy IServiceScope az IServiceScopeFactory.CreateScope segítségével az alkalmazás hatókörén belüli hatókörű szolgáltatás feloldásához. Ez a módszer akkor hasznos, ha indításkor egy hatókörrel rendelkező szolgáltatáshoz fér hozzá az inicializálási feladatok futtatásához.

Az alábbi példa bemutatja, hogyan érheti el a hatókörrel rendelkező IMyDependency szolgáltatást, és hogyan hívhatja meg WriteMessage metódusát a Program.Main:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myDependency = services.GetRequiredService<IMyDependency>();
                myDependency.WriteMessage("Call services from main");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Hatókör érvényesítése

Lásd: Konstruktorinjektálás viselkedéseFüggőséginjektálás a .NET-ben

További információ: Hatókör érvényesítése.

Szolgáltatások kérése

A szolgáltatások és függőségeik egy ASP.NET Core-kérelemben HttpContext.RequestServiceskeresztül érhetők el.

A keretrendszer kérésenként létrehoz egy hatókört, és RequestServices elérhetővé teszi a hatókörbe tartozó szolgáltatót. A hatókörön belüli szolgáltatások mindaddig érvényesek, amíg a kérés aktív.

Jegyzet

A függőségek konstruktorparaméterekként való kérését részesítse előnyben a szolgáltatások RequestServicesvaló feloldása helyett. A függőségek konstruktorparaméterként való kérése könnyebben tesztelhető osztályokat eredményez.

Tervezési szolgáltatások függőséginjektáláshoz

Függőséginjektálási szolgáltatások tervezésekor:

  • Kerülje az állapotalapú, statikus osztályokat és tagokat. Kerülje a globális állapot létrehozását azáltal, hogy az alkalmazásokat a singleton szolgáltatások használatára tervezi.
  • Kerülje a függő osztályok közvetlen példányosítását a szolgáltatásokban. A közvetlen példányosítás egy adott implementációhoz párosítja a kódot.
  • A szolgáltatások kicsi, jól faktorált és könnyen tesztelhetővé tétele.

Ha egy osztály sok injektált függőséget használ, annak jele lehet, hogy az osztály túl sok felelősséget vállal, és megsérti az Egységes felelősség elve (SRP). Próbálja meg újra bontani az osztályt úgy, hogy egyes feladatait új osztályokba helyezi át. Ne feledje, hogy Razor Lapok lapmodellosztályainak és MVC-vezérlőosztályainak a felhasználói felülettel kapcsolatos problémákra kell összpontosítaniuk.

Szolgáltatások megszüntetése

A tároló az általa létrehozott Dispose típusokhoz meghívja a IDisposable-t. A tárolóból feloldott szolgáltatásokat a fejlesztőnek soha nem szabad megsemmisítenie. Ha egy típus vagy gyár singletontként van regisztrálva, a konténer automatikusan megsemmisíti a singletont.

A következő példában a szolgáltatástároló hozza létre a szolgáltatásokat, és automatikusan megsemmisíti:

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    
    var myKey = Configuration["MyKey"];
    services.AddSingleton<IService3>(sp => new Service3(myKey));

    services.AddRazorPages();
}
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

A hibakeresési konzol a következő kimenetet jeleníti meg az Index lap minden egyes frissítése után:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, MyKey = My Key from config
Service1.Dispose

A szolgáltatástároló által nem létrehozott szolgáltatások

Vegye figyelembe a következő kódot:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new Service1());
    services.AddSingleton(new Service2());

    services.AddRazorPages();
}

Az előző kódban:

  • A szolgáltatáspéldányokat nem a szolgáltatástároló hozza létre.
  • A keretrendszer nem szünteti meg automatikusan a szolgáltatásokat.
  • A szolgáltatások eltávolításáért a fejlesztő felel.

IDisposable útmutató átmeneti és megosztott példányokhoz

Lásd .NET- függőséginjektálásának átmeneti és megosztott példányainak IDisposable útmutatóját

Alapértelmezett szolgáltatástároló cseréje

Lásd Alapértelmezett szolgáltatástároló-csere a .NET- függőséginjektálásában

Ajánlások

Lásd .NET- függőséginjektálásának javaslatait

  • Kerülje a szolgáltatáskereső mintahasználatát. Például ne hívjon meg GetService szolgáltatáspéldány beszerzéséhez, ha használhatja a DI-t:

    helytelen:

    helytelen kód

    Helyes:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Egy másik szolgáltatáskereső változat, amely elkerülhető, egy olyan gyár injektálása, amely futásidőben oldja fel a függőségeket. Mindkét gyakorlat ötvözi a és a inverzióját a kontroll stratégiákban.

  • Kerülje a HttpContext statikus elérését (például IHttpContextAccessor.HttpContext).

  • Kerülje a BuildServiceProvider hívásait a ConfigureServices-ben. A BuildServiceProvider hívása általában akkor történik, amikor a fejlesztő egy szolgáltatást akar feloldani a ConfigureServices-ben. Vegyük például azt az esetet, amikor a LoginPath a konfigurációból töltődik be. Kerülje a következő megközelítést:

    a BuildServiceProvider meghívására vonatkozó hibás kód

    Az előző képen, ha kiválasztja a services.BuildServiceProvider alatti zöld hullámos vonalat, a következő ASP0000 figyelmeztetés jelenik meg:

    ASP0000 BuildServiceProvider alkalmazáskódból való meghívása az egyszeri szolgáltatások további másolatát eredményezi. Fontolja meg az olyan alternatív megoldásokat, mint a függőségi injektálási szolgáltatások a "Konfigurálás" paramétereként.

    A BuildServiceProvider hívása egy második tárolót hoz létre, amely széttört singletonokat hozhat létre, és több tároló objektumgráfjaira mutató hivatkozásokat okozhat.

    A LoginPath megszerzésének helyes módja az, hogy használjuk az opciók sablonjához tartozó beépített támogatást a DI-hez.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
    
        services.AddOptions<CookieAuthenticationOptions>(
                            CookieAuthenticationDefaults.AuthenticationScheme)
            .Configure<IMyService>((options, myService) =>
            {
                options.LoginPath = myService.GetLoginPath();
            });
    
        services.AddRazorPages();
    }
    
  • Az eldobható átmeneti szolgáltatásokat a tároló rögzíti az ártalmatlanításhoz. Ez memóriaszivárgássá alakulhat, ha a legfelső szintű tárolóból oldják fel.

  • Engedélyezze a hatókör-ellenőrzést annak érdekében, hogy az alkalmazás ne rendelkezzen hatóköralapú szolgáltatásokat rögzítő egytonnával. További információ: Hatókör érvényesítése.

A javaslatokhoz hasonlóan olyan helyzetek is előfordulhatnak, amikor figyelmen kívül kell hagyni egy javaslatot. A kivételek ritkák, többnyire különleges esetek a keretrendszeren belül.

A DI egy alternatív a statikus/globális objektumhozzáférési mintákhoz. Előfordulhat, hogy nem tudja kihasználni a DI előnyeit, ha statikus objektumhozzáféréssel keveri.

Orchard Core egy olyan alkalmazás-keretrendszer, amely moduláris, több-bérlős alkalmazások építésére szolgál az ASP.NET Core-on. További információkért lásd az Orchard Core dokumentációt.

Tekintse meg az Orchard Core-példákat, amelyek bemutatják, hogyan hozhat létre moduláris és több-bérlős alkalmazásokat az Orchard Core-keretrendszer CMS-specifikus funkciói használata nélkül.

Keretrendszer által biztosított szolgáltatások

A Startup.ConfigureServices metódus regisztrálja az alkalmazás által használt szolgáltatásokat, beleértve a platformfunkciókat, például az Entity Framework Core-t és a ASP.NET Core MVC-t. Kezdetben a IServiceCollection által ConfigureServices számára biztosított szolgáltatásokat a keretrendszer határozza meg, attól függően, hogy a gazdagép hogyan lett konfigurálva . A ASP.NET Core-sablonokon alapuló alkalmazások esetében a keretrendszer több mint 250 szolgáltatást regisztrál.

Az alábbi táblázat a keretrendszer által regisztrált szolgáltatások egy kis mintáját sorolja fel:

Szolgáltatás típusa Élettartam
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Átmeneti
IHostApplicationLifetime Singleton
IWebHostEnvironment Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Átmeneti
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Http.IHttpContextFactory Átmeneti
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Átmeneti
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton

További erőforrások