Entity Framework Core を使用して SQL Server にアクセスする準備を行う (データベースファースト)

Siden oppdatert :
ページ作成日 :

現在表示しているページは選択中の表示言語には対応しておりません。

動作確認環境

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 8
Entity Framework Core
  • Entity Framework Core 8.0
SQL Server
  • SQL Server 2022

※上記は検証環境ですがほかのバージョンでも動作する可能性はあります。

はじめに

Entity Framework Core は O/R マッパーのライブラリであり、 データベースにアクセスする際に SQL 文を直接使用せずコードで定義されたモデル(クラス)を介してデータベースのレコードにアクセスできます。 これにより以下のようなメリットがあります。

  • SQL 文を直接記述しないので SQL インジェクションなどのセキュリティリスクを減らす
  • SQL 文は文字列であるため構文をミスしてもビルドエラーのチェック対象にならないがモデルはプログラム構文なのでビルド時にミスをチェックできる

Entity Framework Core ではこれらのモデルやデータベースへ接続するコードを既存のデータベースから自動的に生成したり、 逆にコードを手動で作成してからデータベースを自動で生成する方法が備わっています。

前者は「データベースファースト」と呼ばれ後者は「コードファースト」と呼ばれます。 ER 図のような設計図からコードやデータベースを生成する「モデルファースト」もありますが Entity Framework Core ではあまり使われていません。

今回はデータベースがすでにあるという前提でコードを生成する「データベースファースト」のパターンを使用します。

SQL Server のセットアップ

本 Tips では SQL Server のデータベースにアクセスするため事前に SQL Server をセットアップしておいてください。 開発環境の PC、またはネットワーク経由でほかの PC にセットアップする形でも構いません。 開発環境から SQL Server に接続できれば OK です。 本 Tips では別環境に SQL Server をインストールしています。

SQL Server のセットアップ手順については冗長になるため割愛します。 以下のページに SQL Server 関連の Tips を掲載しているのでセットアップの手順を知りたい方は参照してください。

テーブルの作成

今回はサンプルとして以下のデータベースとテーブルを作成します。

  • データベース名 : TestDatabase
  • テーブル名 : User
  • テーブルの列 : [ID], [Name], [Password], [Age], [Email], [Birthday], [UpdateDateTime]

どのような方法で作成しても問題ありませんが、面倒であれば以下の SQL を SQL Server に対して実行して生成してください。

以下はデータベース作成 SQL ですが、データベースの作成についてはバージョンなどによってパスなどが変わるため SQL ではなく GUI やコマンドで作ったほうが確実かもしれません。

USE [master]
GO
CREATE DATABASE [TestDatabase]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'TestDatabase', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\TestDatabase.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'TestDatabase_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\TestDatabase_log.ldf' , SIZE = 8192KB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
 WITH CATALOG_COLLATION = DATABASE_DEFAULT, LEDGER = OFF
GO
ALTER DATABASE [TestDatabase] SET COMPATIBILITY_LEVEL = 160
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [TestDatabase].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO
ALTER DATABASE [TestDatabase] SET ANSI_NULL_DEFAULT OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_NULLS OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_PADDING OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_WARNINGS OFF 
GO
ALTER DATABASE [TestDatabase] SET ARITHABORT OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_CLOSE OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_SHRINK OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_UPDATE_STATISTICS ON 
GO
ALTER DATABASE [TestDatabase] SET CURSOR_CLOSE_ON_COMMIT OFF 
GO
ALTER DATABASE [TestDatabase] SET CURSOR_DEFAULT  GLOBAL 
GO
ALTER DATABASE [TestDatabase] SET CONCAT_NULL_YIELDS_NULL OFF 
GO
ALTER DATABASE [TestDatabase] SET NUMERIC_ROUNDABORT OFF 
GO
ALTER DATABASE [TestDatabase] SET QUOTED_IDENTIFIER OFF 
GO
ALTER DATABASE [TestDatabase] SET RECURSIVE_TRIGGERS OFF 
GO
ALTER DATABASE [TestDatabase] SET  DISABLE_BROKER 
GO
ALTER DATABASE [TestDatabase] SET AUTO_UPDATE_STATISTICS_ASYNC OFF 
GO
ALTER DATABASE [TestDatabase] SET DATE_CORRELATION_OPTIMIZATION OFF 
GO
ALTER DATABASE [TestDatabase] SET TRUSTWORTHY OFF 
GO
ALTER DATABASE [TestDatabase] SET ALLOW_SNAPSHOT_ISOLATION OFF 
GO
ALTER DATABASE [TestDatabase] SET PARAMETERIZATION SIMPLE 
GO
ALTER DATABASE [TestDatabase] SET READ_COMMITTED_SNAPSHOT OFF 
GO
ALTER DATABASE [TestDatabase] SET HONOR_BROKER_PRIORITY OFF 
GO
ALTER DATABASE [TestDatabase] SET RECOVERY FULL 
GO
ALTER DATABASE [TestDatabase] SET  MULTI_USER 
GO
ALTER DATABASE [TestDatabase] SET PAGE_VERIFY CHECKSUM  
GO
ALTER DATABASE [TestDatabase] SET DB_CHAINING OFF 
GO
ALTER DATABASE [TestDatabase] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF ) 
GO
ALTER DATABASE [TestDatabase] SET TARGET_RECOVERY_TIME = 60 SECONDS 
GO
ALTER DATABASE [TestDatabase] SET DELAYED_DURABILITY = DISABLED 
GO
ALTER DATABASE [TestDatabase] SET ACCELERATED_DATABASE_RECOVERY = OFF  
GO
EXEC sys.sp_db_vardecimal_storage_format N'TestDatabase', N'ON'
GO
ALTER DATABASE [TestDatabase] SET QUERY_STORE = ON
GO
ALTER DATABASE [TestDatabase] SET QUERY_STORE (OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30), DATA_FLUSH_INTERVAL_SECONDS = 900, INTERVAL_LENGTH_MINUTES = 60, MAX_STORAGE_SIZE_MB = 1000, QUERY_CAPTURE_MODE = AUTO, SIZE_BASED_CLEANUP_MODE = AUTO, MAX_PLANS_PER_QUERY = 200, WAIT_STATS_CAPTURE_MODE = ON)
GO
ALTER DATABASE [TestDatabase] SET  READ_WRITE 
GO

