我创建了 API:控制器经常与多个“站点”一起工作。
(并非真正处理订单,而是用作示例)
[ApiController]
[Route("{site}/Order")]
public class OrderController
{
private readonly Dictionary<string, IOrderModel> _orderModels;
public class OrderController(Dictionary<string, IOrderModel> orderModels)
{
_orderModels = orderModels;
}
[HttpGet("{orderNumber:int}"]
public async Task<IActionResult> GetOrderAsync(string site, int orderNumber)
{
if (!_orderModels.TryGetValue(site, out var orderModel))
return NotFound();
if (!User.HasAccessToSite(site))
return Forbid();
var order = await orderModel.GetExistingOrderAsync(orderNumber);
return Ok(order)
}
}
它工作正常,但我需要一个 CreateOrderModels 方法,我可以手动计算出所有依赖项
private static Dictionary<string, IOrderModel> CreateOrderModels(IServiceProvider services)
{
var orderModels = new Dictionary<string, IOrderModel>();
var site1Repository = services.GetRequiredService<Site1Repository>();
var logger = services.GetRequiredService<ILogger<CommonOrderModel>>>();
orderModels.Add("Site1", new CommonOrderModel(site1Repository, logger);
...
return orderModels;
}
(通常只是“测试”和“产品”等网站,所以这不是什么大问题,但想使用内置功能)
我认为通过新的基于密钥的 DI,我可以简化创建过程,例如
services.AddKeyedSingleton<IOrderModel, CommonOrderModel>("Site1");
services.AddKeyedSingleton<IOrderModel, SpecialSite2OrderModel>("Site2");
services.AddKeyedSingleton<IOrderModel, CommonOrderModel>("Site3");
services.AddKeyedSingleton<IRepository, Site1Repository>("Site1");
services.AddKeyedSingleton<IRepository, Site2Repository>("Site2");
services.AddKeyedSingleton<IRepository, Site3Repository>("Site3");
这里的问题是 CommonOrderModel 具有特定于站点的依赖关系。 我想做的是这样的:
public CommonOrderModel([FromKeyedServices(KeyedService.ParentKey)] IRepository repository, ILogger<CommonOrderModel> logger)
即当创建“Site1”的 CommonOrderModel 时,我希望它使用 IRepository 作为相同的密钥。
我知道这是不可能开箱即用的。试图查看源代码,看看是否可以将功能注入某处,但失败了。
主要问题似乎是依赖关系图在任何工厂方法中都不可用,因此它无法从更高层获取密钥。
这可能吗?
您当然可以通过 AddKeyedTransient(IServiceCollection, Type, Object, Func
这允许使用传递的密钥和服务提供者解决与委托的依赖关系。
这里是示例:
// Sample services
public interface IKeyedService { }
public class KeyedService1 : IKeyedService
{
private readonly IKeyedDependency _dependency;
public KeyedService1(IKeyedDependency dependency)
{
_dependency = dependency;
}
}
public class KeyedService2 : IKeyedService
{
private readonly IKeyedDependency _dependency;
public KeyedService2(IKeyedDependency dependency)
{
_dependency = dependency;
}
}
public interface IKeyedDependency { }
public class KeyedDependency1 : IKeyedDependency { }
public class KeyedDependency2 : IKeyedDependency { }
及应用:
var builder = WebApplication.CreateBuilder(args);
const string key1 = "key1";
const string key2 = "key2";
builder.Services.AddKeyedTransient<IKeyedDependency, KeyedDependency1>(key1);
builder.Services.AddKeyedTransient<IKeyedDependency, KeyedDependency2>(key2);
builder.Services.AddKeyedTransient<IKeyedService, KeyedService1>(key1, (sp, key) =>
new (sp.GetRequiredKeyedService<IKeyedDependency>(key)));
builder.Services.AddKeyedTransient<IKeyedService, KeyedService2>(key2, (sp, key) =>
new(sp.GetRequiredKeyedService<IKeyedDependency>(key)));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.MapGet("/", ([FromKeyedServices(key1)] IKeyedService k1, [FromKeyedServices(key2)] IKeyedService k2) =>
{
})
.WithOpenApi();
app.Run();
下面你可以看到结果: