В настоящее время я использую 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.
Каждый раз, когда запускается событие timer, выполняется метод Run (),Здесь создается другой экземпляр ядра, который снова считывает настройки и, в зависимости от значения, ядро выбирает все реализации интерфейса и выполняет соответствующий метод.Каждая из этих реализаций имеет конструктор, в котором IUnitOfWork и IMyRepository внедряются для доступа к данным.
Когда методы заканчиваются, я избавляюсь от контекста и уничтожаю ядро.
Почему я его установилкак это?Первоначально я создал только одно ядро в Main и использовал конструктор в BotService для внедрения реализаций, а не для создания другого экземпляра ядра.Проблема заключалась в том, что DatabaseFactory для работы требовался InSingletonScope или InThreadScope.
Если бы я использовал InSingeltonScope, контекст стал бы устаревшим, и в конечном итоге возникали проблемы, когда контекст недопустим.Если я использовал InThreadScope, я столкнулся с той же проблемой, потому что он не удаляет объекты после завершения потока.В конце концов Run () использовала ранее использовавшийся поток и исключительную ситуацию, так как я уже избавился от контекста.Если я удаляю строку кода, где я удаляю контекст, то мы сталкиваемся с той же проблемой, что и InSingletonScope, где мы получаем устаревший контекст при повторном использовании потока.
Это приводит к текущемукод, в котором я гарантирую, что каждый Time Run () выполняется, контекст существует до тех пор, пока он не будет выполнен там, где он расположен, и поскольку ядро также расположено, я гарантирую, что в следующий раз при использовании того же потока мы получим новый контекст, так какЯдро воссоздано (по крайней мере, я думаю, что это именно то, что происходит).
Мои навыки Ninject не настолько продвинуты, и существует очень ограниченная информация о том, как решить эту проблему.Я думаю, что правильным подходом было бы создать одно ядро только в Main и затем иметь возможность вставлять то, что мне нужно, в объект BotService через конструктор.Но в то же время контекст должен быть создан для каждого Run (), чтобы избежать устаревшего контекста, который случился бы, если бы я использовал одну из областей, упомянутых выше, с этим подходом.
Как я могу изменитьпример выше так будет правильно?В настоящее время я использую Ninject 2.2.1.4.