テーブル作成 SQL です。

USE [TestDatabase]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[User](
  [ID] [int] NOT NULL,
  [Name] [nvarchar](20) NOT NULL,
  [Password] [nvarchar](20) NOT NULL,
  [Age] [int] NULL,
  [Email] [nvarchar](200) NULL,
  [Birthday] [date] NULL,
  [UpdateDateTime] [datetime2](7) NULL,
  CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

レコード追加 SQL です。

USE [TestDatabase]
GO
INSERT [dbo].[User] ([ID], [Name], [Password], [Age], [Email], [Birthday], [UpdateDateTime]) VALUES (1, N'氏名1', N'aaaa', 20, N'aaaa@example.com', CAST(N'2020-04-01' AS Date), CAST(N'2021-03-14T00:00:00.0000000' AS DateTime2))
GO
INSERT [dbo].[User] ([ID], [Name], [Password], [Age], [Email], [Birthday], [UpdateDateTime]) VALUES (2, N'氏名2', N'bbbb', 30, N'bbbb@example.com', CAST(N'2010-04-01' AS Date), CAST(N'2021-03-14T00:00:00.0000000' AS DateTime2))
GO

Visual Studio のセットアップ

こちらもすでにセットアップしているものとします。 セットアップの手順などを知りたい場合は以下のページにまとめています。

プロジェクトの作成

Entity Framework Core は特定の実行環境に依存しているわけではないので多くのプロジェクトで使用することができます。 今回はシンプルなコンソールアプリケーションの環境で Entity Framework Core を使用してみます。

新しいプロジェクトで「コンソール アプリ」を選択します。

プロジェクトを作成した状態です。プロジェクト名などはなんでも構いません。

Entity Framework Core のパッケージを取得する

NuGet で Entity Framework Core を使用するためのパッケージを取得します。

依存関係を右クリックして「NuGet パッケージの管理」を選択します。

タブから「参照」を選択している状態で検索欄に EntityFrameworkCore と入力します。すると一覧に Entity Framework Core 関連のパッケージが表示されます。

この中から今回は SQL Server を使用するので以下のパッケージをインストールします。

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.EntityFrameworkCore.SqlServer

ここでは Microsoft.EntityFrameworkCore を例にインストールしています。他の2つも同様にインストールしてください。

インストール対象を選択して インストール ボタンをクリックします。バージョンは最新の安定板を選択します。

ダイアログは基本的に OK をクリックして問題ありません。

他の2つもインストールしてください。

パッケージは以下のようになっていると思います。

データベースのテーブル構成からモデル (コード) を作成する

モデルなどのコードを自動生成させるにはまずプロジェクトをビルドしてエラーがないことを確認します。 エラーがあるとモデルの作成に失敗します。すでにエラーがないことを確認済みであるのであればビルトする必要はありません。

Visual Studio から「パッケージ マネージャー コンソール」を開きます。 ない場合はメニューの「ツール」「NuGet パッケージ マネージャー」「パッケージ マネージャー コンソール」から開けます。

以下のようなウィンドウが表示されるので、右上の「既定のプロジェクト」がモデルを作成する対象のプロジェクトになっていることを確認します。 (複数プロジェクトを持っている場合に気を付ける必要があります)

入力欄に以下のテキストを入力します。環境によってパラメータが変わりますので、それぞれ以下の説明ともとに適時変えてください。

Scaffold-DbContext -Provider Microsoft.EntityFrameworkCore.SqlServer -Connection "Data Source=<サーバー名>\<インスタンス名>;Database=<データメース名>;user id=<接続ユーザー名>;password=<接続パスワード>;TrustServerCertificate=true" -f -OutputDir "<出力フォルダパス>" -Context "<コンテキストクラス名>" -UseDatabaseNames -DataAnnotations -NoPluralize

入力例

Scaffold-DbContext -Provider Microsoft.EntityFrameworkCore.SqlServer -Connection "Data Source=TestServer;Database=TestDatabase;user id=TestUser;password=pass;TrustServerCertificate=true" -f -OutputDir "Models\Database" -Context "TestDatabaseDbContext" -UseDatabaseNames -DataAnnotations -NoPluralize
パラメータ 説明 パラメータ例
Provider SQL Server なら Microsoft.EntityFrameworkCore.SqlServer で固定です。 Microsoft.EntityFrameworkCore.SqlServer
Connection データベースに接続するための接続文字列です。接続文字列は他のアプリでも共通で使用できるので指定する内容については接続文字列の記法にしたがって記述してください。Windows 認証、SQL Server 認証どちらでも使用可能です。ちなみにモデルを作成するためだけに一時的に使用されるだけですので、この接続文字列についてはアプリ公開後のセキュリティなどを意識する必要はありません。パスワードに記号を入れている場合はエスケープに注意してください。 "Data Source=ServerName\SQLEXPRESS;Database=TestDatabase;user id=UserName;password=**********"
f すでにプログラムがあった場合でも強制的に上書きします。 <なし>
OutputDir コードを出力するフォルダパス。プロジェクトフォルダからの相対パス Models\Database
Context Entity Framework を使用する際のコンテキストクラス名 TestDatabaseDbContext
UseDatabaseNames 指定するとデータベースのテーブル名がそのままクラス名になります。指定しない場合は規則に従ってエンティティクラス名の大文字小文字が調整されます。 <なし>
DataAnnotations 指定すると列の型によって各プロパティに DataAnnotation 属性が自動で付加されます。データベースの型に沿って自動的に入力チェックを行いたい場合に少し便利です。 <なし>
Namespace 生成されるエンティティクラスが所属する名前空間。指定しない場合はフォルダに合わせて名前空間が決定される。 TestNamespace
ContextNamespace 生成される Context が所属する名前空間。指定しない場合はフォルダに合わせて名前空間が決定される。 TestNamespace
NoOnConfiguring コードの中に生の接続文字列を埋め込まないようにします。 <なし>
NoPluralize Context の各テーブル名のプロパティ名が複数形にならないようにします。 <なし>

Enter を押して実行すると以下のようになり、コードが自動生成されます。エラーになる場合はその理由が表示されるのでエラー内容に合わせて対応してください。

User テーブルのモデルコードは以下のようになっています。

using System.ComponentModel.DataAnnotations;

namespace SetupSqlServerDatabaseFirst.Models.Database;

public partial class User
{
  [Key]
  public int ID { get; set; }

  [StringLength(20)]
  public string Name { get; set; } = null!;

  [StringLength(20)]
  public string Password { get; set; } = null!;

  public int? Age { get; set; }

  [StringLength(200)]
  public string? Email { get; set; }

  public DateOnly? Birthday { get; set; }

  public DateTime? UpdateDateTime { get; set; }
}

ちなみに警告が表示されるのは、生成されたコンテキストクラスのコードに接続文字列がそのまま記載されるためです。 可能であれば接続文字列は別なところに保管して実行時に設定するようにすべきですが、今回は動作確認目的なのでそのままにしておきます。

レコードを取得して表示してみる

データベースのレコードにアクセスするためのコードはできたので試しにレコードを取得してコンソールに表示してみます。

Program.cs を開き以下のように修正してください。

// See https://aka.ms/new-console-template for more information
using SetupSqlServerDatabaseFirst.Models.Database;
using System.Text.Json;

Console.WriteLine("Hello, World!");

// データベースコンテキストのインスタンスを生成する
using var dbContext = new TestDatabaseDbContext();

// データベースから User 一覧を取得する
var users = dbContext.User.ToList();

// 取得した User 情報をコンソールに書き出す
foreach (var user in users)
{
  Console.WriteLine(JsonSerializer.Serialize(user));
}

自動生成した DbContext クラスを new で生成します。データベースの接続を自動的に破棄できるように using var で宣言しています。

dbContext には各モデルにアクセスするためのプロパティが生成されているので今回は User プロパティにアクセスすることによって User テーブルのレコードを操作することができます。 発行される SQL は内部的に自動生成されるので意識する必要はありません。

ここでは ToList 拡張メソッドを使用して User テーブルのレコードをすべて取得しています。

後は foreachJsonSerializer.Serialize メソッドを使用して User の情報をコンソールに表示しています。 前述の通り User テーブルの各列はプロパティとして宣言されているので個別に値を取り出すことも可能です。