[英]How to configure services based on request in ASP.NET Core
在 ASP.NET Core 中,我们可以在启动期间注册所有依赖项,这些依赖项在应用程序启动时执行。 然后注册的依赖项将被注入到控制器构造函数中。
public class ReportController
{
private IReportFactory _reportFactory;
public ReportController(IReportFactory reportFactory)
{
_reportFactory = reportFactory;
}
public IActionResult Get()
{
vart report = _reportFactory.Create();
return Ok(report);
}
}
现在我想根据当前请求中的数据(用户授权级别或随请求传递的查询字符串中的某个值)注入IReportFactory
的不同实现。
问题:在 ASP.NET Core 中是否有任何内置抽象(中间件)可以注册另一个接口实现?
如果没有内置功能,可能的方法是什么?
更新IReportFactory
接口被用作一个简单的例子。 实际上我在不同的地方注入了一堆低级接口。 现在我希望这些低级接口的不同实现将根据请求数据注入。
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;
}
}
因为我们知道我们需要在应用程序的入口点使用哪个实现(我认为控制器操作可以被视为应用程序的入口点),所以我们希望在那里注入正确的实现 - 不需要对已经存在的设计进行更改。
不,你不能。 IServiceCollection
在应用程序启动期间填充并在调用Configure
方法之前构建。 在那之后(正在构建容器),不能再更改注册。
但是,您可以实现抽象工厂,无论是作为工厂方法还是作为接口/类。
// 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>()
});
或者使用抽象工厂类
恐怕你不能通过简单的依赖注入直接达到目标,因为在启动阶段配置的依赖注入,换句话说,所有的服务和实现都在请求到来之前就已经配置好了。
但是,您可以注入 Create Service 委托,以便我们可以在运行时创建所需的服务实现实例。
例如,如果我们有一个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, };
}
}
由于我们想在未来获得所需的实现,我们需要先注册实现。
services.AddScoped<ReportFactory1>();
services.AddScoped<ReportFactory2>();
这就是魔法发生的地方:
IReportFactory
我们只是添加一个Func<HttpContext,IReportFactory>
代替,它是一个CreateReportFactoryDelegate
公共委托 IReportFactory CreateReportFactoryDelegate(Microsoft.AspNetCore.Http.HttpContext 上下文);
我们也需要将 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>();
};
});
现在,我们可以将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);
}
}
可以通过在 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.