Sitecore標準のDI機能を使用する

samatsu 5/1/2017 2294 N/A Sitecore Programming

Sitecore 8.2 では、標準の状態で Microsoft.Extensions.DependencyInjection を使用した依存性の注入 (Dependency Injection) を行えるようになっています。 Microsoft.Extensions.DependencyInjection は.NET CoreのDIのフレームワークを提供するもので、ASP.NET Core でもDIの実装に使用されています。

このフレームワークの機能をSitecore 8.2では標準で利用することができるようになっています。SitecoreでDIの実装を行うには、NInjectなど、一般的なDIの実装を可能にするフレームワークを使用することができますが、今回は、Sitecore 8.2 で初めから標準の機能として利用できるように用意されている Microsoft.Extensions.DependencyInjection の機能を使用してDIの実装を行ってみます。

今回は、サンプルとして、コントローラーレンダリングで使用するサービスクラスを注入できるようにDIの設定を行います。

検証した Sitecoreのバージョンは 8.2 Update-3, 使用した Microsoft.Extensions.DependencyInjectionのバージョンは、1.1.0です。

1. NuGetパッケージの追加

Sitecore MVC開発用にセットアップしたプロジェクトで、 Microsoft.Extensions.DependencyInjection.Abstractions を検索してインストールします。今回は、 Microsoft.Extensions.DependencyInjection は必要ありません。Sitecoreが内部的に Microsoft.Extensions.DependencyInjection のアセンブリを使用していますが、カスタムプログラム側では、機能を使用するだけですので、Microsoft.Extensions.DependencyInjection.Abstractions を参照に追加するだけで十分です。

Sitecore 8.2 で使用しているdllと同じバージョンを使用したい場合は、 上手の通り、 1.0.0 のバージョンを指定します。執筆時点で、最新のバージョンは 1.1.0 だったことと、バージョンが変わっていても問題なく動作しそうであったため、実際には 1.1.0 を使用して動作確認をしました。1.1.0を使用する場合は、Web.configのassemblyBindingの設定で、1.1.0のバージョンを使うようにバインドの設定を忘れずに追加してください。

2.サンプルコードの作成

依存性の注入を行うために、シンプルなコントローラーレンダリングを作成しました。TitleAndSummaryController は、引数に IRenderingItemResolver を取るコンストラクターのみ定義してあり、空のコンストラクターは定義されていないことに注意してください。

Sitecoreの標準の機能では、空のコンストラクターが定義されていないとエラーが発生しますが、今回はDependencyInjection の機能を使用してコントローラーをインスタンス化します。

namespace SitecoreSample.Areas.DI.Controllers
{
    public class TitleAndSummaryController : Controller
    {
        private IRenderingItemResolver _resolver = null;
        public TitleAndSummaryController(IRenderingItemResolver resolver)
        {
            _resolver = resolver;
        }

        public ActionResult Index()
        {
            Item current = _resolver.ContextItem;
            var model = new TitleAndDescriptionModel
            {
                Title = new HtmlString(FieldRenderer.Render(current, "Title")),
                Description = new HtmlString(FieldRenderer.Render(current, "Text"))
            };
            return View(model);
        }
    }
}

コントローラーの中で、使用されているモデルクラスの定義は次のようになります。非常に単純なクラスです。

namespace SitecoreSample.Areas.DI.Models
{
    public class TitleAndDescriptionModel 
    {
        public HtmlString Title { get; set; }
        public HtmlString Description { get; set; }
    }
}

TitleAndSummaryControllerのコンストラクターに渡す IRenderingItemResolver の定義と、このインタフェースを実装する RenderingItemResolver の定義は次の通りです。こちらも単純に RenderingContext を使用してデータソースのアイテム(もしくはコンテキストアイテム) を公開するプロパティを定義しています。RenderingItemResolverクラスのコンストラクターは、 RenderingContext を引数にとるように宣言されていることに注意してください。

namespace SitecoreSample.Areas.DI.Services
{
    public interface IRenderingItemResolver
    {
        Item ContextItem { get; }
    }

    public class RenderingItemResolver : IRenderingItemResolver
    {
        protected RenderingContext Context { get; private set; }
        public RenderingItemResolver(RenderingContext renderingContext)
        {
            this.Context = renderingContext;
        }
        public Item ContextItem
        {
            get
            {
                return Context.Rendering.Item;
            }
        }
    }
}

これで、必要なクラスの準備はほとんど完了です。

3.コンフィギュレーターの登録

コントローラーやサービスクラスを作成したので、あとは、それらのクラスを作成するときに必要な、DIの構成をするだけです。Sitecoreでは、 IServiceConfiguratorインタフェースを実装するクラスで、DIの設定を行います。

今回は、下のように SampleConfigurator クラスを作成して、 IRenderintItemResolver インタフェースを実装する RenderingItemResolver の登録を行っています。また、RenderingItemResolverのコンストラクタで必要な RenderingContext の登録も、ラムダ式を使用したファクトリメソッドを使って行っています。最後に、TitleAndSummaryControllerが DIの機能を使用してインスタンス化されるように、TitleAndSummaryControllerの登録も行っています。IServiceCollectionに登録するときに使用できるスコープは、 Transient, Scope, Singleton です。それぞれのスコープの詳細は、本記事の末尾に紹介されているページからご確認ください。

namespace SitecoreSample.Areas.DI.Infrastructure
{
    public class SampleConfigurator : IServicesConfigurator
    {
        public void Configure(IServiceCollection serviceCollection)
        {
            serviceCollection.AddTransient<RenderingContext>(x => RenderingContext.Current);
            serviceCollection.AddTransient<IRenderingItemResolver, RenderingItemResolver>();
            serviceCollection.AddTransient<TitleAndSummaryController>(); 
        }
    }
}

コントローラーを登録するのが面倒な場合は、リフレクションを使用して、アセンブリファイルに登録されているコントローラーを抽出して、DIの対象とするようなコードを記述することもできます。

これで最後です、Includeファイルのパッチファイル用に、MyCustomServiceConfigurator.config という名前で パッチファイルを作成し、次のように設定をパッチする xml を記述しました。

<configuration>
  <sitecore>
    <services>
      <configurator type="SitecoreSample.Areas.DI.Infrastructure.SampleConfigurator, SitecoreSample"></configurator>
    </services>
  </sitecore>
</configuration>

プログラムをビルドし、必要なファイルを配置してコンポーネントがバインドされているページにアクセスしてコンテンツが表示されれば成功です。

もし、動作させたときに、次のエラーメッセージが表示された場合は、

'ファイルまたはアセンブリ 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'、またはその依存関係の 1 つが読み込めませんでした。見つかったアセンブリのマニフェスト定義はアセンブリ参照に一致しません。 (HRESULT からの例外:0x80131040)

アセンブリバインディングの設定を Sitecoreの Web.config に忘れずに追加してください。例えば、1.1.0を使った場合は、次の設定を追加してあげてください。リダイレクトするバージョンの名前は適宜変更する必要があります。

 <dependentAssembly>
    <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
 </dependentAssembly>

 

4. 参考情報

SitecoreのドキュメントサイトにもSitecoreの標準の機能を使用してMicrosoft.Extensions.DependencyInjectionを使用したDIの設定を行う方法が記載されています。

Dependency injection
https://doc.sitecore.net/sitecore_experience_platform/developing/developing_with_sitecore/dependency_injection

関連するMicrosoftのドキュメントのリンクも掲載します。

Introduction to Dependency Injection in ASP.NET Core
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

Essential .NET - .NET Core による依存関係の挿入
https://msdn.microsoft.com/ja-jp/magazine/mt707534.aspx