Вставить уже разрешенный экземпляр (через конструктор) в ту же область, что и Autofac? - PullRequest
0 голосов
/ 22 мая 2018

Ну, я думаю, что это возможно, но не уверен, как это можно сделать.Вот мой сценарий: у меня есть 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, введенный в ServiceB, должен быть именно экземпляром, serviceA введенным в MyConsumer, а не каким-то новым отличным экземпляром).Обратите внимание: я не хочу показывать ServiceA через ServiceB и просто сделать MyConsumer зависимым только от IServiceB (на самом деле ServiceA является базовой услугой, в то время как другие службы, включая ServiceB, являются просто расширениями, то естьможет быть больше сервисов, таких как ServiceB в этом случае)

Я действительно новичок в Autofac и действительно даже с Unity (я использовал больше всего), я никогда не думал об этом сценарии раньше, поэтомуна самом деле у меня нет никакого кода, сделанного до сих пор.

Я надеюсь, что кто-то здесь уже сталкивался с тем же сценарием и мог бы предложить мне кое-что, спасибо!

Дополнительный вопрос: что если порядок параметров (переданный в MyConsumer конструктор) будет изменен?Я имею в виду, это влияет на порядок разрешения и может привести к неожиданному результату?Если это возможно, порядок параметров не должен иметь значения (потому что MyConsumer должен просто заботиться о том, что ему нужно, а не о том, как упорядочены необходимые ему вещи).

Более полное представление о проблеме Некоторые предлагали использовать defined scope, который поддерживается в некоторых случаях autofac.Однако мой сценарий здесь другой, и я думаю, что не очень удобно заставлять autofac понимать, что я хочу.

Здесь MyConsumer фактически никогда не может быть разрешен напрямую (с использованием метода .Resolve), потому что он можетбыть просто зависимостью другого класса (это то, что мы называем .Resolve).Используя scope convention, я думаю, что это правильный подход, но область действия в моем случае отличается, я думаю, что он естественным образом определяется конструктором класса (все внедренные зависимости должны находиться в одной области видимости - и каждый экземпляр типа в этомсфера должна быть одноэлементной - которая разделяется между ними).Не уверен, почему этого не хватает из того, что Autofac может предоставить нам.

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Вот отдельное решение от другого ответа, которое, мы надеемся, решит ваши проблемы по поводу особого использования контейнера:

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 интерфейс, а затем заменить конструктор чем-то вроде

public MyConsumer(Func<IServiceA> serviceAFactory, Func<IServiceB> serviceBFactory)
{
    _serviceA = serviceAFactory();
    _serviceB = serviceBFactory(_serviceA);
    _serviceB.SetServiceA(_serviceA);
}
0 голосов
/ 27 мая 2018

Мне кажется, все, что вам нужно сделать, это убедиться, что все они - MyConsumer, ServiceA и ServiceB (и другие "расширения", как вы их называете) - зарегистрированы как InstancePerLifetimeScope(),Пока они разрешены из одной и той же области видимости, все они будут «одиночками» для этой области.Вы пробовали это в своем коде?Если у вас возникли какие-то проблемы с этим подходом, вам нужно четко сказать это в своем вопросе, потому что довольно сложно понять, в чем заключается ваша настоящая проблема.Лично я считаю, что проблема не в самом автофаке и не в том, чтобы в него вписывать свои услуги, а в том, что он просто не знаком с новым контейнером DI - что вполне естественно, каждая новая технология имеет свою кривую обучения.Просто продолжайте пытаться, продолжайте экспериментировать, и вы сделаете это.Autofac - очень мощный DI-контейнер, поэтому я уверен, что он может делать то, что вы хотите.

0 голосов
/ 22 мая 2018

Я собрал небольшой пример того, что, по моему мнению, будет работать в вашей ситуации:

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>), в которых только один экземпляр IServiceA, IServiceB и IMyConsumer будут существовать.Стоит отметить, что вы всегда должны разрешать / вводить Owned<IMyConsumer> и никогда не разрешать / вводить простой IMyConsumer, если вы выберете такой подход, иначе зависимости IServiceA и IServiceB будут одиночными.

Это также должно обработать ваш бонусный вопрос - ничто в этом примере не заботится о порядке параметров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...