Cách giải quyết Instance Inside ConfigureServices trong ASP.NET Core


100

Có thể giải quyết một phiên bản của IOptions<AppSettings>từ ConfigureServicesphương thức trong Khởi động không? Thông thường, bạn có thể sử dụng IServiceProviderđể khởi tạo các phiên bản nhưng bạn không có nó ở giai đoạn này khi đăng ký dịch vụ.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<AppSettings>(
        configuration.GetConfigurationSection(nameof(AppSettings)));

    // How can I resolve IOptions<AppSettings> here?
}

Câu trả lời:


151

Bạn có thể xây dựng một nhà cung cấp dịch vụ bằng cách sử dụng BuildServiceProvider()phương pháp trên IServiceCollection:

public void ConfigureService(IServiceCollection services)
{
    // Configure the services
    services.AddTransient<IFooService, FooServiceImpl>();
    services.Configure<AppSettings>(configuration.GetSection(nameof(AppSettings)));

    // Build an intermediate service provider
    var sp = services.BuildServiceProvider();

    // Resolve the services from the service provider
    var fooService = sp.GetService<IFooService>();
    var options = sp.GetService<IOptions<AppSettings>>();
}

Bạn cần Microsoft.Extensions.DependencyInjectiongói cho việc này.


Trong trường hợp bạn chỉ cần ràng buộc một số tùy chọn ConfigureServices, bạn cũng có thể sử dụng Bindphương pháp:

var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);

Chức năng này có sẵn thông qua Microsoft.Extensions.Configuration.Bindergói.


Điều gì sẽ xảy ra nếu bạn cần giải quyết dịch vụ này trong một phần khác của ứng dụng? Tôi chắc chắn rằng nó không được thực hiện tất cả trong ConfigureServices () phải không?
Ray

1
@Ray sau đó bạn có thể sử dụng các cơ chế tiêm phụ thuộc mặc định như tiêm hàm tạo. Câu hỏi này đặc biệt về việc giải quyết các dịch vụ bên trong ConfigureServicesphương thức.
Henk Mollema,

@pcdev Bạn nhận được NULL khi bạn làm như vậy và sau đó cố gắng giải quyết trường hợp. Bạn phải thêm dịch vụ trước.
IngoB

@IngoB vâng, xin lỗi, nhận xét đó không chính xác và nên bị xóa - Tôi không hiểu đúng chuyện gì đang xảy ra khi tôi viết nhận xét đó. Vui lòng xem câu trả lời mà tôi đã liên kết với trước đây mà tôi đã cập nhật - Tôi đã thực hiện thêm một số cuộc điều tra và giờ hiểu rõ hơn.
pcdev

14
Mặc dù điều này có thể hữu ích trong trường hợp phương pháp thêm dịch vụ không có quá tải nhà máy triển khai (ví dụ: tại đây ), việc sử dụng BuildServiceProvidergây ra cảnh báo nếu được sử dụng trong mã ứng dụng, chẳng hạn ConfigureServicesnhư nó dẫn đến một bản sao bổ sung của các dịch vụ singleton. tạo. Câu trả lời của Ehsan Mirsaeedi ở đây là giải pháp lý tưởng nhất cho những trường hợp như thế này.
Neo

65

Cách tốt nhất để khởi tạo các lớp phụ thuộc vào các dịch vụ khác là sử dụng quá tải Thêm XXX cung cấp cho bạn IServiceProvider . Bằng cách này, bạn không cần phải khởi tạo nhà cung cấp dịch vụ trung gian.

Các mẫu sau đây cho thấy cách bạn có thể sử dụng quá tải này trong các phương thức AddSingleton / AddTransient .

services.AddSingleton(serviceProvider =>
{
    var options = serviceProvider.GetService<IOptions<AppSettings>>();
    var foo = new Foo(options);
    return foo ;
});


services.AddTransient(serviceProvider =>
{
    var options = serviceProvider.GetService<IOptions<AppSettings>>();
    var bar = new Bar(options);
    return bar;
});

16
Sử dụng giải pháp này thay vì câu trả lời được chấp nhận cho .Net Core 3 trở lên!
Joshit

7
@Joshit Tôi không chắc rằng đây là sự thay thế khả thi cho câu trả lời được chấp nhận trong mọi trường hợp. IServiceProvider có sẵn cho AddSingleton, AddScoped, AddTransient. Nhưng có nhiều phương thức Add khác không cung cấp quá tải này, tức là AddCors, AddAuthentication, AddAuthorization.
Jpsy

