Függőséginjektálás a ASP.NET Core-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-
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 .NET
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 aIndexModel
osztályt. - Ha
MyDependency
függőségekkel rendelkezik, aIndexModel
osztálynak is konfigurálnia kell őket. Egy nagy,MyDependency
fü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 MyDependency
tí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
MyDependency
típust, csak aIMyDependency
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
MyDependency
pé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
vagyInvokeAsync
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 aInvokeAsync
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 IndexModel
hasonló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 aIndexModel
-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 RequestServices
való 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
Alapértelmezett szolgáltatástároló cseréje
Lásd
Ajánlások
Lásd
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:
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.
Ajánlott minták a több-bérlős architektúrákhoz a DI-ben
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
- ASP.NET Core Blazor függőséginjektálás
- Függőségi injektálás a ASP.NET Core nézeteibe
- Függőségek befecskendezése az ASP.NET Core vezérlőkbe
- Függőséginjektálás a ASP.NET Core követelménykezelőiben
- NDC konferencia minták DI alkalmazásfejlesztéshez
- alkalmazás indítása a ASP.NET Core
- Gyári alapú köztes szoftver aktiválása ASP.NET Core
- A függőségi injektálás alapjainak megismerése a .NET-ben
- függőség-injektálási irányelvek
- Útmutató: függőség-injektálás használata a .NET-ben
- .NET-függőséginjektálás
- ASP.NET CORE DEPENDENCY INJECTION: MI AZ ISERVICECOLLECTION?
- Négy módszer az IDisposables kezelésére ASP.NET Core-ban
- Clean Code írása ASP.NET Core-ban függőséginjektálással (MSDN)
- explicit függőségek elve
- Inverz vezérlőtárolók és a függőséginjektálási minta (Martin Fowler)
- Szolgáltatás regisztrálása több felülettel ASP.NET Core DI-
Á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-
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 .NET
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 aIndexModel
osztályt. - Ha
MyDependency
függőségekkel rendelkezik, aIndexModel
osztálynak is konfigurálnia kell őket. Egy nagy,MyDependency
fü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 MyDependency
tí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
MyDependency
típust, csak aIMyDependency
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
MyDependency
pé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.cs
frissí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
vagyInvokeAsync
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 aInvokeAsync
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 IndexModel
hasonló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 aIndexModel
-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 RequestServices
való 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
Alapértelmezett szolgáltatástároló cseréje
Lásd
Ajánlások
Lásd
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:
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.
Ajánlott minták a több-bérlős architektúrákhoz a DI-ben
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
- Függőségi injektálás a ASP.NET Core nézeteibe
- Függőségek befecskendezése az ASP.NET Core vezérlőkbe
- Függőséginjektálás a ASP.NET Core követelménykezelőiben
- ASP.NET Core Blazor függőséginjektálás
- NDC konferencia minták DI alkalmazásfejlesztéshez
- alkalmazás indítása a ASP.NET Core
- Gyári alapú köztes szoftver aktiválása ASP.NET Core
- A függőségi injektálás alapjainak megismerése a .NET-ben
- függőség-injektálási irányelvek
- Útmutató: függőség-injektálás használata a .NET-ben
- .NET-függőséginjektálás
- ASP.NET CORE DEPENDENCY INJECTION: MI AZ ISERVICECOLLECTION?
- Négy módszer az IDisposables kezelésére ASP.NET Core-ban
- Clean Code írása ASP.NET Core-ban függőséginjektálással (MSDN)
- explicit függőségek elve
- Inverz vezérlőtárolók és a függőséginjektálási minta (Martin Fowler)
- Szolgáltatás regisztrálása több felülettel ASP.NET Core DI-
Á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-
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 .NET
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 aIndexModel
osztályt. - Ha
MyDependency
függőségekkel rendelkezik, aIndexModel
osztálynak is konfigurálnia kell őket. Egy nagy,MyDependency
fü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 MyDependency
tí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
MyDependency
típust, csak aIMyDependency
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
MyDependency
pé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.cs
frissí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
vagyInvokeAsync
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 aInvokeAsync
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 IndexModel
hasonló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 aIndexModel
-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 RequestServices
való 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
Alapértelmezett szolgáltatástároló cseréje
Lásd
Ajánlások
Lásd
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:
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.
Ajánlott minták a több-bérlős architektúrákhoz a DI-ben
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
- Függőségi injektálás a ASP.NET Core nézeteibe
- Függőségek befecskendezése az ASP.NET Core vezérlőkbe
- Függőséginjektálás a ASP.NET Core követelménykezelőiben
- ASP.NET Core Blazor függőséginjektálás
- NDC konferencia minták DI alkalmazásfejlesztéshez
- alkalmazás indítása a ASP.NET Core
- Gyári alapú köztes szoftver aktiválása ASP.NET Core
- Négy módszer az IDisposables kezelésére ASP.NET Core-ban
- Clean Code írása ASP.NET Core-ban függőséginjektálással (MSDN)
- explicit függőségek elve
- Inverz vezérlőtárolók és a függőséginjektálási minta (Martin Fowler)
- Szolgáltatás regisztrálása több felülettel ASP.NET Core DI-
Í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-
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 .NET
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 aIndexModel
osztályt. - Ha
MyDependency
függőségekkel rendelkezik, aIndexModel
osztálynak is konfigurálnia kell őket. Egy nagy,MyDependency
fü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 MyDependency
tí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
MyDependency
típust, csak aIMyDependency
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
MyDependency
pé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 ConfigureServices
frissí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
vagyInvokeAsync
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 aInvokeAsync
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 IndexModel
hasonló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 aIndexModel
-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 RequestServices
való 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
Alapértelmezett szolgáltatástároló cseréje
Lásd
Ajánlások
Lásd
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:
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. ABuildServiceProvider
hívása általában akkor történik, amikor a fejlesztő egy szolgáltatást akar feloldani aConfigureServices
-ben. Vegyük például azt az esetet, amikor aLoginPath
a konfigurációból töltődik be. Kerülje a következő megközelítést: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.
Ajánlott minták a több-bérlős architektúrákhoz a DI-ben
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
- Függőségi injektálás a ASP.NET Core nézeteibe
- Függőségek befecskendezése az ASP.NET Core vezérlőkbe
- Függőséginjektálás a ASP.NET Core követelménykezelőiben
- ASP.NET Core Blazor függőséginjektálás
- NDC konferencia minták DI alkalmazásfejlesztéshez
- alkalmazás indítása a ASP.NET Core
- Gyári alapú köztes szoftver aktiválása ASP.NET Core
- Négy módszer az IDisposables kezelésére ASP.NET Core-ban
- Clean Code írása ASP.NET Core-ban függőséginjektálással (MSDN)
- explicit függőségek elve
- Inverz vezérlőtárolók és a függőséginjektálási minta (Martin Fowler)
- Szolgáltatás regisztrálása több felülettel ASP.NET Core DI-