現在、NinjectでTopShelfを使用してWindowsサービスを作成しています。私はセットアップに次のコードをWindowsサービスはTopShelf使用していますTopShelf、Ninject、EFコードの問題点の問題
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
}
}
:これらはNinjectバインディングです
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();
}
}
:
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サービスの背後にあるオブジェクトは、すべての仕事をしています
まず、このコードに満足していないと言いましょう。アプリケーションが起動すると、カーネルのインスタンスが作成され、設定の値がフェッチされ、TopShelfを使用してBotServiceオブジェクトを使用してWindowsサービスを作成します。
タイマイベントが発生するたびに、Run()メソッドが実行されます。ここでカーネルの別のインスタンスが作成され、再び設定を読み込み、カーネルがそのインタフェースのすべての実装をフェッチし、対応するメソッドを実行します。これらの各実装には、IUnitOfWorkとIMyRepositoryがデータアクセスのために注入されるコンストラクタがあります。
このメソッドが終了すると、コンテキストを破棄してカーネルを破棄します。
なぜこのように設定しましたか?もともと、私はMainに1つのカーネルしか作成せず、BotServiceにコンストラクタを使用して、カーネルの別のインスタンスを作成するのではなく、実装を注入しました。問題は、DatabaseFactoryが動作するためにInSingletonScopeまたはInThreadScopeが必要であることでした。
InSingeltonScopeを使用した場合、コンテキストが古くなり、最終的にはコンテキストが無効な場所ではじまり始める可能性があります。 InThreadScopeを使用した場合、スレッドが完了するとオブジェクトを破棄しないため、同じ問題が発生します。最終的にRun()は以前に使用されたスレッドを使用していましたが、すでにContextを処分しているので例外が発生しています。コンテキストをうまく処分するコード行を削除した場合、InSingletonScopeと同じ問題が発生し、スレッドが再利用されたときに古くなったコンテキストになります。
これは、各Time Run()が実行されることが保証されている現在のコードにつながります。コンテキストは、処理される場所まであり、カーネルも同様に廃棄されるので、カーネルが再作成されて以来、私たちは新しいコンテキストを取得しています(少なくとも、これは起こっていると思います)。
私のNinjectのスキルはそれほど高度ではなく、この問題に近づく方法に関する情報は非常に限られています。私は適切なアプローチは、メインのみで1つのカーネルを作成し、必要なものをコンストラクタ経由でBotServiceオブジェクトに注入できるようにすることだと思います。しかし同時に、このアプローチで上記のスコープの1つを使用した場合に起こる古いコンテキストを避けるために、Run()ごとにコンテキストを作成する必要があります。
上記の例を修正するにはどうしたらよいですか?私は現在、Ninject 2.2.1.4を使用しています。
私はTopshelfメーリングリストでこれを投げました。 https://groups.google.com/d/topic/topshelf-discuss/5H_VUYNzjeY/discussionここで大きな助けになるには、NInjectには馴染みがありません。もし私が推測しなければならないのであれば、ループの始めにUnitOfWork/etcをリフレッシュして最後に処分して、新しいカーネルを作成する必要はありません。 – Travis