前回の続きです。
前回は『ClientはASP.NET Webアプリケーション、ServerをWindowsコンソールアプリケーションで実装』という形の実装に変更しました。
構成としては今回私がやりたい要件*1を満たせそうですが、一点、実現できていないことがありました。
それは、DLLとした切り出したジョブに対してServer(ジョブの実行プロセス)側から設定などの情報を差し込むことです。
例えばジョブの中でDBに接続したいのですが、その際の接続文字列もしくはDbContextなどは実行プロセス側で用意したものを利用したいわけです。
結果を先に言うと、DIコンテナを使うことで実現が可能でした。HangfireはDIコンテナを用いてIoCを実現するインフラがあり、また.NETのメジャーなDIコンテナを簡単に使うための拡張も提供されています。今回はDIコンテナとしてAutofacを使ってみました。
今回はその内容のメモになります。
今回やってみること
今回は、DIコンテナを使って『Hangfire Serverのプロセスからジョブ実行時にDB接続文字列を渡す』というのを実現してみます。
ソリューションは前回使ったものに手を入れていきます。
1. ジョブをインタフェースにと実装に切り分ける
まずはJOBをインタフェースと実装に分離します。さらに『Serverプロセスで実装する何らかのオブジェクトを受け取る』想定の実験(?)のために、設定値を格納するオブジェクト実装するためのインタフェース『IJobConfiguration』を作成し、『CustomJob.cs』はコンストラクタでその実装を受け取って利用するように書き換えます。
namespace MyJobsLib
{
public interface IJobConfiguration
{
string ConnectionString { get; set; }
}
}
namespace MyJobsLib
{
public interface ICostomJob
{
void Execute(string message);
}
}
using System.IO;
namespace MyJobsLib
{
public class CustomJob : ICostomJob
{
private IJobConfiguration JobConfiguration = null;
public CustomJob(IJobConfiguration jobConfiguration)
{
JobConfiguration = jobConfiguration;
}
public void Execute(string message)
{
var fileName = string.Format("HangfireJob_{0}.txt", System.Guid.NewGuid());
var filepath = Path.Combine(Path.GetTempPath(), fileName);
using (var writer = new StreamWriter(filepath, true))
{
writer.WriteLine(message);
writer.WriteLine(JobConfiguration.ConnectionString);
}
}
}
}
2. 【Hangfire Client側】 キュー登録処理を変更
BackgroundJob.Enqueue
メソッドでキューにジョブを登録する際の記述を以下のように変更します。型引数にジョブのインタフェースを指定する記述になります。引数の書き方も変わります。
using Hangfire;
using System.Web.Mvc;
namespace HangfireSample01.Web.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
BackgroundJob.Enqueue<MyJobsLib.ICostomJob>(x => x.Execute("Job Executed."));
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
3. 【Hangfire Server側】 DIコンテナを利用するように変更
最後にServer側です。ここは以下の対応を行います。
- IJobConfigurationを実装したクラスを実装
- Autofacに対応した拡張をNuGetでインストール
- コンテナの登録
IJobConfigurationを実装したクラスを実装する
IJobConfigurationを実装した、Serverのプロセス側からジョブに注入するオブジェクトのクラスを実装します。今回はそのままJobConfiguration
とし、コンストラクタで設定値を積み込むようなものを想定したものにしてみました。
namespace HangfireSample01.ServerConsole
{
public class JobConfiguration : MyJobsLib.IJobConfiguration
{
public string ConnectionString { get; set; }
public JobConfiguration()
{
ConnectionString = "接続文字列だよ";
}
}
}
Autofacに対応した拡張をNuGetでインストールする
NuGetパッケージ Hangfire.Autofac
をインストールします。Autofac自体も自動的にインストールされます。パッケージマネージャーコンソールで行う場合は以下です。
PM > Install-Package HangFire.Autofac
コンテナの登録
最後にコンテナに型を登録し、コンテナの情報をHangfireのActivatorに設定する記述を行います。
using Autofac;
using Hangfire;
using System;
namespace HangfireSample01.ServerConsole
{
class Program
{
static void Main(string[] args)
{
var constr = @"Data Source=localhost;Initial Catalog=HangfireJob;Integrated Security=True";
GlobalConfiguration.Configuration.UseSqlServerStorage(constr);
var builder = new ContainerBuilder();
builder.RegisterType<JobConfiguration>().As<MyJobsLib.IJobConfiguration>();
builder.RegisterType<MyJobsLib.CustomJob>().As<MyJobsLib.ICostomJob>();
GlobalConfiguration.Configuration.UseAutofacActivator(builder.Build());
using (var server = new BackgroundJobServer())
{
Console.WriteLine("Hangfire Server started. Press any key to exit...");
Console.ReadKey();
}
}
}
}
以上でコーディングは完了です。
実行して確認する
さっそく実行して確認してみると、ダッシュボードで見えるジョブ名が「ICostomJob.Execute」とインタフェースでの記述に変わっています。またジョブによって作成されるファイルにも、Server側のプロセスから注入された値が反映されていることが分かります。
まとめ
HangfireはDIコンテナの利用が想定されていて、メジャーなDIコンテナの実装に対応した拡張も提供されています。(参照:Extensions/IoC Containers。
実際に、特に面倒もなくDIコンテナを利用できることがお分かりいただけたのではないでしょうか。
サンプルソースは例によって以下のブランチstep03です。
github.com
これでいったんこのメモのシリーズは終了する予定ですが、この後もHangfireを使っていて調べたこと・ハマったこと等を随時メモしていくと思います。