gpt4 book ai didi

c# - DI,从工厂解决服务实现

转载 作者:行者123 更新时间:2023-12-02 12:14:58 24 4
gpt4 key购买 nike

我有一个接口(interface)及其多个实现:

interface IFoo { bool CanFoo(); }
class Foo1 : IFoo { bool CanFoo() => true; }
class Foo2 : IFoo { bool CanFoo() => false; }

我想将它们全部注册到 IServiceCollection并提供一个像这样的自定义实现工厂:

services
.AddTransient<IFoo, Foo1>()
.AddTransient<IFoo, Foo2>();

// register the factory last
services.AddTransient<IFoo>(provider =>
{
var registrations = provider.GetServices<IFoo>(); // Exception

return registrations.FirstOrDefault(r => r.CanFoo());
};

正如您所看到的,这个想法是解决第一个可用的实现。然而,此代码的结果是 StackOverflowException因为provider.GetServices<IFoo>()即使工厂没有实现接口(interface),也会尝试解析工厂,从而导致无限循环。

有几个问题:

  1. 在这种情况下,服务提供商是否会尝试解决工厂问题?
  2. 如果可能的话,我该如何克服这个问题?

更新

工厂首先参与的原因是因为必须在运行时根据输入数据选择正确的服务,而输入数据可能会随着每个新请求而改变。因此,我无法将范围缩小到应用程序启动期间的特定服务注册。

最佳答案

如注释中所述,您的代码失败是因为对于依赖项注入(inject)容器,您的工厂IFoo 实例的有效源。因此,当要求解析 IFoo 的所有服务时,工厂也会被要求解析。

如果您希望它起作用,那么您必须以某种方式分离类型。例如,您可以创建一个标记接口(interface) IActualFoo,用于注册您的服务:

services.AddTransient<IActualFoo, Foo1>()
services.AddTransient<IActualFoo, Foo2>();

IFoo 工厂内部,您可以解析 IActualFoo 并将其强制转换为 IFoo。当然,这意味着 IAtualFoo 将从 IFoo 继承,并且您的 Foo 服务实际上必须实现 IAtualFoo


另一种方法是将选择正确的 IFoo 实现的责任转移到工厂中。正如其他人所指出的,您的方法有一个缺点,即它将为 IFoo 的每个实现创建实例,然后才选择正确的实例。这太浪费了。

如果工厂能够决定选择正确的 IFoo 实现,那就更好了。通常,这看起来像这样:

services.AddTransient<Foo1>();
services.AddTransient<Foo2>();
services.AddTransient<IFoo>(sp =>
{
if (someMagicCondition)
return sp.GetService<Foo1>();
else
return sp.GetService<Foo2>();
});

因此工厂将根据某些条件逻辑创建正确的实例。虽然这种方法经常被使用,但它需要工厂内部承担全部责任,并在设计时进行一组有限的可能的 IFoo 实现。因此,如果您想稍后动态添加实现,这将不起作用。

您还可以注册一组 IFoo 工厂。因此,不要让 IFoo 实现来决定它是否是正确的实现,而是将该逻辑移动到每个 IFoo 实现的工厂:

services.AddSingleton<IFooFactory, Foo1Factory>();
services.AddSingleton<IFooFactory, Foo2Factory>();
services.AddSingleton<IFoo>(sp =>
{
var factories = sp.GetServices<IFooFactory>();
return factories.FirstOrDefault(f => f.CanFoo())?.CreateFoo();
});
public interface IFooFactory
{
bool CanFoo();
IFoo CreateFoo();
}

public Foo1Factory : IFooFactory
{
public bool CanFoo() => true;
public IFoo CreateFoo() => new Foo1();
}
public Foo2Factory : IFooFactory
{
public bool CanFoo() => false;
public IFoo CreateFoo() => new Foo2();
}

当然,您也可以传递服务提供者并解析它们(如果它们已注册),而不是在工厂内更新 Foo 实现。

关于c# - DI,从工厂解决服务实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54232153/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com