Autofac: зарегистрировать компонент как разные сервисы, используя разные конструкторы - PullRequest
0 голосов
/ 27 июня 2018

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

Я пробовал это:

_builder.RegisterType<TComponent>()
    .As<IService1>()
    .FindConstructorsWith(ConstructorFinder1)
    .SingleInstance();

_builder.RegisterType<TComponent>()
    .As<IService2>()
    .FindConstructorsWith(ConstructorFinder2)
    .SingleInstance();

Но это приводит к двум разным «одиночным» экземплярам, ​​в зависимости от того, какая служба использовалась.

Итак, я попробовал:

_builder.RegisterType<TComponent>()
    .As<IService1>()
    .FindConstructorsWith(ConstructorFinder1)
    .As<IService2>()
    .FindConstructorsWith(ConstructorFinder2)
    .SingleInstance();

Это решает проблему синглтона, но, к сожалению, второй вызов FindConstructorsWith отменяет первый вызов, т. Е. Для обеих служб используется ConstructorFinder2.

Я предполагал (надеялся), что ConstructorFinders будет храниться в отношении службы, но, очевидно, это не так.

Является ли то, что я пытаюсь достичь концептуально, неверным, Autofac не поддерживает это или я просто что-то упускаю?

EDIT: Еще раз спасибо Трэвису за отличный ответ. Видимо, я пропустил несколько деталей, которые запутали. Позвольте мне добавить немного сейчас.

Этот вопрос фактически был своего рода продолжением Как определить, какой конструктор Autofac использует при разрешении (где Трэвис также помог мне в этом). Таким образом, проблема возникает при десериализации и затрагивает множество различных объектов.

Я получаю аргументы о композиции, разделении проблем и о том, что наличие нескольких ctors часто считается запахом кода, но в контексте десериализации (по крайней мере, для приложения, которое я сейчас разрабатываю), чрезвычайно полезно иметь возможность создавать экземпляры по-разному, в зависимости от того, были ли они вновь созданы или десериализованы из файла проекта. Несколько членов, которые должны быть инициализированы при создании нового экземпляра, не должны инициализироваться при десериализации (поскольку их значения в любом случае будут переопределены при десериализации). Это будет означать дополнительные затраты производительности и (и, в данном случае) вызвать другие проблемы, связанные с одноразовыми инициализациями.

Потратив несколько дней, пытаясь найти решение (с осложнениями, также со стороны Newtonsoft Json), я решил прекратить Autofac и внедрить наш собственный контейнер IOC. В общих целях он не может (очевидно!) Каким-либо образом конкурировать с Autofac, но, поскольку мы действительно использовали только небольшое подмножество замечательных функций Autofac, я чувствовал, что мы можем попытаться реализовать свои собственные. Это заняло у меня намного меньше, чем те дни, которые я потратил на то, чтобы обернуть голову вокруг черного монолитного ящика. Да, Autofac с открытым исходным кодом, но, проходя через код, не ходите по парку.

Первые тесты очень многообещающие, и приятно восстановить полный контроль над таким жизненно важным компонентом приложения.

Опять же, причина ухода из Autofac заключалась в том, что невозможно (практически) определить, как создается одноэлементный компонент в зависимости от службы, для которой он был создан. И с точки зрения общей структуры / концепции, я понимаю, что имеет смысл строго разделять сервис и конструкцию. Но во время десериализации все обстоит иначе, я считаю. И теперь, когда я независим от Autofac, я могу решить изменить механизмы, чтобы они вписывались в общую концепцию более простым способом.

1 Ответ

0 голосов
/ 29 июня 2018

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

Позвольте мне пройти через это, чтобы понять, могу ли я объяснить, почему трудно ответить.

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

Если это синглтон , это означает, что один во всей системе , верно? Это будет эффективно "первым в победах". Если что-то разрешит его как IService1, будет вызван связанный с ним конструктор, и даже если вы попытаетесь разрешить его как IService2, позже не будет построений, потому что синглтон был создан. Обратное также верно - IService2 разрешается, и путь конструктора идет туда, тогда вещи, запрашивающие IService1, получат синглтон, и конструктор не будет вызван.

Это вызывает беспокойство:

  • Если вы знаете , что, безусловно, будет решаться в первую очередь, тогда зачем вам два разных селектора конструктора?
  • Если вы не знаете , что будет решаться в первую очередь, то учитываете ли вы непредсказуемость системы?

Я уже видел подобные вопросы раньше, и обычно , что они указывают, это одна из двух вещей:

  1. Вы пытаетесь сделать что-то вроде выбора или специальной логики, основанной на контексте. Существует FAQ по Autofac по этому поводу, который может помочь. Обычно обходной путь заключается в рефакторинге. Я вернусь к этому через секунду.
  2. Вы пытаетесь «разделить регистрации» между двумя разными приложениями. Ответ на этот вопрос заключается в использовании модулей Autofac и повторном использовании этих ; но если есть специальные регистрации для каждого типа приложения, пусть это произойдет.

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

В примечании по рефакторингу для пункта 1, выше, я могу также предположить, основываясь на желании единственного объекта, что есть какой-то ресурс, такой как соединение с базой данных, который должен быть общим или дорогим для ускорения. Рассмотрим разбиение TComponent на три отдельных класса:

  • TCommonExpensiveComponent - это материал, который на самом деле дорого раскручивается и действительно должен быть одиночным, но не отличается по IService1 и IService2.
  • TService1 - реализовать IService1 только с требуемым конструктором, чтобы вам не нужен искатель конструктора. Пусть он потребляет TCommonExpensiveComponent.
  • TService2 - реализовать IService2 только с требуемым конструктором, чтобы вам не нужен искатель конструктора. Пусть он потребляет TCommonExpensiveComponent.

Идея состоит в том, чтобы избежать сложности регистрации, оставить общий / одиночный файл, который вам нужен, и по-прежнему использовать другое конструктор по мере необходимости. Вы также можете добавить некоторые общие базовые / абстрактные классы, из которых TService классы могут наследовать, если действительно много общей логики.

Является ли то, что я пытаюсь достичь концептуально, неверным, Autofac не поддерживает это или я просто что-то упускаю?

Технически, вы можете сделать несколько действительно сумасшедших вещей в Autofac, если хотите, например, написать пользовательский источник регистрации , который ждет, когда кто-то запросит регистрацию IService1 или IService2, а затем выберет основанный на этом конструктор, динамически обслуживающий регистрацию по мере необходимости. Но, на самом деле, даже не начинайте этот путь.

Вместо этого было бы хорошо уточнить, в чем заключается проблема, которую вы пытаетесь решить, и как вы планируете работать над вызовами, перечисленными выше, если мой ответ здесь не поможет. Сделайте это в новом вопросе, который более подробно описывает вашу проблему и то, что вы пробовали. Это не форум, а попытка разговора и отмена дополнительной помощи, учитывая, что текущий вопрос действительно не не выполнимо Плюс, если вы сделаете секунду, чтобы сделать шаг назад и, возможно, переформулировать вопрос, звучит так, как будто он может помочь здесь.

...