WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

WinForms で構成情報とコンテキストの DI (CORE)

by WebSurfer 30. March 2021 15:00

.NET Core 3.1 の Windows Forms アプリで、構成情報を取得する方法、さらに DI 機能を追加して、取得した構成情報と EF Core で利用するコンテキストクラスを DI する方法を書きます。

DataGridView に結果を表示

ASP.NET Core アプリのプロジェクトを Visual Studio 2019 のテンプレートを使って作成すると、appsettings.json などの構成ファイルが自動的に生成されてプロジェクトに含まれます。さらに、構成ファイルから情報を読み込んで IConfiguration オブジェクトが生成され、構成情報を取得できるようになります。(詳しくは Microsoft のドキュメント「ASP.NET Core の構成」参照)

また、作成したプロジェクトには DI 機能も自動的に組み込まれ、生成された IConfiguration オブジェクトを DI コンテナに登録し、必要に応じて Controller や Page のコンストラクタ経由で DI できる機能が実装されます。

DI コンテナには ILogger, UserManager, EF Core で使用するコンテキストクラスなども登録でき、これらも必要に応じて Controller や Page のコンストラクタ経由で DI できます。

Windows Forms やコンソールアプリの場合はそれらの機能は Visual Studio 2019 のテンプレートを使っても実装されず、自力でコードを書いて実装する必要があります。

.NET Core 3.1 の Windows Forms アプリで appsettings.json ファイルから IConfiguration オブジェクトを作って構成情報を取得する方法、DI機能を実装して IConfiguration オブジェクトとコンテキストクラスを DI する方法を以下に書きます。

(1) プロジェクトの作成

Visual Studio 2019 のテンプレートを利用して、ターゲットフレームワーク .NET Core 3.1 で Windows Forms アプリのプロジェクトを作成します。

VS2019 のテンプレート

ターゲットフレームワークを、この記事を書いた時点での最新 .NET 5.0 ではなく .NET Core 3.1 としたのは、3.1 が Long Term Support (LTS) 版であること、5.0 でサポートされた新機能を使用しなくても可能なことを確認するためです。

(2) NuGet パッケージのインストール

Visual Studio 2019 の[ツール(T)]⇒[NuGet パッケージマネージャ(N)]⇒[ソリューションの NuGet パッケージの管理(N)...] を開いて以下のパッケージをインストールします。

  1. Microsoft.EntityFrameworkCore.SqlServer
  2. Microsoft.EntityFrameworkCore.Tools
  3. Microsoft.Extensions.Configuration.Json
  4. Microsoft.Extensions.DependencyInjection

下の画像がインストールした結果です。各パッケージのバージョンはこの記事を書いた時点での最新です。プロジェクトのターゲットフレームワークは .NET Core 3.1 なのですが、それに合わせる必要はなかったです。

NuGet パッケージのインストール

ちなみに、上のリストの 1 は EF Core を利用して SQL Server にアクセスして処理を行うための機能、2 は Visual Studio の NuGet Pakage Manager Console で Add-Migration, Scaffold-DbContext などのコマンドを利用できるようにするための機能、3 は appsettings.json などの構成ファイルから情報を取得するための機能、4 は DI 機能の実装のために必要です。

(3) appsettings.json の作成

ソリューションエクスプローラーを操作してプロジェクトに json ファイルを追加します。ファイル名は任意ですが、この記事では ASP.NET アプリに合わせて "appsettings.json" にしました。内容はこの記事では以下の通り接続文字列のみとしましたが、他に任意の情報を含めることができます。

{
  "ConnectionStrings": {
    "NorthwindConnection": "Data Source=(local)\\sqlexpress; ..."
  }
}

作成したらそのプロパティの中の「出力ディレクトリにコピー」を「常にコピーする」または「新しい場合はコピーする」に設定するのを忘れないようにしてください。接続文字列のバックスラッシュ \ は \\ にエスケープする必要があるので注意してください。

appsettings.json のプロパティ設定

(4) 構成情報が取得できることを確認

上記ステップ (1) で作成した Windows Forms アプリの Form1 のコンストラクタに以下のコードを追加して、変数 connString に appsettings.json に設定した接続文字列が取得できることを確認します。

using System.Windows.Forms;
using System.IO;
using WinFormsCore3App1.Contexts;

// NuGet packages:
// Microsoft.Extensions.Configuration.Json
// Microsoft.Extensions.DependencyInjection
// Microsoft.EntityFrameworkCore.SqlServer
// Microsoft.EntityFrameworkCore.Tools

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;

namespace WinFormsCore3App1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Configuration の生成
            // appsettings.json に接続文字列が含まれている
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false);
            IConfiguration config = builder.Build();

            // appsettings.json から取得した接続文字列
            string connString = 
                config.GetConnectionString("NorthwindConnection");
        }
    }
}

(5) DI 機能の実装

DI 機能の実装と IConfiguration オブジェクトの DI コンテナへの登録は以下の 2 行を上のステップ (4) の Form1 のコンストラクタに追加することで可能です。

// DI コンテナ
IServiceCollection services = new ServiceCollection();

// Configuration を DI コンテナに登録
services.AddSingleton(config);

Configuration を DI できるようにするにはさらなるコードの追加が必要です。 それは下にコンテキストクラスの DI 方法と共に書きます。

(6) コンテキストクラスの生成

