ASP.NET Core: Dependency Injection trong ASP.NET Core
ASP.NET Core hỗ trợ mẫu thiết kế phần mềm Dependency Injection (DI), đây là một kỹ thuật để đạt được Đảo ngược Kiểm soát (IoC) giữa các lớp và các phần phụ thuộc của chúng.
Để biết thêm thông tin cụ thể về DI trong controller MVC, hãy xem Nội dung phụ thuộc vào controller trong ASP.NET Core.
Để biết thông tin về cách sử dụng DI trong các ứng dụng không phải ứng dụng web, hãy xem Dependency Injection trong .NET.
Để biết thêm thông tin về việc đưa các tùy chọn vào phần phụ thuộc, hãy xem Mẫu tùy chọn trong ASP.NET Core.
Chủ đề này cung cấp thông tin về DI trong ASP.NET Core. Tài liệu chính về việc sử dụng DI có trong Dependency Injection trong .NET.
Xem hoặc tải xuống mã mẫu (cách tải xuống)
Tổng quan về Dependency Injection
Một dependency là một đối tượng mà một đối tượng khác phụ thuộc vào. Kiểm tra lớp MyDependency
sau với một phương thức WriteMessage
mà các lớp khác phụ thuộc vào:
public class MyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
}
}
Một lớp có thể tạo một thể hiện của lớp MyDependency
để sử dụng phương thức WriteMessage
của nó. Trong ví dụ sau, lớp MyDependency
là một phụ thuộc của lớp IndexModel
:
public class IndexModel : PageModel
{
private readonly MyDependency _dependency = new MyDependency();
public void OnGet()
{
_dependency.WriteMessage("IndexModel.OnGet");
}
}
Lớp tạo và phụ thuộc trực tiếp vào lớp MyDependency
. Các phụ thuộc code, chẳng hạn như trong ví dụ trên, có vấn đề và nên tránh vì những lý do sau:
- Để thay thế
MyDependency
bằng một triển khai khác, thì lớpIndexModel
phải được sửa đổi. - Nếu
MyDependency
có các phụ thuộc, chúng cũng phải được cấu hình bởi lớpIndexModel
. Trong một dự án lớn có nhiều lớp tùy thuộc vàoMyDependency
, code cấu hình sẽ nằm rải rác trên ứng dụng. - Việc triển khai này rất khó để kiểm tra đơn vị.
Dependency Injection giải quyết những vấn đề này thông qua:
- Việc sử dụng một interface hoặc lớp cơ sở để trừu tượng hóa việc triển khai phụ thuộc.
- Đăng ký phần phụ thuộc trong vùng chứa dịch vụ. ASP.NET Core cung cấp một bộ chứa dịch vụ tích hợp sẵn, đó là IServiceProvider. Các dịch vụ thường được đăng ký trong file
Program.cs
của ứng dụng. - Injection dịch vụ vào hàm tạo của lớp mà nó được sử dụng. Framework đảm nhận trách nhiệm tạo một thể hiện của sự phụ thuộc và xử lý nó khi không còn cần thiết.
Trong ứng dụng mẫu, interface IMyDependency
khai báo phương thức WriteMessage
:
public interface IMyDependency
{
void WriteMessage(string message);
}
Interface này được thực hiện bởi một kiểu cụ thể, đó là MyDependency
:
public class MyDependency : IMyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
}
}
Ứng dụng mẫu đăng ký dịch vụ IMyDependency
với kiểu cụ thể MyDependency
. Phương thức AddScoped đăng ký dịch vụ với thời gian tồn tại trong phạm vi, thời gian tồn tại của một yêu cầu. Tuổi thọ của dịch vụ được mô tả sau trong chủ đề này.
using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddScoped<IMyDependency, MyDependency>();
var app = builder.Build();
Trong ứng dụng mẫu, dịch vụ IMyDependency
được yêu cầu và được sử dụng để gọi phương thức WriteMessage
:
public class Index2Model : PageModel
{
private readonly IMyDependency _myDependency;
public Index2Model(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public void OnGet()
{
_myDependency.WriteMessage("Index2Model.OnGet");
}
}
Bằng cách sử dụng mẫu DI, controller hoặc Razor Page:
- Không sử dụng kiểu cụ thể
MyDependency
, chỉ thực thi interfaceIMyDependency
. Điều đó giúp dễ dàng thay đổi việc triển khai mà không cần sửa đổi controller hoặc Razor Page. - Không tạo thể hiện của
MyDependency
, nó được tạo bởi bộ chứa DI.
Việc thực thi interface IMyDependency
có thể được cải thiện bằng cách sử dụng API ghi nhật ký tích hợp:
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}");
}
}
Bản cập nhật file Program.cs
sẽ tiến hành đăng ký thực thi IMyDependency
mới:
using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddScoped<IMyDependency, MyDependency2>();
var app = builder.Build();
MyDependency2
phụ thuộc vào ILogger<TCategoryName> mà nó yêu cầu trong hàm tạo. ILogger<TCategoryName>
là một dịch vụ do framework cung cấp.
Không có gì lạ khi sử dụng DI theo kiểu chuỗi. Mỗi phụ thuộc được yêu cầu lần lượt yêu cầu các phụ thuộc của chính nó. Vùng chứa giải quyết các phụ thuộc trong biểu đồ và trả về dịch vụ được giải quyết đầy đủ. Tập hợp chung các phụ thuộc phải được giải quyết thường được gọi là cây phụ thuộc, đồ thị phụ thuộc hoặc đồ thị đối tượng.
Vùng chứa giải quyết ILogger<TCategoryName>
bằng cách tận dụng các kiểu mở (generic), loại bỏ nhu cầu đăng ký mọi kiểu được xây dựng (generic).
Trong thuật ngữ DI, một dịch vụ:
- Thường là một đối tượng cung cấp dịch vụ cho các đối tượng khác, chẳng hạn như dịch vụ
IMyDependency
. - Không liên quan đến dịch vụ web, mặc dù dịch vụ này có thể sử dụng dịch vụ web.
Framework này cung cấp một hệ thống ghi nhật ký mạnh mẽ. Việc thực thi IMyDependency
hiển thị trong các ví dụ trên được viết để minh họa DI cơ bản, không phải để triển khai ghi nhật ký. Hầu hết các ứng dụng không cần phải ghi nhật ký. Đoạn mã sau minh họa việc sử dụng ghi nhật ký mặc định, không yêu cầu đăng ký bất kỳ dịch vụ nào:
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);
}
}
Sử dụng đoạn code trên sẽ không cần phải cập nhật Program.cs
, vì việc ghi nhật ký được cung cấp bởi framework.
Đăng ký các nhóm dịch vụ với các phương thức mở rộng
Framework ASP.NET Core sử dụng quy ước để đăng ký một nhóm các dịch vụ liên quan. Quy ước là sử dụng một phương thức mở rộng Add{GROUP_NAME}
duy nhất để đăng ký tất cả các dịch vụ theo yêu cầu của tính năng framework. Ví dụ: phương thức mở rộng AddControllers đăng ký các dịch vụ cần thiết cho controller MVC.
Đoạn code sau đây được tạo bởi mẫu Razor Pages bằng cách sử dụng tài khoản người dùng cá nhân và cho biết cách thêm các dịch vụ bổ sung vào vùng chứa bằng các phương thức tiện ích mở rộng AddDbContext và AddDefaultIdentity:
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();
Hãy xem xét những điều sau đây đăng ký dịch vụ và cấu hình các tùy chọn:
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();
Các nhóm đăng ký liên quan có thể được chuyển sang phương thức mở rộng để đăng ký dịch vụ. Ví dụ: các dịch vụ cấu hình được thêm vào lớp sau:
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;
}
}
}
Các dịch vụ còn lại được đăng ký trong một lớp tương tự. Đoạn mã sau sử dụng các phương thức tiện ích mở rộng mới để đăng ký dịch vụ:
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddConfig(builder.Configuration)
.AddMyDependencyGroup();
builder.Services.AddRazorPages();
var app = builder.Build();
Lưu ý: Mỗi phương thức mở rộng services.Add{GROUP_NAME}
thêm và có khả năng định cấu hình dịch vụ. Ví dụ: AddControllersWithViews thêm các dịch vụ mà controller MVC có view yêu cầu và AddRazorPages thêm các dịch vụ mà Razor Pages yêu cầu.
Tuổi thọ của dịch vụ
Xem Thời gian tồn tại của dịch vụ trong Dependency Injection trong .NET
Để sử dụng các dịch vụ có phạm vi trong middleware, hãy sử dụng một trong các phương pháp sau:
- Đưa dịch vụ vào phương thức
Invoke
hoặc của middlewareInvokeAsync
. Việc sử dụng injection hàm tạo sẽ đưa ra một ngoại lệ thời gian chạy vì nó buộc dịch vụ được xác định phạm vi hoạt động giống như một đơn vị. Mẫu trong phần Tùy chọn đăng ký và trọn đời thể hiện cách tiếp cậnInvokeAsync
. - Sử dụng middleware dựa trên Factory. Middleware đã đăng ký sử dụng phương pháp này được kích hoạt theo yêu cầu của khách hàng (kết nối), cho phép các dịch vụ trong phạm vi được đưa vào hàm tạo của middleware.
Để biết thêm thông tin, hãy xem Viết middleware ASP.NET Core tùy chỉnh.
Phương thức đăng ký dịch vụ
Xem Các phương thức đăng ký dịch vụ trong Dependency Injection trong .NET
Việc sử dụng nhiều triển khai khi mô phỏng các kiểu để thử nghiệm là điều phổ biến.
Đăng ký một dịch vụ chỉ với một loại triển khai tương đương với việc đăng ký dịch vụ đó với cùng một loại triển khai và dịch vụ. Đây là lý do tại sao không thể đăng ký nhiều lần triển khai dịch vụ bằng các phương thức không sử dụng loại dịch vụ rõ ràng. Các phương thức này có thể đăng ký nhiều thể hiện của một dịch vụ, nhưng tất cả chúng sẽ có cùng kiểu thực thi.
Bất kỳ phương thức đăng ký dịch vụ nào ở trên đều có thể được sử dụng để đăng ký nhiều thể hiện dịch vụ của cùng một loại dịch vụ. Trong ví dụ sau, AddSingleton
được gọi hai lần với IMyDependency
như là kiểu dịch vụ. Lời gọi thứ hai để AddSingleton
ghi đè lời gọi trước đó khi được giải quyết IMyDependency
và thêm vào lời gọi trước đó khi nhiều dịch vụ được giải quyết qua IEnumerable<IMyDependency>
. Các dịch vụ xuất hiện theo thứ tự chúng đã được đăng ký khi được giải quyết qua IEnumerable<{SERVICE}>
.
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);
}
}
Hành vi injection hàm tạo
Xem Hành vi injection hàm tạo trong Dependency injection trong .NET
Ngữ cảnh Entity Framework
Theo mặc định, ngữ cảnh Entity Framework được thêm vào bộ chứa dịch vụ bằng cách sử dụng thời gian tồn tại trong phạm vi vì các hoạt động cơ sở dữ liệu ứng dụng web thường được đặt trong phạm vi yêu cầu của máy khách. Để sử dụng thời gian tồn tại khác, hãy chỉ định thời gian tồn tại bằng cách sử dụng quá tải AddDbContext. Các dịch vụ có thời gian tồn tại nhất định không được sử dụng database context có thời gian tồn tại ngắn hơn thời gian tồn tại của dịch vụ.
Tùy chọn trọn đời và đăng ký
Để chứng minh sự khác biệt giữa thời gian tồn tại của dịch vụ và các tùy chọn đăng ký của chúng, hãy xem xét các giao diện sau biểu thị một tác vụ dưới dạng hoạt động với mã định danh là OperationId
. Tùy thuộc vào thời gian tồn tại của dịch vụ của một hoạt động được định cấu hình cho các interface sau, bộ chứa cung cấp các phiên bản giống hoặc khác nhau của dịch vụ khi được một lớp yêu cầu:
public interface IOperation
{
string OperationId { get; }
}
public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }
Lớp Operation
sau thực thi tất cả các interface ở trên. Hàm tạo Operation
tạo GUID và lưu trữ 4 ký tự cuối cùng trong property OperationId
:
public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
public Operation()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string OperationId { get; }
}
Đoạn mã sau tạo nhiều đăng ký của lớp Operation
theo thời gian tồn tại được đặt tên:
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();
Ứng dụng mẫu thể hiện thời gian tồn tại của đối tượng cả trong và giữa các yêu cầu. IndexModel
và middleware yêu cầu từng loại IOperation
và ghi nhật ký OperationId
cho từng loại:
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);
}
}
Tương tự như IndexModel
, middleware giải quyết các dịch vụ tương tự:
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>();
}
}
Các dịch vụ có phạm vi và nhất thời phải được giải quyết theo phương thức InvokeAsync
:
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);
}
Đầu ra của trình ghi cho thấy:
- Các đối tượng thoáng qua luôn khác nhau. Giá trị nhất thời
OperationId
là khác nhau trongIndexModel
và trong middleware. - Các đối tượng trong phạm vi giống nhau đối với một yêu cầu nhất định nhưng khác nhau đối với mỗi yêu cầu mới.
- Các đối tượng Singleton giống nhau cho mọi yêu cầu.
Để giảm đầu ra ghi nhật ký, hãy đặt "Logging:LogLevel:Microsoft:Error" trong file appsettings.Development.json
:
{
"MyKey": "MyKey from appsettings.Developement.json",
"Logging": {
"LogLevel": {
"Default": "Information",
"System": "Debug",
"Microsoft": "Error"
}
}
}
Giải quyết một dịch vụ khi khởi động ứng dụng
Đoạn mã sau cho biết cách giải quyết một dịch vụ có phạm vi trong khoảng thời gian giới hạn khi ứng dụng khởi động:
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();
Xác thực phạm vi
Xem Hành vi injection hàm tạo trong Dependency Injection trong .NET
Để biết thêm thông tin, hãy xem Xác thực phạm vi.
Yêu cầu dịch vụ
Các dịch vụ và phần phụ thuộc của chúng trong một yêu cầu ASP.NET Core được hiển thị thông qua HttpContext.RequestServices.
Framework tạo phạm vi cho mỗi yêu cầu và RequestServices
hiển thị nhà cung cấp dịch vụ có phạm vi. Tất cả các dịch vụ trong phạm vi đều hợp lệ miễn là yêu cầu đang hoạt động.
Ghi chú
Nên yêu cầu các phụ thuộc làm tham số hàm tạo hơn là giải quyết các dịch vụ từ RequestServices
. Yêu cầu các phụ thuộc làm tham số hàm tạo sẽ tạo ra các lớp dễ kiểm tra hơn.
Dịch vụ thiết kế cho DI
Khi thiết kế các dịch vụ để tiêm phụ thuộc:
- Tránh các lớp và thành viên có trạng thái và tĩnh. Thay vào đó, hãy tránh tạo trạng thái global bằng cách thiết kế các ứng dụng để sử dụng các dịch vụ đơn lẻ.
- Tránh khởi tạo trực tiếp các lớp phụ thuộc trong các dịch vụ. Khởi tạo trực tiếp kết hợp code với một triển khai cụ thể.
- Làm cho các dịch vụ trở nên nhỏ gọn, được tính toán hợp lý và dễ dàng kiểm tra.
Nếu một lớp có nhiều phụ thuộc được đưa vào, đó có thể là dấu hiệu cho thấy lớp đó có quá nhiều trách nhiệm và vi phạm Nguyên tắc Trách nhiệm Đơn lẻ (SRP). Cố gắng cấu trúc lại lớp bằng cách chuyển một số trách nhiệm của nó sang các lớp mới. Hãy nhớ rằng các lớp mô hình trang Razor Pages và các lớp controller MVC nên tập trung vào các mối quan tâm về giao diện người dùng.
Xử lý các dịch vụ
Vùng chứa gọi Dispose cho các kiểu IDisposable mà nó tạo. Các dịch vụ được giải quyết từ vùng chứa không bao giờ được nhà phát triển loại bỏ. Nếu một kiểu hoặc factory được đăng ký dưới dạng một singleton, thì vùng chứa sẽ tự động loại bỏ singleton đó.
Trong ví dụ sau, các dịch vụ được tạo bởi bộ chứa dịch vụ và được loại bỏ tự động: 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");
}
}
Console gỡ lỗi hiển thị đầu ra sau mỗi lần làm mới trang Index:
Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose
Các dịch vụ không được tạo bởi bộ chứa dịch vụ
Hãy xem xét đoạn mã sau:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());
Trong đoạn code trên:
- Các phiên bản dịch vụ không được tạo bởi bộ chứa dịch vụ.
- Framework không tự động xử lý các dịch vụ.
- Nhà phát triển chịu trách nhiệm xử lý các dịch vụ.
Hướng dẫn IDisposable dành cho Phiên bản tạm thời và phiên bản dùng chung
Xem hướng dẫn IDisposable cho Phiên bản tạm thời và được chia sẻ trong Dependency Injection trong .NET
Thay thế vùng chứa dịch vụ mặc định
Xem Thay thế bộ chứa dịch vụ mặc định trong Dependency Injection trong .NET
Khuyến nghị
Xem các đề xuất trong Dependency Injection trong .NET
- Tránh sử dụng mẫu định vị dịch vụ. Ví dụ: không gọi GetService để lấy phiên bản dịch vụ khi bạn có thể sử dụng DI thay thế:
Như sau sẽ không đúng:
Như sau là chính xác:
public class MyClass
{
private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
{
_optionsMonitor = optionsMonitor;
}
public void MyMethod()
{
var option = _optionsMonitor.CurrentValue.Option;
...
}
}
- Một biến thể định vị dịch vụ khác cần tránh là đưa vào một factory giải quyết các phụ thuộc trong thời gian chạy. Cả hai thực tế này kết hợp các chiến lược Đảo ngược Kiểm soát.
- Tránh truy cập tĩnh vào
HttpContext
(ví dụ: IHttpContextAccessor.HttpContext).
DI là một giải pháp thay thế cho các mẫu truy cập đối tượng tĩnh/toàn cục. Bạn có thể không nhận ra được những lợi ích của DI nếu bạn kết hợp nó với truy cập đối tượng tĩnh.
Các mẫu được đề xuất cho nhiều bên thuê trong DI
Orchard Core là một framework ứng dụng để xây dựng các ứng dụng mô-đun nhiều bên thuê trên ASP.NET Core. Để biết thêm thông tin, hãy xem Tài liệu cốt lõi của Orchard.
Xem các mẫu Orchard Core để biết ví dụ về cách xây dựng các ứng dụng mô-đun và nhiều bên thuê chỉ sử dụng Orchard Core Framework mà không có bất kỳ tính năng cụ thể nào của CMS.
Các dịch vụ do framework cung cấp
Program.cs
đăng ký các dịch vụ mà ứng dụng sử dụng, bao gồm các tính năng nền tảng, chẳng hạn như Entity Framework Core và ASP.NET Core MVC. Ban đầu, dịch vụ IServiceCollection
được cung cấp để Program.cs
có các dịch vụ được xác định bởi framework tùy thuộc vào cách máy chủ được định cấu hình. Đối với các ứng dụng dựa trên mẫu ASP.NET Core, framework đăng ký hơn 250 dịch vụ.
Bảng sau liệt kê một mẫu nhỏ các dịch vụ đã đăng ký theo khung này:
Loại dịch vụ | Lifetime |
---|---|
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory | Transient |
IHostApplicationLifetime | Singleton |
IWebHostMôi trường | Singleton |
Microsoft.AspNetCore.Hosting.IStartup | Singleton |
Microsoft.AspNetCore.Hosting.IStartupFilter | Transient |
Microsoft.AspNetCore.Hosting.Server.IServer | Singleton |
Microsoft.AspNetCore.Http.IHttpContextFactory | Transient |
Microsoft.Extensions.Logging.ILogger<TCategoryName> | Singleton |
Microsoft.Extensions.Logging.ILoggerFactory | Singleton |
Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Singleton |
Microsoft.Extensions.Options.IConfigureOptions<TOptions> | Transient |
Microsoft.Extensions.Options.IOptions<TOptions> | Singleton |
System.Diagnostics.DiagnosticSource | Singleton |
System.Diagnostics.DiagnosticListener | Singleton |
Tài nguyên bổ sung
- Dependency Injection vào view trong ASP.NET Core
- Dependency Injection vào controller trong ASP.NET Core
- Dependency Injection trong trình xử lý yêu cầu trong ASP.NET Core
- Dependency Injection ASP.NET Core Blazor
- Các mẫu Conference NDC để phát triển ứng dụng DI
- Khởi động ứng dụng trong ASP.NET Core
- Kích hoạt middleware dựa trên factory trong ASP.NET Core
- Bốn cách để xử lý IDisposables trong ASP.NET Core
- Viết code sạch trong ASP.NET Core với Dependency Injection (MSDN)
- Nguyên tắc phụ thuộc rõ ràng
- Đảo ngược vùng chứa điều khiển và mẫu DI (Martin Fowler)
- Cách đăng ký dịch vụ có nhiều interface trong ASP.NET Core DI