C#のEF Coreでデータ挿入・更新・削除を完全マスター!初心者でもわかるデータベース操作
生徒
「C#でデータベースにデータを保存したり変更したりする方法ってありますか?」
先生
「はい、Entity Framework Core(EF Core)を使えば、データベース操作がとても簡単にできますよ。」
生徒
「データの挿入や更新、削除ってどうやるんですか?」
先生
「それでは、EF Coreを使ったデータベース操作の基本を順番に見ていきましょう!」
1. Entity Framework Core(EF Core)とは?
Entity Framework Core(エンティティ フレームワーク コア)、通称EF Coreは、C#でデータベースを操作するためのツールです。従来はSQL文(データベース専用の言語)を直接書く必要がありましたが、EF Coreを使うとC#のコードだけでデータベース操作ができます。
例えるなら、EF Coreは「翻訳者」のような存在です。あなたがC#で「このデータを保存して」と指示すると、EF Coreがそれをデータベースが理解できる言葉(SQL)に翻訳して実行してくれます。これにより、プログラミング初心者でもデータベース操作が簡単にできるようになります。
2. EF Coreでデータベース操作を始める準備
EF Coreを使ってデータベース操作をするには、まず準備が必要です。ここでは、簡単な例として「学生情報」を管理するデータベースを作ってみましょう。
まず、モデルクラスを作成します。モデルクラスとは、データベースのテーブルに対応するC#のクラスのことです。例えば、学生の名前と年齢を保存するテーブルを作る場合、次のようなクラスを定義します。
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
このクラスは、データベースの「Students」という名前のテーブルに対応します。Idは学生を識別するための番号、Nameは名前、Ageは年齢を表します。
次に、DbContextというクラスを作成します。DbContextは、データベースとの接続を管理し、データの取得や保存を行うための「窓口」のような役割を果たします。
using Microsoft.EntityFrameworkCore;
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("接続文字列をここに記述");
}
}
DbSet<Student>は、Studentテーブルに対する操作を行うためのプロパティです。OnConfiguringメソッドでは、データベースへの接続設定を行います。
3. EF Coreでデータを挿入する方法(Add・SaveChanges)
データベースに新しいデータを追加することを挿入(Insert)と言います。EF Coreでは、とても簡単にデータを挿入できます。
データを挿入する手順は次の通りです。まず、新しいオブジェクト(インスタンス)を作成し、そのオブジェクトをDbContextに追加して、最後に変更を保存します。
using (var context = new SchoolContext())
{
// 新しい学生データを作成
var newStudent = new Student
{
Name = "山田太郎",
Age = 20
};
// データベースに追加
context.Students.Add(newStudent);
// 変更を保存
context.SaveChanges();
Console.WriteLine("データが挿入されました!");
}
このコードでは、まずnew Studentで新しい学生オブジェクトを作成しています。次にcontext.Students.Add(newStudent)でそのオブジェクトをDbContextに追加します。ただし、この時点ではまだデータベースには保存されていません。
最後にcontext.SaveChanges()を呼び出すことで、実際にデータベースにデータが保存されます。SaveChangesは「変更を確定する」という意味です。この呼び出しを忘れると、データは保存されないので注意しましょう。
AddRangeメソッドを使うと便利です。例:context.Students.AddRange(student1, student2, student3);
4. EF Coreでデータを更新する方法(Update・SaveChanges)
既にデータベースに保存されているデータを変更することを更新(Update)と言います。EF Coreでは、データを取得して、その内容を変更し、保存するという流れで更新を行います。
例えば、特定の学生の年齢を変更したい場合は、次のようにします。
using (var context = new SchoolContext())
{
// IDが1の学生を検索
var student = context.Students.FirstOrDefault(s => s.Id == 1);
if (student != null)
{
// 年齢を更新
student.Age = 21;
// 変更を保存
context.SaveChanges();
Console.WriteLine("データが更新されました!");
}
else
{
Console.WriteLine("該当する学生が見つかりませんでした。");
}
}
このコードでは、まずFirstOrDefaultメソッドを使って、IDが1の学生を検索しています。FirstOrDefaultは、条件に合う最初のデータを取得するメソッドで、見つからない場合はnullを返します。
データが見つかったら、そのプロパティ(ここではAge)を直接変更します。EF Coreは自動的にその変更を追跡しているので、SaveChanges()を呼び出すだけでデータベースに反映されます。
明示的にcontext.Students.Update(student)を呼び出すこともできますが、EF Coreが変更を追跡している場合は不要です。ただし、別のコンテキストから取得したオブジェクトを更新する場合は、Updateメソッドを使う必要があります。
5. EF Coreでデータを削除する方法(Remove・SaveChanges)
データベースからデータを削除することを削除(Delete)と言います。EF Coreでは、削除したいデータを取得してから、それを削除するという流れになります。
例えば、特定の学生のデータを削除したい場合は、次のようにします。
using (var context = new SchoolContext())
{
// IDが1の学生を検索
var student = context.Students.FirstOrDefault(s => s.Id == 1);
if (student != null)
{
// データを削除
context.Students.Remove(student);
// 変更を保存
context.SaveChanges();
Console.WriteLine("データが削除されました!");
}
else
{
Console.WriteLine("該当する学生が見つかりませんでした。");
}
}
このコードでは、context.Students.Remove(student)でデータを削除対象としてマークします。そしてSaveChanges()を呼び出すことで、実際にデータベースからデータが削除されます。
複数のデータを一度に削除したい場合は、RemoveRangeメソッドを使います。例えば、年齢が18歳未満の学生を全員削除する場合は次のようにします。
using (var context = new SchoolContext())
{
// 年齢が18歳未満の学生を全て取得
var youngStudents = context.Students.Where(s => s.Age < 18).ToList();
// 複数のデータを削除
context.Students.RemoveRange(youngStudents);
// 変更を保存
context.SaveChanges();
Console.WriteLine($"{youngStudents.Count}件のデータが削除されました!");
}
6. SaveChangesの重要性とトランザクション
SaveChanges()メソッドは、EF Coreでのデータベース操作において非常に重要な役割を果たします。このメソッドを呼び出すまで、Add、Update、Removeなどの操作はメモリ上で追跡されているだけで、実際のデータベースには反映されていません。
SaveChanges()を呼び出すと、EF Coreは追跡していた全ての変更を一つのトランザクションとしてデータベースに送信します。トランザクションとは、複数の操作を「一つのまとまり」として扱う仕組みです。
例えば、銀行の送金を考えてみましょう。AさんからBさんに1万円を送金する場合、「Aさんの口座から1万円を引く」と「Bさんの口座に1万円を足す」という2つの操作が必要です。もし最初の操作だけ成功して2つ目が失敗したら、お金が消えてしまいます。トランザクションを使うと、両方の操作が成功した場合のみ変更が確定され、どちらかが失敗した場合は全てキャンセルされます。
EF Coreでは、SaveChanges()が失敗した場合、その前に行った全ての変更が自動的にロールバック(取り消し)されます。これにより、データベースの整合性が保たれます。
SaveChanges()を呼び出すようにしましょう。これにより、パフォーマンスが向上し、データの整合性も保たれます。
7. 非同期処理でデータベース操作を行う方法
データベース操作は時間がかかることがあります。特に大量のデータを扱う場合や、ネットワーク経由でデータベースにアクセスする場合は、処理が完了するまで待つ必要があります。
この待ち時間の間、プログラムが完全に停止してしまうと、ユーザーインターフェースが固まったり、他の処理ができなくなったりします。これを解決するのが非同期処理です。
EF Coreは、全ての主要なメソッドに非同期版を用意しています。非同期版のメソッドには「Async」という名前が付いています。例えば、SaveChanges()の非同期版はSaveChangesAsync()です。
public async Task InsertStudentAsync()
{
using (var context = new SchoolContext())
{
var newStudent = new Student
{
Name = "佐藤花子",
Age = 19
};
context.Students.Add(newStudent);
// 非同期で保存
await context.SaveChangesAsync();
Console.WriteLine("データが非同期で挿入されました!");
}
}
非同期処理を使う場合は、メソッドにasyncキーワードを付け、非同期メソッドの呼び出しにはawaitキーワードを付けます。これにより、データベース処理の完了を待っている間も、他の処理を実行できるようになります。
現代のアプリケーション開発では、非同期処理が推奨されています。特にWebアプリケーションやモバイルアプリケーションでは、ユーザー体験を向上させるために非常に重要です。
8. エラーハンドリングとデバッグのコツ
データベース操作では、様々なエラーが発生する可能性があります。例えば、ネットワーク接続の問題、データベースの容量不足、データの制約違反などです。これらのエラーに適切に対処することが重要です。
try-catch文を使うと、エラーが発生した場合でもプログラムがクラッシュせずに、適切な処理を行うことができます。try-catch文は、「試してみて、失敗したらこうする」という仕組みです。
using (var context = new SchoolContext())
{
try
{
var newStudent = new Student
{
Name = "鈴木一郎",
Age = 22
};
context.Students.Add(newStudent);
context.SaveChanges();
Console.WriteLine("データが正常に挿入されました。");
}
catch (DbUpdateException ex)
{
Console.WriteLine("データベースの更新中にエラーが発生しました。");
Console.WriteLine($"エラー内容: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine("予期しないエラーが発生しました。");
Console.WriteLine($"エラー内容: {ex.Message}");
}
}
このコードでは、tryブロックの中でデータベース操作を行っています。もしエラーが発生した場合、catchブロックが実行されます。DbUpdateExceptionは、データベース更新時に発生する特定のエラーをキャッチします。その他の予期しないエラーは、一般的なExceptionでキャッチします。
デバッグの際には、Visual Studioのブレークポイント機能を使うと便利です。コードの任意の行にブレークポイントを設定すると、その行でプログラムの実行が一時停止し、変数の値を確認できます。また、EF Coreが実行するSQL文をログに出力することもでき、これにより何が起こっているのかを詳しく確認できます。
9. 実践的な例:学生管理システムの完成形
ここまで学んだ挿入、更新、削除の操作を組み合わせて、簡単な学生管理システムを作ってみましょう。このシステムでは、メニューを表示して、ユーザーが選んだ操作を実行します。
using System;
using System.Linq;
class Program
{
static void Main()
{
while (true)
{
Console.WriteLine("\n=== 学生管理システム ===");
Console.WriteLine("1. 学生を追加");
Console.WriteLine("2. 学生情報を更新");
Console.WriteLine("3. 学生を削除");
Console.WriteLine("4. 全学生を表示");
Console.WriteLine("5. 終了");
Console.Write("選択してください: ");
var choice = Console.ReadLine();
using (var context = new SchoolContext())
{
switch (choice)
{
case "1":
Console.Write("名前: ");
var name = Console.ReadLine();
Console.Write("年齢: ");
var age = int.Parse(Console.ReadLine());
var newStudent = new Student { Name = name, Age = age };
context.Students.Add(newStudent);
context.SaveChanges();
Console.WriteLine("学生が追加されました。");
break;
case "2":
Console.Write("更新する学生のID: ");
var updateId = int.Parse(Console.ReadLine());
var studentToUpdate = context.Students.FirstOrDefault(s => s.Id == updateId);
if (studentToUpdate != null)
{
Console.Write("新しい年齢: ");
studentToUpdate.Age = int.Parse(Console.ReadLine());
context.SaveChanges();
Console.WriteLine("学生情報が更新されました。");
}
else
{
Console.WriteLine("学生が見つかりませんでした。");
}
break;
case "3":
Console.Write("削除する学生のID: ");
var deleteId = int.Parse(Console.ReadLine());
var studentToDelete = context.Students.FirstOrDefault(s => s.Id == deleteId);
if (studentToDelete != null)
{
context.Students.Remove(studentToDelete);
context.SaveChanges();
Console.WriteLine("学生が削除されました。");
}
else
{
Console.WriteLine("学生が見つかりませんでした。");
}
break;
case "4":
var allStudents = context.Students.ToList();
Console.WriteLine("\n--- 全学生一覧 ---");
foreach (var student in allStudents)
{
Console.WriteLine($"ID: {student.Id}, 名前: {student.Name}, 年齢: {student.Age}");
}
break;
case "5":
Console.WriteLine("終了します。");
return;
default:
Console.WriteLine("無効な選択です。");
break;
}
}
}
}
}
このプログラムは、メニュー駆動型のシステムです。ユーザーが番号を入力すると、対応する操作が実行されます。while (true)により、ユーザーが「5」を選んで終了するまで、メニューが繰り返し表示されます。
各操作でusing (var context = new SchoolContext())を使用していますが、これは処理が終わったら自動的にデータベース接続を閉じるためです。usingステートメントを使うと、リソースの管理が自動的に行われ、メモリリークを防ぐことができます。
10. パフォーマンスを向上させるテクニック
EF Coreを使う際、パフォーマンスを意識することも重要です。特に大量のデータを扱う場合、適切な方法を使わないと処理が非常に遅くなることがあります。
変更追跡を無効にする:データを読み取るだけで更新しない場合、変更追跡を無効にすることでパフォーマンスが向上します。AsNoTracking()メソッドを使います。
using (var context = new SchoolContext())
{
// 読み取り専用のクエリ
var students = context.Students
.AsNoTracking()
.Where(s => s.Age >= 20)
.ToList();
foreach (var student in students)
{
Console.WriteLine($"{student.Name} - {student.Age}歳");
}
}
一括操作を活用する:複数のデータを挿入する場合、一つずつAddを呼び出すより、AddRangeを使った方が効率的です。
必要なデータだけを取得する:全てのプロパティが必要ない場合は、Selectメソッドを使って必要なデータだけを取得しましょう。これにより、ネットワーク通信量が減り、処理速度が向上します。
適切なインデックスを設定する:データベース側で検索によく使うカラムにインデックスを設定すると、検索速度が大幅に向上します。EF Coreでは、Fluent APIを使ってインデックスを設定できます。
これらのテクニックを組み合わせることで、より高速で効率的なアプリケーションを作ることができます。ただし、最初から完璧を目指す必要はありません。まずは基本的な操作を理解し、必要に応じて最適化していくのが良いでしょう。