5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ASP.NET CoreアプリのDependency Injection処理を別のDIコンテナに委譲する

Last updated at Posted at 2016-11-13

最初に補足

コントローラー以外でもDI連携する方法について完全版も記述したので、そちらも参照してください

やること

ASP.NET CoreではDependency Injectionが標準機能として組み込まれていますが、それでは機能不足な事も多いと思います。

そこで、他のDIコンテナとASP.NET Coreを連携させる方法について記述します。

通常、各種DIコンテナについては、その拡張としてASP.NET Coreとのインテグレーション機能が提供されると思いますが、今回は自作のDependency Resolverを使用して、連携方法の仕組み自体について記述します。

この方法と同じやり方をすることで、インテグレーション機能が提供されていないコンテナについても、ASP.NET Coreとの連携が可能となります。

環境

  • Visual Strudio 2015
  • .NET Core Tooling Preview 2 for Visual Studio 2015
  • Smart.Resolver 1.0.4 (自作のGuice型Dependency Resolver)

前提

ASP.NET Core Web Applicationを作成し、NuGetでUsa.Smart.Resolverを追加しておきます。

手順

IControllerActivator実装

IControllerActivatorの派生クラスとして、以下のような実装を用意します。

なお、IResolverはSmart.ResolverのDependency Resolverインターフェースになります。

SmartResolverControllerActivator.cs
using System;

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;

using Smart.Resolver;

public class SmartResolverControllerActivator : IControllerActivator
{
    private readonly IResolver resolver;

    public SmartResolverControllerActivator(IResolver resolver)
    {
        this.resolver = resolver;
    }

    public object Create(ControllerContext context)
    {
        return resolver.Get(context.ActionDescriptor.ControllerTypeInfo.AsType());
    }

    public void Release(ControllerContext context, object controller)
    {
        (controller as IDisposable)?.Dispose();
    }
}

Startup修正

StartupクラスでStandardResolverをメンバに定義し、ConfigureServices()でSmartResolverControllerActivatorの設定を行います。

Startup.cs
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;

using Smart.Resolver;

public class Startup
{
    private readonly StandardResolver resolver = new StandardResolver();
...
    public void ConfigureServices(IServiceCollection services)
    {
        // 標準のASP.NET設定はここ

        services.AddSingleton<IControllerActivator>(new SmartResolverControllerActivator(resolver));
    }
...
}

IControllerActivatorが設定されると、コントローラーのインスタンスはその実装経由で行われるようになります。

この例では、実際のインスタンスの生成をSmart.ResolverのDependency Resolver実装であるStandardResolverに委譲することで、ASP.NET CoreとSmart.Resolverの連携を実現しています。

サンプル

実際に動くサンプルを以下に用意しました。

このサンプルは、用途毎に複数のDB接続があるようなアプリケーションを想定したものです。

接続を生成する複数のIConnectionFactoryのインスタンスについて、ASP.NET Coreの標準ではサポートされない条件付きバインディングを行っています。

なお、SQLiteとDapperを使用し、実際にデータアクセスまで行っています。

インスタンス生成委譲対象

サンプルで、StandardResolverにインスタンスの生成/管理を委譲する主なクラスについて以下に記述します。

クラス スコープ 概要
CharacterController Prototype CharacterServiceを使用するAPIコントローラー
ItemController Prototype ItemServiceを使用するAPIコントローラー
CharacterService Singleton 名称"Character"のCallbackConnectionFactoryを使用する
ItemService Singleton 名称"Master"のCallbackConnectionFactoryを使用する
IConnectionFactory Singleton * 2 "Character"、"Master"の2つのインスタンスが存在する

Resolver初期化コード

Resolverの初期化コードを以下に抜粋します。

var connectionStringMaster = Configuration.GetConnectionString("Master");
resolver
    .Bind<IConnectionFactory>()
    .ToConstant(new CallbackConnectionFactory(() => new SqliteConnection(connectionStringMaster)))
    .Named("Master");
var connectionStringCharacter = Configuration.GetConnectionString("Character");
resolver
    .Bind<IConnectionFactory>()
    .ToConstant(new CallbackConnectionFactory(() => new SqliteConnection(connectionStringCharacter)))
    .Named("Character");

resolver
    .Bind<MasterService>()
    .ToSelf()
    .InSingletonScope()
    .WithConstructorArgument("connectionFactory", kernel => kernel.Get<IConnectionFactory>("Master"));
resolver
    .Bind<CharacterService>()
    .ToSelf()
    .InSingletonScope()
    .WithConstructorArgument("connectionFactory", kernel => kernel.Get<IConnectionFactory>("Character"));

CallbackConnectionFactoryについては、接続情報の異なる2つのインスタンスを、Named()メソッドにより異なる名称で登録しています。

2つのServiceクラスについては、Singletonスコープとして登録し、インスタンス生成時のコンストラクタ引数connectionFactoryについて、名称指定でResolverから取得して設定するようにしています。

なお、Named()及びWithConstructorArgument()によって条件付きバインディングを行っていますが、名称による条件付きバインディングはNamedAttributeを使う事でも可能です。

また、Smart.Resolverでは明示的に情報を登録しないクラスについてはPrototypeスコープとして扱われるため、コントローラーについてはResolverへの情報登録は不要となっています。

うさコメ

IControllerActivatorの他に、IViewComponentActivatorとかもありますでよ(・ω・)

参考文献

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?