Autofac生命周期范围使用

时间:2017-04-17 08:12:10

标签: c# .net dependency-injection autofac

我有一个.net微服务,它有一个服务A,它有一个InstancePerLifetimeScope。服务A被注入到MessageService(单例服务)中。

public class MessageService
{
    private readonly ServiceA serviceA;

    public MessageService(ServiceA serviceA)
    {
        this.serviceA = serviceA;
    }

    public void ProcessItem(ItemClass item)
    {
        serviceA.Proccess(item);
    }
}

通过这种方式,ServiceA将成为任何调用它的线程的单例。 要实现InstancePerLifetimeScope,我会:

public class MessageService
{
    private readonly IContainer container;

    public MessageService(IContainer container)
    {
        this.container = container;
    }

    public void ProcessItem(ItemClass item)
    {
        using (var scope = container.BeginLifetimeScope())
        {
            scope.Resolve<ServiceA>().Proccess(item);
        }
    }
}

这是好习惯吗?我读到这是服务定位器模式,被认为是反模式。将容器直接传递到服务中作为注射剂也不是最好的事情。

public class ServiceAFactory
    {
        private readonly IContainer container;

        public ServiceAFactory(IContainer container)
        {
            this.container = container;
        }

        public IServiceA swapsService CreateService()
        {
            using (var scope = container.BeginLifetimeScope())
            {
                return scope.Resolve<IServiceA>();
            }
        }
    }

2 个答案:

答案 0 :(得分:3)

防止在您的应用程序代码中使用容器,并将其专门用作Composition Root的一部分。有很多方法可以做到这一点,因此没有一个答案,但通常你想从业务代码中提取处理Container的代码。

在您的简单示例中,似乎所有类都与容器相关,因此这意味着将其完全移动到Composition Root。由于组合根应该depend on everything else,而应用程序代码不应该依赖于组合根,这意味着其他代码不能调用MessageService。通过引入抽象:IMessageService

,可以轻松,优雅地解决这个问题
public class MessageService : IMessageService
{
    ...
}

接口应该在应用程序级别定义,即在Composition Root中的实现。

如果MessageService包含业务逻辑,事情就会变得更加复杂。您将业务逻辑与Container相关逻辑分开。以下是如何处理此问题的一些想法:

  • MessageService中的容器内容提取到注入MessageService的新服务中。
  • 使用允许创建新实例的ServiceA工厂,但要验证工厂是否is the right abstraction
  • 将容器逻辑移出MessageService,进入ServiceA周围的装饰器/代理。这使得生活方式不匹配对ServiceAMessageService都无动于衷。这包括定义ServiceA周围的抽象,让MessageService依赖于此,并从装饰器的ServiceA方法中解析具体的Proccess

答案 1 :(得分:1)

我同意this other answer,因为您不应将应用程序代码绑定到DI容器细节,例如IContainer

使用Autofac,您可以将工厂Func<ServiceA>(而不是ServiceA)注入MessageService 。只要您在容器中注册ServiceA,Autofac就会自动生成这样的工厂功能。

来自Autofac关于implicit relationship type Func<B>的文档:

  

“使用自动生成的工厂可以让您有效地调用[Resolve<B>()]而无需将您的组件绑定到Autofac。[...]

     

“使用此关系类型尊重生命周期范围。如果您将[B]注册为InstancePerDependency()并多次调用Func<B>,则每次都会获得一个新实例。但是,如果您将[B]注册为SingleInstance()并调用Func<B>多次解析对象,则每次都会获得相同的对象实例。“

(我的轻微改动或遗漏放在方括号内。)