Ninject 4.0.0-бета-0134 выдает сообщение «Обнаружена циклическая зависимость между конструкторами двух сервисов»

#c# #ninject

#c# #ninject

Вопрос:

Сегодня я обновил нашу зависимость Ninject с 3.3.4 до 4.0.0-beta-0134, но теперь она выдает исключение циклической зависимости в шаблоне декоратора:

`Необработанное исключение: Ninject.Исключение ActivationException: ошибка при активации Program IService с использованием условной привязки Program IService к Program Service Между конструкторами двух сервисов была обнаружена циклическая зависимость.

Путь активации: 2) Внедрение программы зависимостей IService в службу параметров конструктора типа Program ServiceDecorator

  1. Запрос для Program IService

Предложения:

  1. Убедитесь, что вы не объявили зависимость для Program IService ни от одной реализации сервиса.
  2. Рассмотрите возможность объединения сервисов в один, чтобы удалить цикл.
  3. Используйте внедрение свойств вместо внедрения конструктора и реализуйте IInitializable, если вам нужно, чтобы логика инициализации выполнялась после ввода значений свойств. `

Вот пример кода:

     public static void Main(string[] args)
    {
        var kernel = new StandardKernel(); 
        kernel.Load(new IocConfig());
        kernel.Get<IService>().Serve();
    }

    internal interface IService
    {
        void Serve();            
    }

    public class Service : IService
    {
        public void Serve() { }
    }

    public class ServiceDecorator : IService
    {
        private readonly IService _service;

        public ServiceDecorator(IService service) => _service = service;

        public void Serve() => _service.Serve();
    }

    public class IocConfig : NinjectModule
    {
        public override void Load()
        {
            Bind<IService>().To<Service>().WhenInjectedInto<ServiceDecorator>().InSingletonScope();
            Bind<IService>().To<ServiceDecorator>().InSingletonScope();
        }
    }
 

Похоже, что ему не нравится использование WhenInjectedInto в NinjectModule. Я нашел другие подобные вопросы, но ни WhenInjectedInto один из них не работал.

Один из способов исправить это — изменить тип параметра сервиса с IService на Service на, но это устраняет большую часть причин использования декоратора.

Кто-нибудь из вас знает о другом решении / обходном пути?

Ответ №1:

После выполнения еще одного прохода может показаться, что проблема на самом деле вызвана использованием .WhenInjectedInto<ServiceDecorator> , поскольку между привязками нет различий.

Альтернативой является простое использование Bind<IService>().To<Service>().InSingletonScope(); , а затем добавление a BindingConfiguration.Condition в ваш декоратор для обработки Get<ServiceDecorator>() варианта использования:

 public static void Main(string[] args)
{
    var kernel = new StandardKernel();
    kernel.Load(new IocConfig());
    kernel.Get<IService>().Serve();
    kernel.Get<ServiceDecorator>().Serve();
}

public interface IService
{
    void Serve();
}

public class Service : IService
{
    public void Serve() { Console.WriteLine("service"); }
}

public class ServiceDecorator : IService
{
    private readonly IService _service;

    public ServiceDecorator(IService service) => _service = service;

    public void Serve() { Console.WriteLine("decorator"); _service.Serve(); }
}

public class IocConfig : NinjectModule
{
    public override void Load()
    {
        Bind<IService>().To<Service>().InSingletonScope();
        Bind<IService>().To<ServiceDecorator>().InSingletonScope().BindingConfiguration.Condition =
            (Ninject.Activation.IRequest request) =>
                request.Service == typeof(ServiceDecorator);
    }
}
 

Комментарии:

1. Спасибо, Дэвид! Это именно то, что я ищу! Работает для меня.

Ответ №2:

Неясно, как вы собираетесь использовать декоратор из этого примера, но одной из возможностей было бы применить новый интерфейс к декоратору, который, в свою очередь, наследуется от IService .

 public interface IServiceDecorator : IService { }

public interface IService
{
    void Serve();
}

public class Service : IService
{
    public void Serve() { }
}

public class ServiceDecorator : IServiceDecorator
{
    private readonly IService _service;

    public ServiceDecorator(IService service) => _service = service;

    public void Serve() => _service.Serve();
}

public class IocConfig : NinjectModule
{
    public override void Load()
    {
        Bind<IService>().To<Service>().InSingletonScope();
        Bind<IService>().To<Service>().WhenInjectedInto<ServiceDecorator>().InSingletonScope();
        Bind<IServiceDecorator>().To<ServiceDecorator>().InSingletonScope();
    }
}
 

Комментарии:

1. Интересно, что это создало бы множество стандартных интерфейсов, которых было бы очень приятно избежать… Спасибо за ваш вклад!