使用Autofac在同一范围内注入已解析的实例(通过构造函数)?

时间:2018-05-22 15:36:20

标签: c# dependency-injection inversion-of-control autofac ioc-container

嗯,我认为这是可能的,但不确定如何做到这一点。这是我的场景:我有2个服务,一个依赖于另一个,像这样:

public interface IServiceA {
    //...
}
public interface IServiceB {
    //...
}
//the actual implementation
public class ServiceA : IServiceA {
    //...
}
public class ServiceB : IServiceB {
    readonly IServiceA _serviceA;
    public ServiceB(IServiceA serviceA){
        _serviceA = serviceA;
    }
}

我有另一个类消耗这两种服务:

public class MyConsumer {
    readonly IServiceA _serviceA;
    readonly IServiceB _serviceB;
    public MyConsumer(IServiceA serviceA, IServiceB serviceB){
        _serviceA = serviceA;
        _serviceB = serviceB;
    }
}

所以我希望在serviceB的构造函数中向serviceA注入MyConsumer的解析内容。 (意味着注入serviceA的{​​{1}}应该是注入ServiceB的实例serviceA - 而不是一些新的不同实例)。 请注意:我不希望通过MyConsumer公开ServiceA,只让ServiceB仅依赖于MyConsumer(实际上IServiceB是基本服务,而其他包括ServiceA在内的服务只是扩展,这意味着在这种情况下可能会有更多服务,例如ServiceB

我是Autofac的新手,甚至是Unity(我用过的最多)我以前从未想过这个场景,所以到目前为止我还没有任何代码。

我希望此处有人遇到过同样的情况,可以提供一些建议,谢谢!

加分问题:如果参数(传入的ServiceB构造函数)的顺序发生了变化怎么办?我的意思是这会影响解决顺序并导致意外结果吗?如果可能,参数顺序无关紧要(因为MyConsumer应该只关心它需要什么,而不是如何订购所需的东西。

问题的大图: 有些人建议在某些情况下使用autofac支持的MyConsumer。不过我的情况有所不同,我认为强制autofac理解我想要的东西并不是很方便。

这里defined scope实际上永远不会直接解析(使用MyConsumer方法)因为它可能只是另一个类的依赖(我们称之为.Resolve)。 通过使用.Resolve,我认为这是正确的方法,但我的情况下的范围是不同的,我认为它是由类的构造函数自然定义的(注入的所有依赖项应该在同一范围内 - 以及每个类型的实例)在该范围内应该是一个单独的 - 在它们之间共享。不知道为什么Autofac可以为我们提供这一点。

3 个答案:

答案 0 :(得分:1)

这是另一个答案的独立解决方案,希望能解决您对容器特殊用途的担忧:

namespace AutofacTest
{
    public interface IServiceA { }
    public interface IServiceB { }

    public class ServiceA : IServiceA
    {
    }

    public class ServiceB : IServiceB
    {
        private readonly IServiceA _serviceA;

        public ServiceB (IServiceA serviceA)
        {
            _serviceA = serviceA;
        }
    }

    public class MyConsumer
    {
        private readonly IServiceA _serviceA;
        private readonly IServiceB _serviceB;

        public MyConsumer(Func<IServiceA> serviceAFactory, Func<IServiceA, IServiceB> serviceBFactory)
        {
            _serviceA = serviceAFactory();
            _serviceB = serviceBFactory(_serviceA);
        }
    }
}

在这个解决方案中,我注入工厂而不是实例,然后调用它们来获取实例。关于这个实现有点丑陋的一点是参数顺序现在很重要。

如果您真的不想让订单变得重要,您可以在IServiceA界面上为IServiceB公开一个setter,然后用

之类的内容替换构造函数
public MyConsumer(Func<IServiceA> serviceAFactory, Func<IServiceB> serviceBFactory)
{
    _serviceA = serviceAFactory();
    _serviceB = serviceBFactory(_serviceA);
    _serviceB.SetServiceA(_serviceA);
}

答案 1 :(得分:0)

我把一些我觉得适合你情况的小样本放在一起:

namespace AutofacTest
{
    public interface IServiceA { }
    public interface IServiceB { }
    public interface IMyConsumer { }

    public class ServiceA : IServiceA
    {
    }

    public class ServiceB : IServiceB
    {
        private readonly IServiceA _serviceA;

        public ServiceB (IServiceA serviceA)
        {
            _serviceA = serviceA;
        }
    }

    public class MyConsumer : IMyConsumer
    {
        private readonly IServiceA _serviceA;
        private readonly IServiceB _serviceB;

        public MyConsumer(IServiceA serviceA, IServiceB serviceB)
        {
            _serviceA = serviceA;
            _serviceB = serviceB;
        }
    }

    public class AutofacInit
    {
        public IContainer BuildContainer()
        {
            var containerBuilder = new ContainerBuilder();

            containerBuilder.RegisterType<ServiceA>().AsImplementedInterfaces().InstancePerLifetimeScope();
            containerBuilder.RegisterType<ServiceB>().AsImplementedInterfaces().InstancePerLifetimeScope();
            containerBuilder.RegisterType<MyConsumer>().AsImplementedInterfaces().InstancePerLifetimeScope();

            return containerBuilder.Build();
        }

        public void Test()
        {
            using (var container = BuildContainer())
            {
                using (var myConsumer = container.Resolve<Owned<IMyConsumer>>())
                {
                    //use myConsumer.Value
                }
            }
        }
    }
}

我正在使用嵌套的生命周期范围(通过解析Owned<IMyConsumer>创建),其中只存在IServiceAIServiceBIMyConsumer的一个实例。需要注意的一点是,如果采用这种方法,您应始终解析/注入Owned<IMyConsumer>并永远不要解析/注入普通IMyConsumer,否则IServiceA和{{1}依赖关系将是单身人士。

这也应该处理你的红利问题 - 这个样本中的任何内容都不关心参数的排序。

答案 2 :(得分:0)

对我而言,您需要做的就是确保所有这些内容 - MyConsumerServiceAServiceB(以及其他“扩展名”) - 注册为InstancePerLifetimeScope()。只要它们从相同的范围内解决,它们都将成为该范围的“单身人士”。你在代码中试过了吗?如果你在使用这种方法时遇到问题,那么你需要在你的问题中明确说出来,因为很难弄清楚你的真正问题是什么。我个人认为问题不在于autofac本身或是否适合你的服务,它只是缺乏对新DI容器的熟悉 - 这是很自然的事情,每种新技术都有它的学习曲线。只要继续尝试,继续尝试,你就会完成它。 Autofac是非常强大的DI容器,所以我很确定它可以做你想要的。