.NET Core 3.1 の Windows Forms アプリで、構成情報を取得する方法、さらに DI 機能を追加して、取得した構成情報と EF Core で利用するコンテキストクラスを DI する方法を書きます。
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 アプリのプロジェクトを作成します。
ターゲットフレームワークを、この記事を書いた時点での最新 .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)...] を開いて以下のパッケージをインストールします。
-
Microsoft.EntityFrameworkCore.SqlServer
-
Microsoft.EntityFrameworkCore.Tools
-
Microsoft.Extensions.Configuration.Json
-
Microsoft.Extensions.DependencyInjection
下の画像がインストールした結果です。各パッケージのバージョンはこの記事を書いた時点での最新です。プロジェクトのターゲットフレームワークは .NET Core 3.1 なのですが、それに合わせる必要はなかったです。
ちなみに、上のリストの 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; ..."
}
}
作成したらそのプロパティの中の「出力ディレクトリにコピー」を「常にコピーする」または「新しい場合はコピーする」に設定するのを忘れないようにしてください。接続文字列のバックスラッシュ \ は \\ にエスケープする必要があるので注意してください。
(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();
// IConfiguration を DI コンテナに登録
services.AddSingleton(config);
IConfiguration を 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;
}
}
}