简体   繁体   English

如何在 ASP.NET Core 中根据请求配置服务

[英]How to configure services based on request in ASP.NET Core

In ASP.NET Core we can register all dependencies during start up, which executed when application starts.在 ASP.NET Core 中,我们可以在启动期间注册所有依赖项,这些依赖项在应用程序启动时执行。 Then registered dependencies will be injected in controller constructor.然后注册的依赖项将被注入到控制器构造函数中。

public class ReportController
{
    private IReportFactory _reportFactory;

    public ReportController(IReportFactory reportFactory)
    {
        _reportFactory = reportFactory;
    }

    public IActionResult Get()
    {
        vart report = _reportFactory.Create();
        return Ok(report);
    }
}

Now I want to inject different implementations of IReportFactory based on data in current request (User authorization level or some value in the querystring passed with an request).现在我想根据当前请求中的数据(用户授权级别或随请求传递的查询字符串中的某个值)注入IReportFactory的不同实现。

Question: is there any built-in abstraction(middleware) in ASP.NET Core where we can register another implementation of interface?问题:在 ASP.NET Core 中是否有任何内置抽象(中间件)可以注册另一个接口实现?

What is the possible approach for this if there no built-in features?如果没有内置功能,可能的方法是什么?

Update IReportFactory interface was used as a simple example.更新IReportFactory接口被用作一个简单的例子。 Actually I have bunch of low level interfaces injected in different places.实际上我在不同的地方注入了一堆低级接口。 And now I want that different implementation of those low level interfaces will be injected based on request data.现在我希望这些低级接口的不同实现将根据请求数据注入。

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService;
    }

    public IActionResult Create()
    {
        var order = _orderService.Create();
        return Ok(order);
    }
}    

 public class OrderService
 {
    private OrderBuilder _orderBuilder;
    private IShippingService _shippingService; // This now have many different implementations

    public OrderService(
        OrderBuilder _orderBuilder,
        IShippingService _shippingService)
    {
        _orderService = orderService;
        _shippingService = shippingService;
    }

    public Order Create()
    {
        var order = _orderBuilder.Build();
        var order.ShippingInfo = _shippingService.Ship();
        return order;
    }
 }  

Because we know which implementation we need to use on entry point of our application (I think controller action can be considered as entry point of application), we want inject correct implementation already there - no changes required in already existed design.因为我们知道我们需要在应用程序的入口点使用哪个实现(我认为控制器操作可以被视为应用程序的入口点),所以我们希望在那里注入正确的实现 - 不需要对已经存在的设计进行更改。

No, you can't.不,你不能。 The IServiceCollection is populated during application startup and built before Configure method is called. IServiceCollection在应用程序启动期间填充并在调用Configure方法之前构建。 After that (container being built), the registrations can't be changed anymore.在那之后(正在构建容器),不能再更改注册。

You can however implement an abstract factory, be it as factory method or as an interface/class.但是,您可以实现抽象工厂,无论是作为工厂方法还是作为接口/类。

// Its required to register the IHttpContextAccessor first
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IReportService>(provider => {
    var httpContext = provider.GetRequired<IHttpContextAccessor>().HttpContext;

    if(httpContext.User.IsAuthorized) 
    {
        return new AuthorizedUserReportService(...);
        // or resolve it provider.GetService<AuthorizedUserReportService>()
    }

    return new AnonymousUserReportService(...);
    // or resolve it provider.GetService<AnonymousUserReportService>()
});

Alternatively use an abstract factory class或者使用抽象工厂类

I'm afraid you can not directly acheive the goal via simple dependency injection , as the the dependency injection configured at Startup stage , in other words , all services and implementions has been configured before a request comming .恐怕你不能通过简单的依赖注入直接达到目标,因为在启动阶段配置的依赖注入,换句话说,所有的服务和实现都在请求到来之前就已经配置好了。

However , you can inject a Create Service delegate so that can we create the required service implemention instance in runtime .但是,您可以注入 Create Service 委托,以便我们可以在运行时创建所需的服务实现实例。

