Trong ASP.NET Core, Dependency Injection (DI) cho phép bạn đăng ký dịch vụ với các vòng đời khác nhau: Transient, Scoped và Singleton. Mỗi kiểu có cách quản lý instance khác nhau và phù hợp với các trường hợp sử dụng khác nhau. Dưới đây là phân biệt chi tiết

Transient Dependency Injection
Mô tả
- Transient có nghĩa là “tạm thời”. Khi một dịch vụ được đăng ký với kiểu Transient, DI container sẽ tạo ra một instance mới mỗi khi dịch vụ đó được yêu cầu.
- Điều này đảm bảo rằng không có dữ liệu trạng thái nào được lưu giữ giữa các lần sử dụng, vì mỗi lần yêu cầu sẽ nhận về một đối tượng hoàn toàn mới.
Khi nào nên sử dụng
- Dịch vụ Stateless:
Sử dụng khi dịch vụ không cần lưu trữ trạng thái giữa các lần gọi. Ví dụ: các helper, các dịch vụ xử lý logic đơn giản mà không có dữ liệu nội bộ cần chia sẻ. - Tác vụ nhẹ:
Nếu việc khởi tạo đối tượng không tốn nhiều tài nguyên, việc tạo mới liên tục sẽ không gây ảnh hưởng đến hiệu năng tổng thể của ứng dụng.
Ưu điểm và nhược điểm
Ưu điểm:
- Mỗi lần sử dụng đều có instance riêng biệt, đảm bảo tính độc lập và không có sự ảnh hưởng lẫn nhau.
- Giảm thiểu khả năng xảy ra lỗi do trạng thái được chia sẻ giữa các yêu cầu.
Nhược điểm:
- Nếu đối tượng có quá trình khởi tạo phức tạp hoặc tốn tài nguyên, việc tạo mới liên tục có thể làm giảm hiệu năng.
- Không phù hợp cho các dịch vụ cần duy trì trạng thái trong suốt quá trình thực thi của một request hay ứng dụng.
Ví dụ minh hoạ
public interface ITransientService
{
Guid GetOperationID();
}
public class TransientService : ITransientService
{
private Guid _operationId;
public TransientService()
{
_operationId = Guid.NewGuid();
}
public Guid GetOperationID() => _operationId;
}
// Đăng ký trong DI container:
builder.Services.AddTransient<ITransientService, TransientService>();
Trong ví dụ trên, mỗi lần ITransientService
được yêu cầu (ví dụ trong controller hoặc service khác), DI container sẽ tạo ra một instance mới với một Guid
mới.
Scoped Dependency Injection
Mô tả
- Scoped có nghĩa là “theo phạm vi”. DI container sẽ tạo ra một instance duy nhất cho mỗi HTTP request. Trong cùng một request, nếu dịch vụ được yêu cầu nhiều lần, cùng một instance sẽ được sử dụng.
- Điều này giúp chia sẻ trạng thái giữa các thành phần trong một request nhưng không bị chia sẻ giữa các request khác nhau.
Khi nào nên sử dụng
- Lưu trữ trạng thái trong một Request:
Dịch vụ cần giữ thông tin, trạng thái hay dữ liệu tạm thời trong suốt vòng đời của một HTTP request. Một ví dụ điển hình làDbContext
trong Entity Framework Core, nơi mà tất cả các thao tác database trong cùng một request cần được theo dõi và lưu trữ. - Quản lý giao dịch:
Khi bạn muốn gộp nhiều thao tác liên quan đến database thành một đơn vị công việc (Unit of Work) duy nhất cho request đó.
Ưu điểm và nhược điểm
Ưu điểm:
- Giúp chia sẻ dữ liệu và trạng thái giữa các thành phần cùng tham gia xử lý một request.
- Tăng hiệu quả quản lý tài nguyên trong request, vì instance được tạo ra duy nhất và có thể tái sử dụng nhiều lần.
Nhược điểm:
- Instance không được chia sẻ giữa các request, nên nếu cần trạng thái toàn cục thì không phù hợp.
- Cần chú ý khi làm việc với các tác vụ bất đồng bộ, đảm bảo rằng trạng thái của instance không bị sai lệch giữa các phần của request.
Ví dụ minh hoạ
public interface IScopedService
{
Guid GetOperationID();
}
public class ScopedService : IScopedService
{
private Guid _operationId;
public ScopedService()
{
_operationId = Guid.NewGuid();
}
public Guid GetOperationID() => _operationId;
}
// Đăng ký trong DI container:
builder.Services.AddScoped<IScopedService, ScopedService>();
Ví dụ: Nếu bạn inject IScopedService
vào nhiều thành phần trong cùng một HTTP request, chúng đều sẽ nhận được cùng một Guid
(instance duy nhất trong request đó).
Singleton Dependency Injection
Mô tả
- Singleton có nghĩa là “đơn nhất”. Khi dịch vụ được đăng ký với kiểu Singleton, DI container sẽ tạo ra một instance duy nhất trong toàn bộ vòng đời của ứng dụng. Mọi yêu cầu sử dụng dịch vụ này, cho dù ở request nào, đều nhận về cùng một instance.
- Instance này được tạo ra khi ứng dụng khởi động (hoặc khi lần đầu tiên được yêu cầu) và tồn tại cho đến khi ứng dụng kết thúc.
Khi nào nên sử dụng
- Dịch vụ Toàn Cục:
Dịch vụ cần chia sẻ trạng thái hoặc dữ liệu toàn cục giữa tất cả các request. Ví dụ: các dịch vụ cache, cấu hình, logging, hay các dịch vụ không có trạng thái nhưng cần được sử dụng lại nhiều lần. - Tiết kiệm tài nguyên:
Nếu việc khởi tạo đối tượng rất tốn kém về tài nguyên, việc tạo ra một instance duy nhất và sử dụng lại sẽ giúp cải thiện hiệu năng tổng thể của ứng dụng.
Ưu điểm và nhược điểm
Ưu điểm:
- Giảm thiểu việc tạo mới đối tượng, tiết kiệm bộ nhớ và tài nguyên.
- Phù hợp với các dịch vụ cần trạng thái toàn cục hoặc được chia sẻ giữa các thành phần khác nhau trong ứng dụng.
Nhược điểm:
- Do instance được chia sẻ trên toàn bộ ứng dụng, cần đảm bảo rằng code bên trong dịch vụ đó phải thread-safe (an toàn khi truy cập đồng thời từ nhiều thread).
- Không nên inject các dịch vụ có lifetime ngắn hơn (Transient hay Scoped) vào trong Singleton, vì điều này có thể dẫn đến lỗi khi truy cập các instance không còn tồn tại hoặc không phù hợp.
Ví dụ minh hoạ
public interface ISingletonService
{
Guid GetOperationID();
}
public class SingletonService : ISingletonService
{
private Guid _operationId;
public SingletonService()
{
_operationId = Guid.NewGuid();
}
public Guid GetOperationID() => _operationId;
}
// Đăng ký trong DI container:
builder.Services.AddSingleton<ISingletonService, SingletonService>();
Trong ví dụ trên, bất kể có bao nhiêu request, tất cả các thành phần sử dụng ISingletonService
đều nhận được cùng một Guid
, vì chỉ có duy nhất một instance được tạo ra.
So Sánh Tổng Quan
Lifetime | Cách hoạt động | Khi sử dụng | Ưu điểm | Nhược điểm |
---|---|---|---|---|
Transient | Tạo ra một instance mới mỗi khi được yêu cầu | Dịch vụ stateless, helper, logic không lưu trạng thái | Không có dữ liệu lẫn lộn giữa các lần gọi | Tốn tài nguyên nếu khởi tạo đối tượng tốn kém |
Scoped | Tạo ra một instance duy nhất cho mỗi HTTP request | Dịch vụ cần chia sẻ trạng thái trong suốt vòng đời của một request | Dễ chia sẻ dữ liệu trong một request, quản lý tài nguyên tốt | Không chia sẻ giữa các request khác nhau |
Singleton | Tạo ra một instance duy nhất cho toàn bộ vòng đời của ứng dụng | Dịch vụ toàn cục, cache, logging, cấu hình | Tiết kiệm tài nguyên, instance dùng chung cho toàn bộ ứng dụng | Cần đảm bảo thread-safety, cẩn trọng với phụ thuộc lifetime ngắn hơn |
Lưu ý khi sử dụng các Lifetime
- Phụ thuộc giữa các dịch vụ:
Không nên inject một dịch vụ có lifetime ngắn hơn (Transient hoặc Scoped) vào một dịch vụ Singleton. Điều này có thể gây ra lỗi vì instance của dịch vụ ngắn hơn có thể không tồn tại hoặc không được cập nhật kịp thời. - Thread-Safety:
Với Singleton, vì instance được dùng chung cho toàn bộ ứng dụng, nên cần đảm bảo rằng các thuộc tính và phương thức bên trong được thiết kế để xử lý truy cập đồng thời từ nhiều thread. - Hiệu Năng:
Lựa chọn đúng lifetime cho dịch vụ sẽ giúp ứng dụng sử dụng tài nguyên hiệu quả. Ví dụ, các đối tượng tốn kém khi khởi tạo nên có thể được đăng ký dưới dạng Singleton, trong khi các đối tượng nhẹ và stateless có thể sử dụng Transient. - Kiểm Thử:
Việc lựa chọn lifetime phù hợp cũng giúp việc kiểm thử trở nên dễ dàng hơn. Ví dụ, đối với các dịch vụ Transient, việc mock và kiểm thử độc lập sẽ không ảnh hưởng đến trạng thái của các phần khác trong ứng dụng.
Ví dụ trong ứng dụng ASP.NET Core
Dưới đây là một ví dụ minh họa cách đăng ký và sử dụng các dịch vụ với lifetime khác nhau trong một ứng dụng ASP.NET Core:
// Interfaces and implementations
public interface IOperationService
{
Guid OperationId { get; }
}
public class TransientOperation : IOperationService
{
public Guid OperationId { get; } = Guid.NewGuid();
}
public class ScopedOperation : IOperationService
{
public Guid OperationId { get; } = Guid.NewGuid();
}
public class SingletonOperation : IOperationService
{
public Guid OperationId { get; } = Guid.NewGuid();
}
// Startup (Program.cs trong .NET 6+)
var builder = WebApplication.CreateBuilder(args);
// Đăng ký dịch vụ với các lifetime khác nhau
builder.Services.AddTransient<IOperationService, TransientOperation>(); // Transient
builder.Services.AddScoped<IOperationService, ScopedOperation>(); // Scoped
builder.Services.AddSingleton<IOperationService, SingletonOperation>(); // Singleton
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Trong controller, bạn có thể inject các dịch vụ này (nếu dùng interface khác nhau hoặc qua constructor injection riêng biệt) để kiểm tra sự khác biệt giữa chúng. Khi bạn gọi trong một HTTP request, bạn sẽ thấy rằng:
- Transient: Mỗi lần gọi phương thức trong controller nhận được một
OperationId
mới. - Scoped: Trong cùng một request,
OperationId
sẽ giống nhau, nhưng khác ở các request khác. - Singleton:
OperationId
luôn giống nhau cho mọi request trong suốt vòng đời ứng dụng.
Kết luận
Việc lựa chọn giữa Transient, Scoped và Singleton phụ thuộc vào yêu cầu cụ thể của từng dịch vụ trong ứng dụng:
- Transient:
Dành cho các dịch vụ không lưu trữ trạng thái hoặc những helper cần tính độc lập tuyệt đối giữa các lần sử dụng. - Scoped:
Lý tưởng cho các dịch vụ cần lưu trữ trạng thái trong một HTTP request, như quản lý giao dịch hoặc theo dõi thay đổi của dữ liệu trong quá trình xử lý request. - Singleton:
Phù hợp với các dịch vụ cần được chia sẻ toàn cục, nơi việc khởi tạo một lần duy nhất và tái sử dụng giúp tiết kiệm tài nguyên. Tuy nhiên, cần cẩn trọng về thread-safety và không nên inject các dịch vụ có lifetime ngắn hơn.
Hiểu rõ sự khác biệt và lựa chọn lifetime phù hợp giúp bạn xây dựng ứng dụng ASP.NET Core hiệu quả, dễ bảo trì và mở rộng. Hy vọng bài viết chi tiết này sẽ giúp bạn nắm bắt được kiến thức cần thiết về Dependency Injection trong ASP.NET Core.
CÔNG TY TNHH GIẢI PHÁP CÔNG NGHỆ TRANG DESIGNER
Trang Designer chuyên thiết kế website chuẩn SEO, thiết kế logo toàn diện giúp doanh nghiệp xây dựng một thương hiệu mạnh và bán hàng hiệu quả trên các nền tảng số cho nhiều lĩnh vực kinh doanh.
Vui lòng liên hệ: 138 Hiền Vương, Phường Phú Thạnh, Quận Tân Phú, TP. Hồ Chí Minh
Điện thoại: 0903.728.335
Website: www.trangdesigner.id.vn