【问题标题】:Use the ActivatorUtilities to determine which implementation to inject at runtime使用 ActivatorUtilities 确定在运行时注入哪个实现
【发布时间】:2019-12-12 21:38:29
【问题描述】:

我正在使用内置的 .Net Core IoC 容器来解决应用程序的依赖关系。我配置它的方式如下,使用 Scrutor 扫描我的程序集:

services.Scan(s => s
    .FromAssembliesOf(currentAssemblyTypesList)
    .AddClasses(false)
    .UsingRegistrationStrategy(RegistrationStrategy.Append)
    .AsImplementedInterfaces()
    .WithTransientLifetime());

到目前为止,我遇到了一些简单的案例,其中每个接口都由一个依赖项实现,因此之前的配置完美地解决了整个依赖关系树。

现在考虑以下代码:

public interface IService {}  

public class ServiceOne : IService 
{
     public ServiceOne(IDependency dependency) {}
}  

public class ServiceTwo : IService 
{
     public ServiceTwo(IDependency dependency) {}
}  

public class SomeClass  
{  
    public SomeClass(IService service) {}  

    public void DoSomething()
    {
        this.service.SomeMethod();
    }
}

在这种情况下,“SomeClass”位于依赖关系树的中间,并且我有两个实现相同接口的服务,而应该将哪个服务注入“SomeClass”直到运行时才知道。由于一些不重要的原因,我被要求为此使用 ActivatorUtilities 类。

我正在处理确定应该实例化哪个 IService 的两种方案:

  1. 在应用程序启动时,会设置一个标志,用于确定在注册之前要使用哪个服务(对于类似的设置,但不适用于相同的依赖关系树)。
  2. 在“DoSomething”方法的执行过程中,会决定应该使用两个服务中的哪一个,所以我猜应该注册某种工厂并将其注入“SomeClass”。

所以问题是我需要更改或添加到依赖项注册过程以及如何使用 ActivatorUtilities 类来实现这些目标?

谢谢。

【问题讨论】:

    标签: c# .net-core ioc-container


    【解决方案1】:

    目前还不是很清楚你想要达到什么目标,但我认为它是这样的?

    services.AddTransient<IService>(sp =>
    {
        if (condition1 && condition2)
            ActivatorUtilities.CreateInstance<ServiceOne>(sp);
        else
            ActivatorUtilities.CreateInstance<ServiceTwo>(sp);
    });
    

    【讨论】:

    • 我发现有时获得适量的细节是一种杂耍行为。我因“太多”和“不够”而被否决。我想我会重写问题以使其尽可能清楚。
    【解决方案2】:

    因此,最棘手的部分是在注册已经发生时弄清楚如何处理第一个场景,结果证明有一种方法可以替换特定定义。这可以通过以下代码完成:

    Func<IServiceProvider, object> factoryMethod = sp =>
    {
        if (condition1 && condition2)
        {
            return ActivatorUtilities.CreateInstance<ServiceOne>(sp);
        }
        else
        {
            return ActivatorUtilities.CreateInstance<ServiceTwo>(sp);
        }
    };
    
    services.Replace(ServiceDescriptor.Describe(typeof(IService), factoryMethod, ServiceLifetime.Transient));
    

    condition1condition2 在依赖项注册时已知时,这非常有效,换句话说,在应用程序启动时,在发出任何请求之前。

    对于在应用程序运行并发出请求之前不知道条件的另一种情况,因为内置的 .Net Core IoC 容器不像 Castle 或 Autofac 等其他容器那样功能丰富这是手动创建一个工厂方法对象,如下所示:

    public interface IServiceFactory
    {
        IService Get(MyObject myObject);
    }
    
    public class ServiceFactory : IServiceFactory
    {
        private readonly IServiceProvider sp;
    
        public ServiceFactory(IServiceProvider sp)
        {
            this.sp = sp;
        }
    
        public IService Get(MyObject myObject)
        {
            if(myObject.SomeProperty == "whatever")
            {
                return ActivatorUtilities.CreateInstance<ServiceOne>(this.sp);
            }
            else
            {           
                return ActivatorUtilities.CreateInstance<ServiceTwo>(this.sp);
            }
        }
    }
    

    这里唯一要记住的是,可以而且应该在定义所有其余应用程序接口的任何地方定义接口,并且您不要希望应用程序的其余部分由于使用了 IServiceProvider 接口,因此与 MicrosoftExtensions.DependencyInjection 包紧密耦合,因此工厂的实现应该在定义其余注册逻辑的任何地方。

    我非常努力地寻找 ActivatorUtilities.CreateFactory 方法的示例,它可以给我类似的东西,但找不到。我希望这对某人有用。

    【讨论】:

      猜你喜欢
      • 2019-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-14
      • 2017-07-17
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多