如何使用依赖注入解析工厂类中的服务? | C#

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

我在 DI 容器上注册了同一接口的六个实现,如下所示:

services.AddScoped<IProductService, ProductService1>();
services.AddScoped<IProductService, ProductService2>();
// ...
services.AddScoped<IProductService, ProductService6>();

使用这些服务的控制器有一个参数

productName
,我用它来选择我想要的服务。为了分离关注点,我实现了一个工厂类,它接受该字符串并从服务提供者检索所请求的服务。像这样的东西:

public string ProductController(string productName)
{
   var myService = _productFactory.GetProductService(productName);
   // ... do other stuff
}

这就是我对工厂课程的想法

public IProductService GetProductService(string productName)
{
    switch (productName.ToLower())
    {
        case "product1":
            return _serviceProvider.GetRequiredService<ProductService1>();
        case "product2":
            return _serviceProvider.GetRequiredService<ProductService2>();
        // ...
    }
}

但是,这会返回错误,因为它找不到

ProductService1
类型的服务,等等。根据我对这些方法如何工作的理解,它只找到类型
IProductService

如何根据实现来搜索所需的服务?我可以按名称搜索服务吗?或者有没有办法将字符串链接到 DI 容器上的实现?如果我无法告诉工厂类应该在每种情况下检索哪个实现,我不知道工厂类将如何工作。

c# .net-core dependency-injection factory-pattern service-provider
1个回答
0
投票

您实际上并未将任何内容注册为 ProductService1、2 或 3 等。您将它们全部注册为 IProductService。您可以将它们注册为您尝试解析它们的类型,如下所示:

services.AddScoped<ProductService1>();

这是一个完整的工作示例:

using Microsoft.Extensions.DependencyInjection;

var factory = new ProductServiceFactory();
var service = factory.GetProductService("product1");
Console.WriteLine(service.ToString());

class ProductServiceFactory
{
    readonly IServiceProvider _serviceProvider;

    public ProductServiceFactory()
    {
        var services = new ServiceCollection();
        services.AddScoped<ProductService1>();
        services.AddScoped<ProductService2>();
        services.AddScoped<ProductService3>();
        _serviceProvider = services.BuildServiceProvider();
    }

    public IProductService GetProductService(string productName)
    {
        switch (productName.ToLower())
        {
            case "product1":
                return _serviceProvider.GetRequiredService<ProductService1>();
            case "product2":
                return _serviceProvider.GetRequiredService<ProductService2>();
            case "product3":
                return _serviceProvider.GetRequiredService<ProductService3>();
            default:
                throw new ArgumentException(productName);
        }
    }
}




interface IProductService { }

class ProductService1 : IProductService { }

class ProductService2 : IProductService { }

class ProductService3 : IProductService { }

请注意,这实际上可能会更干净一些。如果您使用 Scrutor 进行注册,则无需注册每个单独的类型,因此下次添加产品服务时它将自动注册。

var services = new ServiceCollection();
services.Scan(s => s.FromAssemblyOf<IProductService>()
                    .AddClasses(c => c.AssignableTo<IProductService>())
                    .AsSelf()
                    );
// no longer needed to do individual registrations
// Scrutor will pick up these and any new IProductService implementations!
//services.AddScoped<ProductService1>();
//services.AddScoped<ProductService2>();
//services.AddScoped<ProductService3>();
_serviceProvider = services.BuildServiceProvider();

有一些方法可以清理工厂并进行制造,这样您就不必不断添加案例陈述。例如,假设您的 IProductServices 必须声明它们支持哪种产品,我们可以这样获取它们:

using Microsoft.Extensions.DependencyInjection;

var factory = new ProductServiceFactory();
var service = factory.GetProductService("product1");
Console.WriteLine(service.ToString());

class ProductServiceFactory
{
    IEnumerable<IProductService> _productServices;

    public ProductServiceFactory()
    {
        var services = new ServiceCollection();
        services.Scan(s => s.FromAssemblyOf<IProductService>()
                            .AddClasses(c => c.AssignableTo<IProductService>())
                            .AsImplementedInterfaces()
                            );
        var serviceProvider = services.BuildServiceProvider();
        _productServices = serviceProvider.GetServices<IProductService>();
    }

    public IProductService GetProductService(string productName)
    {
        var productService = _productServices.SingleOrDefault(p => productName.Equals(p.ProductName, StringComparison.InvariantCultureIgnoreCase));
        
        if (productService is null)
        {
            throw new ArgumentException(productName);
        }

        return productService;
    }
}


interface IProductService
{
    string ProductName { get; }
}

class ProductService1 : IProductService
{
    public string ProductName => "product1";
}

class ProductService2 : IProductService
{
    public string ProductName => "product2";
}

class ProductService3 : IProductService
{
    public string ProductName => "product3";
}

还有一些其他技术可能效率更高或更低一些,或者可以更好地处理你的范围界定需求,但我想说这已经是一个很大的改进了。

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