Сначала проблемы с областью действия Ninject с кодом TopShelf, Ninject и EF

В настоящее время я использую TopShelf с Ninject для создания службы Windows. У меня есть следующий код для установки службы Windows с помощью TopShelf:

static void Main(string[] args)
{
    using (IKernel kernel = new StandardKernel(new NinjectDependencyResolver()))
    {
        Settings settings = kernel.Get<Settings>();

        var host = HostFactory.New(x =>
        {
            x.Service<BotService>(s =>
            {
                s.ConstructUsing(name => new BotService(settings.Service.TimeInterval));
                s.WhenStarted(ms => ms.Start());
                s.WhenStopped(ms => ms.Stop());
            });

            x.RunAsNetworkService();

            x.SetServiceName(settings.Service.ServiceName);
            x.SetDisplayName(settings.Service.DisplayName);
            x.SetDescription(settings.Service.Description);
        });

        host.Run();
    }
}

Это объект, стоящий за службой Windows, выполняющий все работа:

public class BotService
{
    private readonly Timer timer;

    public BotService(double interval)
    {
        this.timer = new Timer(interval) { AutoReset = true };
        this.timer.Elapsed += (sender, eventArgs) => Run();
    }

    public void Start()
    {
        this.timer.Start();
    }

    public void Stop()
    {
        this.timer.Stop();
    }

    private void Run()
    {
        IKernel kernel = new StandardKernel(new NinjectDependencyResolver());

        Settings settings = kernel.Get<Settings>();

        if (settings.Service.ServiceType == 1)
        {
            // The interface implementation has constructor injection of IUnitOfWork and IMyRepository
            kernel.GetAll<IExternalReportService>().Each(x => x.Update());
        }

        if (settings.Service.ServiceType == 2)
        {
            // The interface implementation has constructor injection of IUnitOfWork and IMyRepository
            kernel.GetAll<IExternalDataService>().Each(x => x.GetData());
        }

        kernel.Get<IUnitOfWork>().Dispose();
        kernel.Dispose();
    }
}

Это привязки Ninject:

public class NinjectDependencyResolver : NinjectModule
{
    public override void Load()
    {
        Settings settings = CreateSettings();
        ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["DB"];

        Bind<IDatabaseFactory>().To<DatabaseFactory>()
                                .InThreadScope()
                                .WithConstructorArgument("connectionString", connectionStringSettings.Name);

        Bind<IUnitOfWork>().To<UnitOfWork>();
        Bind<IMyRepository>().To<MyRepository>();
        Bind<IExternalReportService>().To<ReportService1>();
        Bind<IExternalReportService>().To<ReportService2>();
        Bind<IExternalDataService>().To<DataService1>();
        Bind<IExternalDataService>().To<DataService2>();

        Bind<Settings>().ToConstant(settings);
    }

    private Settings CreateSettings()
    {
        // Reads values from app.config and returns object with settings
    }
}

Прежде всего позвольте мне сказать, что меня не устраивает этот код. Когда приложение запускает, создается экземпляр ядра, выбираются значения из настроек, и я использую TopShelf для создания службы Windows с помощью объекта BotService.

Каждый раз, когда запускается событие таймера, выполняется метод Run (). Здесь создается другой экземпляр ядра, он снова считывает настройки и, в зависимости от значения, ядро ​​извлекает все реализации интерфейса и выполняет соответствующий метод.Каждая из этих реализаций имеет конструктор, в который вводятся IUnitOfWork и IMyRepository для доступа к данным.

Когда методы завершены, я избавляюсь от контекста и удаляю ядро.

Почему я так настроил? Первоначально я создал только одно ядро ​​в Main и использовал конструктор в BotService для внедрения реализаций, а не для создания другого экземпляра ядра. Проблема заключалась в том, что для работы DatabaseFactory требовались InSingletonScope или InThreadScope.

Если бы я использовал InSingeltonScope, контекст стал бы устаревшим, и в конечном итоге проблемы начали бы накапливаться там, где контекст недействителен. Если бы я использовал InThreadScope, я столкнулся с той же проблемой, потому что он не удаляет объекты после завершения потока. В конце концов, Run () использовал ранее использованный поток и возникло исключение, поскольку я уже удалил Context. Если я удаляю строку кода, в которой хорошо удаляю контекст, мы сталкиваемся с той же проблемой, что и InSingletonScope, когда мы получаем устаревший контекст при повторном использовании потока.

Это приводит к текущему коду, в котором я гарантирую, что каждый раз выполняется Time Run (), контекст существует до тех пор, пока он не будет выполнен в том месте, где он размещен, и, поскольку ядро ​​также удалено, я гарантирую, что в следующий раз будет запущен тот же поток. Используется, мы получаем новый контекст, поскольку ядро ​​воссоздается заново (по крайней мере, я думаю, что это то, что происходит).

Навыки My Ninject не так высоки, и информации о том, как подойти к этой проблеме, очень мало.Я думаю, что правильным подходом было бы создать одно ядро ​​только в Main, а затем иметь возможность внедрять то, что мне нужно, в объект BotService через конструктор. Но в то же время контекст необходимо создавать для каждого Run (), чтобы избежать устаревшего контекста, который мог бы произойти, если бы я использовал одну из областей, упомянутых выше, с этим подходом.

Как я могу изменить приведенный выше пример, чтобы он был правильным? В настоящее время я использую Ninject 2.2.1.4.

5
задан Robert Harvey 18 October 2012 в 23:43
поделиться