For instance , if we have a IReportFactory Interface and two implementions as blew :例如,如果我们有一个IReportFactory接口和两个实现:

public interface IReportFactory
{
    object Create();
}

public class ReportFactory1 : IReportFactory
{
    public object Create()
    {
        return new { F = 1, };
    }
}
public class ReportFactory2 : IReportFactory {
    public object Create()
    {
        return new { F = 2, }; 
    }
}

As we want to get the required implemention in future , we need to register the Implementions first .由于我们想在未来获得所需的实现,我们需要先注册实现。

services.AddScoped<ReportFactory1>();
services.AddScoped<ReportFactory2>();

and here's where the magic happens :这就是魔法发生的地方:

  1. We don't register a IReportFactory我们不注册IReportFactory
  2. We just add a Func<HttpContext,IReportFactory> instead , which is a CreateReportFactoryDelegate我们只是添加一个Func<HttpContext,IReportFactory>代替,它是一个CreateReportFactoryDelegate

    public delegate IReportFactory CreateReportFactoryDelegate(Microsoft.AspNetCore.Http.HttpContext context);公共委托 IReportFactory CreateReportFactoryDe​​legate(Microsoft.AspNetCore.Http.HttpContext 上下文);

We need add the CreateReportFactoryDelegate to servies too.我们也需要将 CreateReportFactoryDe​​legate 添加到服务中。

services.AddScoped<CreateReportFactoryDelegate>(sp => {
    // return the required implemention service by the context;
    return context => {
        // now we have the http context ,
        // we can decide which factory implemention should be returned;
        // ...
        if (context.Request.Path.ToString().Contains("factory1")) {
            return sp.GetRequiredService<ReportFactory1>();
        }
        return sp.GetRequiredService<ReportFactory2>();
    };
});

Now , we can inject a CreateReportFactoryDelegate into controller :现在,我们可以将CreateReportFactoryDelegate注入控制器:

public class HomeController : Controller
{
    private CreateReportFactoryDelegate _createReportFactoryDelegate;

    public HomeController(CreateReportFactoryDelegate createDelegate) {
        this._createReportFactoryDelegate = createDelegate;
        // ...
    }

    public async Task<IActionResult> CacheGetOrCreateAsync() {

        IReportFactory reportFactory = this._createReportFactoryDelegate(this.HttpContext);
        var x=reportFactory.Create();

        // ...
        return View("Cache", cacheEntry);
    }
}

It is possible by using the HttpContextAccessor in Startup.cs可以通过在 Startup.cs 中使用 HttpContextAccessor

services.AddHttpContextAccessor();
services.AddScoped<IYourService>(provider =>
            {
                var contextAccessor = provider.GetService<IHttpContextAccessor>();
                var httpContext = contextAccessor.HttpContext;
                
                var contextVariable = httpContext. ...
                
                // Return implementation of IYourService that corresponds to your contextVariable

              
            });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 ASP.NET Core 6 MVC 中配置服务? - How to Configure services in ASP.NET Core 6 MVC? 如何在 ASP.NET 内核中自动配置服务? - How to automatically Configure services in ASP.NET core? ASP.NET Core 2.0使用Services.Configure配置身份验证 - ASP.NET Core 2.0 Configuring Authentication with Services.Configure 具有托管服务的基于ASP.NET Core事件的后台任务 - ASP.NET Core event based background tasks with hosted services 如何配置nlog.config使其包含asp.net core 2 Web API请求自定义标头信息? - How to configure nlog.config to include asp.net core 2 web api request custom header information? ASP.NET Core 6 MVC:如何配置基于人员部门/角色的身份验证和授权 - ASP.NET Core 6 MVC : how to configure authentication and authorisation based on person department/role 如何在ASP.NET Core中缓存请求? - How to cache a request in ASP.NET Core? Asp.Net Core 中基于请求的 Razor 页面本地化 - Request-based Localization of Razor pages in Asp.Net Core ASP.NET Core中基于请求头授权 - Authorizing based on request headers in ASP.NET Core 如何使用我的服务 ASP.NET Core 6 解决此错误 - How to solve this error with my services ASP.NET Core 6
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM