我在 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 容器上的实现?如果我无法告诉工厂类应该在每种情况下检索哪个实现,我不知道工厂类将如何工作。
您实际上并未将任何内容注册为 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";
}
还有一些其他技术可能效率更高或更低一些,或者可以更好地处理你的范围界定需求,但我想说这已经是一个很大的改进了。