继承基于键的依赖注入中的键

问题描述 投票:0回答:1

我创建了 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 作为相同的密钥。

我知道这是不可能开箱即用的。试图查看源代码,看看是否可以将功能注入某处,但失败了。

主要问题似乎是依赖关系图在任何工厂方法中都不可用,因此它无法从更高层获取密钥。

这可能吗?

c# asp.net-core dependency-injection
1个回答
0
投票

您当然可以通过 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();

下面你可以看到结果:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.