@Jpsy Bạn trộn những thứ không liên quan. AddCors, AddAuthentication, v.v. là những người trợ giúp gọi bên dưới các emthods đăng ký để kết nối các phần mềm trung gian cơ bản khác nhau. AddTransient, AddSingleton, AddScoped là ba đăng ký (với ba vòng đời thường được sử dụng)
Fab

Điều này không bao gồm tất cả các trường hợp. Vui lòng tham khảo câu trả lời của tôi cho một giải pháp.
Ian Kemp

6

Cách đơn giản và đúng đắn nhất để đạt được điều này, trong tất cả các phiên bản của ASP.NET Core , là triển khai IConfigureOptions<TOptions>giao diện. Mặc dù điều này đã có từ thời .NET Core 1.0, nhưng có vẻ như ít người biết về cách nó tạo ra mọi thứ Just Work ™.

Ví dụ: bạn muốn thêm trình xác thực mô hình tùy chỉnh có phụ thuộc vào một trong các dịch vụ khác của ứng dụng của bạn. Ban đầu có vẻ như không thể - không có cách nào để giải quyết IMyServiceDependencyvì bạn không có quyền truy cập vào IServiceProvider:

public class MyModelValidatorProvider : IModelValidatorProvider
{
    public MyModelValidatorProvider(IMyServiceDependency dependency)
    {
        ...
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????));
    });
}

Nhưng "phép thuật" của IConfigureOptions<TOptions>nó làm cho nó trở nên quá dễ dàng:

public class MyMvcOptions : IConfigureOptions<MvcOptions>
{
    private IMyServiceDependency _dependency;

    public MyMvcOptions(IMyServiceDependency dependency)
        => _dependency = dependency;

    public void Configure(MvcOptions options)
    {
        options.ModelValidatorProviders.Add(new MyModelValidatorProvider(_dependency));
    }
}

public void ConfigureServices(IServiceCollection services)
{
    // or scoped, or transient
    services.AddSingleton<IConfigureOptions<MvcOptions>, MyMvcOptions>();
    services.AddControllers();
}

Về cơ bản, bất kỳ thiết lập nào bạn đã thực hiện trong các Add***(***Options)đại diện ConfigureServiceshiện được chuyển sang phương thức IConfigureOptions<TOptions>của lớp bạn Configure. Sau đó, bạn đăng ký các tùy chọn giống như cách bạn đăng ký bất kỳ dịch vụ nào khác, và bạn sẽ đi!

Để biết thêm chi tiết, cũng như thông tin về cách hoạt động đằng sau hậu trường này, tôi giới thiệu bạn với Andrew Locke luôn xuất sắc .


1

Bạn đang tìm kiếm một cái gì đó như sau? Bạn có thể xem nhận xét của tôi trong đoạn mã:

// this call would new-up `AppSettings` type
services.Configure<AppSettings>(appSettings =>
{
    // bind the newed-up type with the data from the configuration section
    ConfigurationBinder.Bind(appSettings, Configuration.GetConfigurationSection(nameof(AppSettings)));

    // modify these settings if you want to
});

// your updated app settings should be available through DI now

-1

Muốn giúp những người khác trông giống nhau nhưng khi sử dụng Autofac cũng vậy.

Nếu bạn muốn nhận ILifetimeScope (tức là vùng chứa phạm vi hiện tại), bạn cần gọi app.ApplicationServices.GetAutofacRoot()phương thức trong Configure(IApplicationBuilder app)này sẽ trả về cá thể ILifetimeScope mà bạn có thể sử dụng để giải quyết các dịch vụ

public void Configure(IApplicationBuilder app)
    {
        //app middleware registrations 
        //...
        //

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }

1
Câu trả lời này quá sai đối với AutoFac, không nằm trong phạm vi của câu hỏi này.
Pure.Krome

Tôi đến đây bằng cách truy cập câu hỏi này với tiền tố autofac và không may là không tìm thấy bất kỳ chủ đề cụ thể nào. Vì vậy, tôi hy vọng rằng những người khác cũng sẽ đến câu hỏi này đang vật lộn với vấn đề này có thể tìm thấy câu trả lời.
đơn giản là tốt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.