この記事では Microsoft のサンプルデータベース Northwind の Products, Categories, Suppliers テーブルからリバースエンジニアリングでコンテキストクラスとエンティティクラスを生成して使います。

詳しくは先の記事「スキャフォールディング機能 (CORE)」のステップ (1) を見てください。

上の appsettings.json と違ってバックスラッシュ \ はエスケープする必要はないところに注意してください。エスケープして \\ とし��りするとエラーになります。

成功するとコンテキストクラス NorthwindContext.cs と各テーブルのエンティティクラス Product.cs, Category.cs, Supplier.cs が指定したフォルダに生成されます。

NorthwindContext.cs ファイルの NorthwindContext クラスの引数を持たないコンストラクタと OnConfiguring メソッドはコメントアウトしてください。

(7) ProductService クラスの作成

SQL Server から EF Core を利用してデータを取得するクラスを作成します。ソリューションエクスプローラーでクラスファイルを ProductService.cs という名前で追加し、以下のコードを実装します。

コードの説明はコメントに書きましたのでそれを見てください。

using Microsoft.Extensions.Configuration;
using WinFormsCore3App1.Contexts;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

namespace WinFormsCore3App1
{
    public class ProductService
    {
        private readonly IConfiguration _configuration;
        private readonly NorthwindContext _context;

        // コンストラクタの引数経由で Configuration と
        // NorthwindContext のインスタンスを DI する
        public ProductService(IConfiguration configuration,
                              NorthwindContext context)
        {
            this._configuration = configuration;
            this._context = context;
        }

        // DI された Configuration から接続文字列が取得できる
        // ことを確認するための検証用メソッド
        public string GetConnectionString()
        {
            return _configuration
                   .GetConnectionString("NorthwindConnection");
        }

        // DI されたコンテキスト NorthwindContext を使ってSQL
        // Server の Northwind データベースからデータを取得し
        // List<ProductItem> 型のオブジェクトとして返す。
        // それを DataGridView に表示
        public async Task<List<ProductItem>> GetListAsync()
        {
            var list = from p in _context.Products
                       join s in _context.Suppliers
                       on p.SupplierId equals s.SupplierId
                       join c in _context.Categories
                       on p.CategoryId equals c.CategoryId
                       select new ProductItem 
                       { 
                           ProductId = p.ProductId,
                           ProductName = p.ProductName,
                           Supplier = s.CompanyName,
                           Category = c.CategoryName,
                           UnitPrice = p.UnitPrice.Value
                       };

            return await list.ToListAsync();
        }
    }

    // DataGridView に渡すデータを格納する Data Transfer
    // Object クラスの定義
    public class ProductItem
    {
        public int ProductId { get; set; }

        public string ProductName { get; set; }

        public string Supplier { get; set; }

        public string Category { get; set; }

        public decimal UnitPrice { get; set; }

    }
}

(8) Form1 クラスの完成

Form1 のコンストラクタで DI コンテナに Configuration, NorthwindContext, ProductService を登録し、ServiceProvider から ProductService のインスタンスを生成する際 Configuration と NorthwindContext のインスタンスはコンストラクタ経由 DI されるように設定します。

上記ステップ (4) のコードを含めた完全なコードは以下の通りです。実行した結果がこの記事の一番上にある画像です。

using System.Windows.Forms;
using System.IO;

// NuGet packages:
// Microsoft.Extensions.Configuration.Json
// Microsoft.Extensions.DependencyInjection
// Microsoft.EntityFrameworkCore.SqlServer
// Microsoft.EntityFrameworkCore.Tools

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WinFormsCore3App1.Contexts;
using Microsoft.EntityFrameworkCore;

namespace WinFormsCore3App1
{
    public partial class Form1 : Form
    {
        // ProductService は SQL Server から EF Core を利用
        // してデータを取得するクラス
        private readonly ProductService productService;

        public Form1()
        {
            InitializeComponent();

            // Configuration の生成
            // appsettings.json に接続文字列が含まれている
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false);
            IConfiguration config = builder.Build();

            // appsettings.json から取得した接続文字列
            string connString = 
                config.GetConnectionString("NorthwindConnection");

            // DI コンテナ
            IServiceCollection services = new ServiceCollection();

            // Configuration を DI コンテナに登録
            services.AddSingleton(config);

            // NorthwindContext はリバースエンジニアリングで生成
            // したコンテキストクラス。それを DI コンテナに登録
            services.AddDbContext<NorthwindContext>(options =>
                options.UseSqlServer(connString));

            // ProductService を DI コンテナに登録
            services.AddSingleton<ProductService>();

            // ServiceProvider を生成
            var provider = services.BuildServiceProvider();

            // ServiceProvider から ProductService のインスタンス
            // を生成。その際、Configuration と NorthwindContext
            // のインスタンスはコンストラクタ DI される
            productService = 
                provider.GetRequiredService<ProductService>();

            // DataGridView と BindingSource はデザイン画面で
            // ツールボックスから Form にドラッグ&ドロップ
            dataGridView1.DataSource = bindingSource1;
        }

        private async void Form1_Load(object sender, System.EventArgs e)
        {
            // ProductService に DI した Configuration から接続
            // 文字列を取得できることの確認用
            var northwind = productService.GetConnectionString();

            // EF Core を使って Northwind の Products テーブルか
            // ら List<T> 型のデータを取得し DataGridView に表示 
            var list = await productService.GetListAsync();
            bindingSource1.DataSource = list;
        }
    }
}

Tags: , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2021  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar