Простой инжектор, сочетающий WCF и обычную регистрацию

Я разрабатываю службу Windows, в которой будут размещаться две вещи:

  • WCF-сервисы
  • обычная служба Windows для периодического выполнения заданий (с использованием Quartz.net)

Таким образом, в основном одно приложение (исполняемое) размещает эти два типа службы.

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

Я уже запустил и запустил часть WCF, используя расширения Simple Injectors WCF:

public class DependencyResolver : IPackage
{
    public void RegisterServices(Container container)
    {
        container.EnablePerWcfOperationLifestyle();
        var wcfLifestyle = new WcfOperationLifestyle();

        container.Register<IUnitOfWork, UnitOfWork>(wcfLifestyle);
        container.Register<ICustomerRepository, CustomerRepository>(
            wcfLifestyle);
        //etc...
        
        container.RegisterWcfServices(Assembly.GetExecutingAssembly());
    }
}

Мой вопрос: могу ли я повторно использовать этот репозиторий и другие регистрации внутри обычной сервисной части Windows, то есть той, которая НЕ использует WCF? Потому что я не уверен, как WcfOperationLifestyle повлияет на них, если я это сделаю.

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

Должен ли я создавать новые регистрации? Если да, то как мне вызвать их из исполняемого кода?


person tjeuten    schedule 01.04.2014    source источник
comment
пожалуйста, можете ли вы описать или дать ссылку о том, как вы сделали свой Self-hosted WCF+WindowsService+SimpleInjectors (особенно интересует DI)   -  person aleha    schedule 25.05.2016


Ответы (1)


ВНИМАНИЕ: этот ответ сильно устарел. Актуальную информацию см. в руководстве по интеграции.


Очень маловероятно, что какой-либо компонент будет иметь ограниченный образ жизни в одной части приложения (WCF) и действовать как переходный процесс в другой части. В вашем случае гораздо более вероятно, что ваша единица работы также должна быть ограничена в Quartz, но поскольку нет неявной области (например, HTTP-запроса, сеанса WCF и т. д.), вам придется явно начать/завершить некоторую область где выполняются операции и регистрируют их как таковые.

В Simple Injector есть несколько неявных стилей жизни с ограниченной областью действия, таких как:

  • WebApiRequestLifestyle
  • WebRequestLifestyle
  • WcfOperationLifestyle

Явный образ жизни scped:

  • LifetimeScopeLifestyle (который определяет область действия для конкретного потока)
  • ExecutionContextScopingLifestyle (который определяет область действия, зависящую от асинхронного контекста).

Поэтому при работе внутри Quartz ваша единица работы должна иметь либо LifetimeScopeLifestyle, либо ExecutionContextScopeLifestyle. Выбор между ними прост: если операция асинхронная и ваши методы возвращают Task<T>, вам нужен ExecutionContextScopeLifestyle, иначе вам нужен LifetimeScopeLifestyle. В этом вопросе/ответе содержится дополнительная информация о том, как начать работу с Quartz, и о сроках службы.

Имея это в виду, я думаю, у вас есть три варианта:

  1. Рассматривайте обе службы (WCF и Quartz) как два независимых приложения, которые находятся в одном и том же AppDomain и создают два Корни композиции с собственным SimpleInjector.Container экземпляром. (вы также можете рассмотреть возможность разделения службы на два отдельных сервисных приложения)
  2. Вы отказываетесь от образа жизни WCF и явно запускаете новый LifetimeScope в каждой операции WCF перед разрешением службы, которая выполняет операцию.
  3. Рассматривайте обе службы как единое целое и используйте один корень композиции с одним контейнером, но продолжайте использовать внедрение зависимостей в своих классах обслуживания WCF. Это означает, что вам придется использовать гибридный образ жизни, чтобы получить правильный образ жизни.

Какой вариант лучше для вас, зависит от многих факторов. Например, создание двух корней композиции может быть очень простым делом, и, извлекая общие регистрации в общий метод, вы можете сделать корни композиции очень удобными в сопровождении. Все образы жизни с областью действия наследуются от ScopedLifestyle, и это позволяет вам разрешить этому общему методу принимать ScopedLifestyle, что позволяет общему методу не обращать внимания на то, для какой службы он вызывается. Когда это становится трудным, когда у вас есть компоненты, где на самом деле должен быть только один единственный экземпляр в полном AppDomain (поскольку Singleton означает «один для каждого контейнера»).

Вместо того, чтобы позволить вашим службам WCF иметь какую-либо логику, вы можете сделать их действительно тонкими слоями, которые будут просто связующим звеном между WCF, вашим контейнером IOC и приложением. Таким образом, вместо того, чтобы внедрять зависимости в ваши службы WCF, вы можете просто разрешить реальную службу при вызове операции WCF:

[OperationContract]
public SomeData DoSomething() {
    using (Bootstrapper.Container.BeginLifetimeScope()) {
        return Bootstrapper.Container.GetInstance<IDoSomethingService>()
            .DoSomething();
    }
}

Это позволяет вам зарегистрировать все компоненты области действия с одним и тем же LifetimeScopeLifestyle. Эта модель работает очень хорошо, когда у вас есть архитектура, управляемая сообщениями, поскольку это означает, что ваш уровень WCF будет состоять не более чем из двух методов, которые можно прочитать здесь. Этот подход может упростить задачу, поскольку WcfOperationLifestyle имеет несколько неожиданное поведение, потому что он кэширует службы не «для каждой операции WCF», а на время действия класса обслуживания WCF (класса, содержащего эти методы операций), и в в конце концов, именно WCF и ваша конфигурация определяют, как долго должна жить такая служба. Это может быть PerCall, PerSession или даже Single.

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

Третий вариант — использовать гибридный образ жизни следующим образом:

ScopedLifestyle hybridLifestyle = Lifestyle.CreateHybrid(
    container.GetCurrentWcfOperationScope() != null,
    new WcfOperationLifestyle(),
    new LifetimeScopeLifestyle());

container.Register<IUnitOfWork, UnitOfWork>(hybridLifestyle);

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

person Steven    schedule 01.04.2014
comment
спасибо за это очень тщательное объяснение. Вы бы порекомендовали разделить службу WCF и Windows на два отдельных исполняемых файла? Мне интересно, является ли обычной практикой использование службы Windows для запуска запланированных заданий, а в то же время иметь фасад WCF для обеспечения взаимодействия. - person tjeuten; 01.04.2014
comment
@tjeuten: я бы сказал, что это обычная практика. IIS довольно агрессивен в утилизации (убийстве) доменов приложений, и это может повлиять на ваш фоновый процесс. - person Steven; 01.04.2014
comment
Хорошо, спасибо, я знаю об утилизации IIS, поэтому решил разместить службы WCF внутри службы Windows. Я хотел спросить, является ли обычной практикой размещение как WCF, так и периодических заданий в одном и том же домене приложения службы Windows (исполняемом) - person tjeuten; 01.04.2014
comment
@tjeuten: я не знаю, но если вы уже знакомы со службами Windows, их довольно легко разделить на две отдельные службы. - person Steven; 01.04.2014