[英]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 :这就是魔法发生的地方:
IReportFactory
IReportFactory
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 CreateReportFactoryDelegate(Microsoft.AspNetCore.Http.HttpContext 上下文);
We need add the CreateReportFactoryDelegate to servies too.我们也需要将 CreateReportFactoryDelegate 添加到服务中。
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.