きよくらの備忘録

「三日坊主と呼ばせない!日記」改め。主にソフトウェア開発関連の話題。

Hangfireを使ってみる (3):HangfireでDIコンテナを使ってみる

前回の続きです。

前回は『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』はコンストラクタでその実装を受け取って利用するように書き換えます。

// IJobConfiguration.cs 【新規】
namespace MyJobsLib
{
  public interface IJobConfiguration
  {
    string ConnectionString { get; set; }
  }
}
// ICostomJob.cs 【新規】
namespace MyJobsLib
{
  public interface ICostomJob
  {
    void Execute(string message);
  }
}
// CustomJob.cs 【変更(主に★の箇所)】
using System.IO;

namespace MyJobsLib
{
  public class CustomJob : ICostomJob // ★ICostomJobの実装にする
  {
    // ★コンストラクタでIJobConfigurationの実装を受け取り保持することにする
    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メソッドでキューにジョブを登録する際の記述を以下のように変更します。型引数にジョブのインタフェースを指定する記述になります。引数の書き方も変わります。

// HomeController.cs 【変更(主に★の箇所)】
using Hangfire;
using System.Web.Mvc;

namespace HangfireSample01.Web.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      // Hangfireでjobをキューに登録
      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側です。ここは以下の対応を行います。

  1. IJobConfigurationを実装したクラスを実装
  2. Autofacに対応した拡張をNuGetでインストール
  3. コンテナの登録
IJobConfigurationを実装したクラスを実装する

IJobConfigurationを実装した、Serverのプロセス側からジョブに注入するオブジェクトのクラスを実装します。今回はそのままJobConfigurationとし、コンストラクタで設定値を積み込むようなものを想定したものにしてみました。

// JobConfiguration.cs 【新規】
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に設定する記述を行います。

// Program.cs 【変更(主に★の箇所)】
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);

      // ★コンテナに型を登録、Activatorに設定
      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側のプロセスから注入された値が反映されていることが分かります。

f:id:kiyokura:20170804012526p:plain

まとめ

HangfireはDIコンテナの利用が想定されていて、メジャーなDIコンテナの実装に対応した拡張も提供されています。(参照:Extensions/IoC Containers

実際に、特に面倒もなくDIコンテナを利用できることがお分かりいただけたのではないでしょうか。

サンプルソースは例によって以下のブランチstep03です。

github.com

これでいったんこのメモのシリーズは終了する予定ですが、この後もHangfireを使っていて調べたこと・ハマったこと等を随時メモしていくと思います。

*1:お仕事で取り組んでる事なので詳細は割愛