DI: Register-Resolve-Release при использовании дочерних контейнеров - PullRequest
1 голос
/ 17 января 2012

Есть это программное обеспечение, X, у которого есть этот действительно сложный API, для которого я должен написать фасад. Я написал библиотеку классов XClientLibrary и создал ее с использованием контейнера DI и IoC (Unity). Это стало возможным, потому что моя библиотека экспортирует сервисы (интерфейсы), поэтому пользователи не знают о конкретных классах, которые используют конструктор DI. Они также не знают о контейнере IoC.

«Корневая служба» - это экземпляр IXClient, который предполагается создать один раз и использовать до тех пор, пока приложение работает. (Кстати, это настольное приложение). X-клиент позволяет пользователям подключаться к X-хостам, если они знают URL. Х-хост позволяет пользователям получать доступ к сервисам хоста, их сервисам и т. Д. (Довольно сложный граф объектов). Это пример кода пользователя:

// 1. app startup
XClientProvider provider = new XClientProvider(); // call only once per app
IXClient xClient = provider.GetClient(); // always returns the same instance    
xClient.Startup();

// 2. app normal usage
IXHost host = xClient.ConnectToHost(new Uri("http://localhost")); // return new instance each time
IXService1 service = host.GetThis();
IXService2 otherService = service.DoThat();
...
host.Dispose();

// get another host, consume it, dispose it, etc
...

// 3. app shutdown    
xClient.Shutdown();
provider.Dispose();

Я пытался следовать рекомендациям Марка Симанна, чтобы реализовать это, но я не уверен, применимы ли они к библиотеке классов. Поставщик клиента - это корень композиции, который является единственным местом, где используется контейнер IoC. Корень композиции следует шаблону RRR :

  • контейнер создан на new XClientProvider() и настроен
  • контейнер разрешает IXClient при вызове GetClient()
  • контейнер расположен на provider.Dispose()

Все усложняется, когда контейнер запрашивается для разрешения IXHost. Его реализация:

internal class XHost : IXHost    
   public XHost(Uri uri, IXService1 service1)

Клиент должен создавать XHost экземпляров, поэтому его реализация должна знать, как создавать IXService1:

internal class XClient : IXClient
    public XClient(Func<IXService1> xService1DelegateFactory)

Вызов фабрики делегатов достигает контейнера, который создает IXService1. Также предположим, что на этом графике есть класс XComponent7, для которого требуется точный экземпляр IXService1, который использовался для создания хоста:

internal class XComponent7 : IXService7
    public XComponent7(Func<IXService1> service1DelegateFactory)

Мне нужно использовать Func, чтобы справиться с циклической зависимостью. Контейнер должен быть настроен так, чтобы после разрешения IXService1 он предоставлял один и тот же экземпляр всякий раз, когда запрашивается разрешение IXService1.

.

Теперь все становится действительно сложно. Я хочу ограничить это поведение «разрешением каждого узла», то есть после создания узла контейнер должен создать IXService1 и кэшировать его и предоставлять его любому компоненту, который ему нужен, если компонент является частью графа объектов. гостья. Мне также нужен способ утилизации всех компонентов при удалении хоста.

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

Проблема в том, что я думаю, что это нарушает шаблон RRR. Поэтому мне интересно, как работает RRR, когда задействованы дочерние контейнеры ... Может быть, IXHost - это еще один "корень", который может быть напрямую разрешен с помощью корня композиции? Или, может быть, есть действительно умный менеджер времени жизни Unity, который может делать то, что мне нужно?

1 Ответ

1 голос
/ 17 января 2012

@ Suiden Итак, я понимаю: ваш клиент - это то, что позволяет вам искать хосты (например, реестр). Хозяева предлагают услуги, реализованные компонентами. Каждое приложение имеет ровно один экземпляр вашего поиска / клиента. Ваши компоненты не только реализуют службы, но и могут нуждаться в других службах для выполнения своей работы. Вы хотите разрешить все части этого графа объектов ровно один раз, и когда вы избавляетесь от клиента, отбросьте все это.

Пара мыслей:

Следует избегать циклических ссылок между зависимостями (или службами). Если эти сервисы нужны друг другу, это означает, что они должны быть одним сервисом. Вот что такое высокая когезия, низкая связь.

Единство не убирает за собой. Это означает, что даже если вы располагаете контейнером, который не будет располагать объекты, созданные этим контейнером. Функция очистки есть в списке пожеланий для Unity vNext

Если вы хотите разрешить экземпляр какой-либо службы и кэшировать этот экземпляр внутри вашего клиента / хоста, куда бы вы ни посмотрели Lazy . Для создания экземпляра T требуется Func и вычисляется, что Func при первом запросе значения. Таким образом, вы можете добавить Func в ваши классы или научить Unity вводить Lazy экземпляров напрямую .

Дочерние контейнеры - это функция, которую я считаю менее полезной. Вы можете указать регистрационную информацию и время жизни объекта. Но чтобы использовать эти области, вы должны ссылаться на соответствующий дочерний контейнер. Это означает, что вы отбрасываете внедрение зависимостей в пользу ServiceLocator anti-pattern .

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