Khởi tạo service trong Startup ASP.NET Core

Khởi tạo service trong Startup ASP.NET Core

Đôi khi chúng ta cần khởi tạo các service trong giai đoạn khởi động ứng dụng của mình. Chúng ta sẽ xem nhanh nhiều cách tiếp cận khả thi dành cho nhà phát triển xây dựng ứng dụng ASP.NET Core.

Tại sao phải khởi tạo dịch vụ trong Startup ASP.NET Core?

Không có gì bí mật khi cơ sở hạ tầng ASP.NET mới nhất phụ thuộc nhiều vào dependency injection.

Khi làm việc với dependency injection, chúng ta sẽ cấu hình các service của mình một lần khi bắt đầu vòng đời của ứng dụng và sau đó gọi ASP.NET Core để khởi tạo các phụ thuộc của chúng ta.

Theo tôi, việc thiết lập một lần và sử dụng ở mọi nơi là một lợi thế lớn khi sử dụng dependency injection.

Khởi tạo service trong phương thức ConfigureServices

Vị trí đầu tiên chúng ta có thể khởi tạo một service nằm trong phương thức ConfigureServices của chúng ta. Lý do để làm điều này có thể là để khởi tạo và đăng ký một thể hiện singleton với bộ sưu tập dịch vụ (services collection) của ứng dụng.

Tôi không khuyên bạn nên khởi tạo bất kỳ service nào trongphương thứcConfigureServices. Như bạn sẽ thấy, có một số lưu ý và chi phí khi làm như vậy.

Trong phương thức ConfigureServices, chúng ta có thể tạo một thể hiện của trình cung cấp dịch vụ (service provider).

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Dependency>();

    var provider = services.BuildServiceProvider();

    var dependency = provider.GetRequiredService<Dependency>();
    Console.WriteLine(dependency.Hello);
}

Đoạn mã sau sẽ không thành công.

public void ConfigureServices(IServiceCollection services)
{
    var provider = services.BuildServiceProvider();

    // we haven't registered Dependency yet 
    var dependency = provider.GetRequiredService<Dependency>();
    
    services.AddScoped<Dependency>();
    
    Console.WriteLine(dependency.Hello);
}

Mã trên không thành công vì chúng ta chưa đăng ký sự phụ thuộc cho lớp Dependency của mình, nhưng chúng ta lại đang cố gắng khởi tạo nó.

Khởi tạo service trong phương thức Configure

Nếu mọi người muốn chạy một số mã khi khởi động, tốt nhất nên làm điều đó trong phương thức Configure, vì tại thời điểm này, chúng ta đã đăng ký tất cả các service của mình.

Chúng ta có thể thực hiện các tác vụ như gọi một service từ xa, thực thi chuyển đổi cơ sở dữ liệu hoặc ghi nhật ký.

Lưu ý rằng bất kỳ thao tác block thread nào cũng có thể làm chậm thời gian khởi động ứng dụng của bạn và bất kỳ lỗi nào cũng sẽ khiến ứng dụng của bạn không thể khởi động.

Phương pháp khởi tạo service tốt nhất

Một cách dễ dàng, cách tốt nhất là thêm phần phụ thuộc của chúng ta vào danh sách các tham số của phương thức Configure. ASP.NET Core sẽ khởi tạo service của chúng ta từ bộ sưu tập dịch vụ (service collection).

public void Configure(
    IApplicationBuilder app, 
    IWebHostEnvironment env,
    // our dependency
    Dependency dependency
)
{
    Console.WriteLine(dependency.Hello);
    
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
    });
}

Ưu điểm của cách tiếp cận này là chúng ta thậm chí có thể giải quyết các phụ thuộc theo phạm vi (scope), như trong trường hợp này, phạm vi là phương thức Configure.

Phương pháp khác nhưng nên hạn chế

Một cách tiếp cận khác, mà tôi không khuyến khích, là sử dụng thuộc tính ApplicationServices của interface IApplicationBuilder. Interface IApplicationBuilder là tham sốmặc định của phương thức Configure trong Startup.

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(
    IApplicationBuilder app, 
    IWebHostEnvironment env
)
{
    // cannot call Scoped dependencies
    var dependency = app
        .ApplicationServices
        .GetRequiredService<Dependency>();
    
    Console.WriteLine(dependency.Hello);
    
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
    });
}

Cách tiếp cận này chỉ hoạt động cho các service được đăng ký là SingletonTransient. Cố gắng khởi tạo các service Scoped sẽ dẫn đến ngoại lệ sau. ASP.NET Core sẽ ném ngoại lệ này vào một nỗ lực để tránh các phụ thuộc bị nắm bắt.

 System.InvalidOperationException: Cannot resolve scoped service 'WebApplication7.Dependency' from root provider.
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
         at Microsoft.Extensions.DependencyInjection.ServiceProvider.Microsoft.Extensions.DependencyInjection.ServiceLookup.IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
...

Điều này nằm ngoài phạm vi của bài viết này, tuy nhiên chúng ta sẽ thảo luận nhanh về các phụ thuộc bị nắm bắt. Phụ thuộc bị nắm bắt là khi một service Singleton giữ một service Scoped. Phụ thuộc bị nắm bắt đặc biệt nguy hiểm vì thể hiện đầu tiên của một service Scoped có thể có thông tin về người dùng đầu tiên yêu cầu ứng dụng của chúng ta.

Bạn bắt buộc không được sử dụng BẤT KỲ dịch vụ nào được khởi tạo trong phương thứcConfigure như một phần của mã đăng ký pipeline của bạn.

Phần kết luận

Chúng ta đã có một số cách để khởi tạo các service trong lớp Startup của chúng ta. Có nhiều cách để giải quyết vấn đề này. Một số thẳng thắn hơn những người khác.

Chúng ta phải xem xét chi phí để khởi tạo các service trong lớp Startupcủa mình và tác động của nó đối với hiệu suất khởi động. Giống như nhiều thứ, tất cả đều phụ thuộc.

Tôi hy vọng bạn tìm thấy bài viết này thú vị và vui lòng cho tôi biết nếu bạn có các cách khởi tạo service khác trong lớp Startup.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *