KatsuYuzuのブログ

.NET の開発をメインとした日記です。

ASP.NET MVC 5 で DI する - その2「生成の一元化」 #aspnetjp

前回、DI をするための基本的なことを説明しました。

今回は DI コンテナによる生成の一元化について説明します。

DI コンテナによる生成の一元化

f:id:KatsuYuzu:20141109235633p:plain:w400
コードは前回からの続きで、今回は Repository で必要になるであろう DB 接続も DI で注入するシナリオで進めます。DB 接続は SqlConnection を利用します。
DB 接続は、これまでに示した簡易なクラスとは違い、コンストラクターで接続文字列を要求しています。このまま Repository に対して SqlConnection を DI した場合、下記の例外が発生します。
f:id:KatsuYuzu:20141112005605p:plain:w400

例外の詳細: System.InvalidOperationException: The type String cannot be constructed. You must configure the container to supply this value.

この問題は、型の登録時に InjectionFactory を用いて生成方法を与えることで解決できます。

var connectionString =
    ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

container.RegisterType<SqlConnection>(
    new PerRequestLifetimeManager(),
    new InjectionFactory(_ => new SqlConnection(connectionString)));

合わせて、確認のために Repository を下記のように変更しました。

public class SampleRepository : ISampleRepository
{
    private readonly SqlConnection connection;

    public SampleRepository(SqlConnection connection)
    {
        this.connection = connection;
        Debug.WriteLine("SampleRepository: Constructor");
        Debug.WriteLine(connection);
    }
}

実行すると、SqlConnection が生成されていることが確認できます。
f:id:KatsuYuzu:20141112004753p:plain:w400

実用例

ここからは DI の説明ではないのですが、せっかくなので少し実用例を紹介します。
@neuecc さんの下記の記事で、SQL のロギングのための MiniProfiler というライブラリが紹介されています。

ここでは記事に倣って ProfiledDbConnection を利用するようにしてみます。

// DbConnection に対して登録しているのがミソで、
// SqlConnection を OracleConnection に変更しても大丈夫です。
// (もちろん SQL 文やストアドは修正が必要でしょう)

// TraceDbProfiler は @neuecc さんの記事を参照
// NLog ではなく Debug.WriteLine を使うように改造して利用

container.RegisterType<DbConnection>(
    new PerRequestLifetimeManager(),
    new InjectionFactory(_ =>
        new ProfiledDbConnection(
            new SqlConnection(connectionString),
            new TraceDbProfiler())));
public class SampleRepository : ISampleRepository
{
    public SampleRepository(DbConnection connection)
    {
        // Dapper ライブラリの拡張メソッドを利用
        var result = connection.ExecuteScalar<string>(
            "SELECT 'Hello, world!'");
        Debug.WriteLine("SampleRepository: " + result);
    }
}

f:id:KatsuYuzu:20141112013524p:plain
これまで通りの手順で、難しいこともなく実現できました。さらに、Repository 自体も ProfiledDbConnection や SQL Server、Oracle などといった DB 接続に依存していません。

まとめ

型の登録時に InjectionFactory で生成方法を与えるだけです。使わない手はないですね!