コンテンツにスキップ

コンソールアプリケーションで DI を利用する⚓︎

概要⚓︎

コンソールアプリケーションでは、通常 Microsoft.Extensions.DependencyInjection を用いた DI を利用できません。 DI を利用するためには、汎用ホストを用いてコンソールアプリケーションを構築しなければなりません。 この実装は定型的なものが多いにも関わらず、デファクトスタンダードとなった OSS ライブラリは現在存在しません。

またコンソールアプリケーションでは、通常コマンドラインから起動パラメーターを設定できるように設計します。 .NET のコンソールアプリケーションプロジェクトでは、起動パラメーターをパースする機能が提供されておらず、何かしらの OSS ライブラリに依存する必要があります。

このサンプルでは、上記の課題を解決するためのコンソールアプリケーションのフレームワークを提供します。

簡易な実装サンプル⚓︎

このサンプルを利用すると、起動パラメーターをバインドするためのパラメータークラスと、アプリケーションの処理本体となるコマンドクラスを実装できます。 以下に実装サンプルを示します。 実装サンプルの全体像は、 サンプルアプリケーションをダウンロード して確認してください。

サンプルアプリケーションにおけるパラメータークラスの実装例
Parameter.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using CommandLine;
using Maris.ConsoleApp.Core;

namespace Maris.Samples.Cli.Commands.GetProductsByUnitPriceRange;

/// <summary>
///  単価の範囲を検索キーにして商品情報を取得するコマンドのパラメーターです。
/// </summary>
[Command("get-by-unit-price-range", typeof(Command), HelpText = "単価の範囲内の商品情報を取得します。")]
internal class Parameter
{
    /// <summary>
    ///  単価の最小値を取得または設定します。
    /// </summary>
    [Option("minimum", Required = false, HelpText = "検索範囲の単価の最小値を指定します。")]
    public decimal? MinimumUnitPrice { get; set; }

    /// <summary>
    ///  単価の最大値を取得または設定します。
    /// </summary>
    [Option("maximum", Required = false, HelpText = "検索範囲の単価の最大値を指定します。")]
    public decimal? MaximumUnitPrice { get; set; }
}
サンプルアプリケーションにおけるコマンドクラスの実装例
Command.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using Maris.ConsoleApp.Core;
using Maris.Samples.ApplicationCore;
using Microsoft.Extensions.Logging;

namespace Maris.Samples.Cli.Commands.GetProductsByUnitPriceRange;

/// <summary>
///  単価の範囲を検索キーにして商品情報を取得する非同期コマンドです。
/// </summary>
internal class Command : AsyncCommand<Parameter>
{
    private readonly ProductApplicationService service;
    private readonly ILogger logger;

    /// <summary>
    ///  <see cref="Command"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="service">商品情報を取り扱うアプリケーションサービス。</param>
    /// <param name="logger">ロガー。</param>
    /// <exception cref="ArgumentNullException">
    ///  <list type="bullet">
    ///   <item><paramref name="service"/> が <see langword="null"/> です。</item>
    ///   <item><paramref name="logger"/> が <see langword="null"/> です。</item>
    ///  </list>
    /// </exception>
    public Command(ProductApplicationService service, ILogger<Command> logger)
    {
        this.service = service ?? throw new ArgumentNullException(nameof(service));
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <summary>
    ///  パラメーターに指定した単価の範囲を検索条件にして商品情報の一覧を取得し、コンソールに出力します。
    /// </summary>
    /// <param name="parameter">パラメーター。</param>
    /// <param name="cancellationToken">キャンセルトークン。</param>
    /// <returns>コマンドの実行結果。</returns>
    protected override async Task<ICommandResult> ExecuteAsync(
        Parameter parameter, CancellationToken cancellationToken)
    {
        var products = await this.service.GetProductsByUnitPriceRangeAsync(
            parameter.MinimumUnitPrice, parameter.MaximumUnitPrice, cancellationToken);
        if (products.Count >= 10)
        {
            this.logger.LogWarning(Events.Over10ProductsFoundInRange, $"単価が {parameter.MinimumUnitPrice} ~ " +
                $"{parameter.MaximumUnitPrice} の商品情報が 10 件以上あります。" +
                $"範囲を絞り込んでください。");
            return CommandResult.CreateWarning(2);
        }

        foreach (var product in products)
        {
            Console.WriteLine($"{product.Id,3} : {product.Name} {product.UnitPrice,7}円");
        }

        return CommandResult.Success;
    }
}
コマンドラインからの実行例
1
Maris.Samples.Cli.exe get-by-unit-price-range --minimum 2000 --maximum 3000

フレームワークを用いたコンソールアプリケーションの開発方法⚓︎

詳細なフレームワークの使い方や開発方法については、 サンプルアプリケーション に付属する README.md を参照してください。

本サンプルで利用する代表的な OSS⚓︎

本サンプルでは以下の OSS ライブラリを使用しています。 他の OSS ライブラリについては、 サンプルアプリケーションをダウンロード して確認してください。

ダウンロード⚓︎

サンプルアプリケーションと詳細な解説は以下からダウンロードできます。