概要
C#とWindows Forms(WinForms)を使用して、基本的なCRUD(作成・読み取り・更新・削除)操作を実装する方法について解説します。DataGridView を活用し、ユーザーが直感的にデータを操作できるように設計されています。
また、一つ「実行」ボタンを1回クリックするだけで、すべてのCRUD処理(作成・更新・削除)が実行される 仕組みを採用しています。
開発環境
- Visual Studio 2022
- .NET Framework 4.8
- SQL Server
機能一覧
- 作成(Create): 新しいファイルを追加
- 読み取り(Read): 既存のファイル一覧を表示
- 更新(Update): 備考欄を編集し、更新
- 削除(Delete): 選択した行を削除
- 実行(Execute): 追加・更新・削除を一括で適用
実装の流れ
1. UIの構築
DataGridView を用いてファイル情報を一覧表示し、ボタンを配置してCRUD操作を実装します。
private void InitializeUI()
{
this.Text = "ファイル管理";
this.Size = new Size(932, 480);
SetupButton(btnExecute, "実行", new Point(712, 37), btnExecute_Click);
SetupButton(btnAddRow, "行追加", new Point(712, 66), btnAddRow_Click);
SetupButton(btnDeleteRow, "行削除", new Point(712, 99), btnDeleteRow_Click);
dataGridView.Location = new Point(32, 37);
dataGridView.Size = new Size(602, 313);
dataGridView.RowHeadersVisible = false;
dataGridView.AllowUserToAddRows = false;
dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView.MultiSelect = false;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
this.Controls.Add(dataGridView);
}
2. データの読み取り(Read)
データベースまたはファイルサービスからデータを取得し、DataGridView に表示します。
private async Task LoadData()
{
var data = await _fileServices.GetFile(string.Empty);
foreach (DataRow row in data.Rows)
{
_dt.Rows.Add(row["FileNo"], row["FileName"], row["remarks"], row["fileData"]);
}
}
3. ファイルの作成(Create)
新しいファイルを追加する機能を実装します。
private void btnAddRow_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "ファイル (*.*)|*.*";
openFileDialog.Title = "ファイル選択";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
byte[] fileData = File.ReadAllBytes(filePath);
string fileName = Path.GetFileName(filePath);
_dt.Rows.Add("", fileName, "", fileData);
dataGridView.Rows[dataGridView.Rows.Count - 1].Selected = true;
}
}
}
- OpenFileDialog を用いて、ユーザーがローカルファイルを選択可能にします。
- File.ReadAllBytes を使ってファイルデータを取得し、DataTable に追加します。
4. ファイルの削除(Delete)
選択した行を削除する処理を実装します。
private void btnDeleteRow_Click(object sender, EventArgs e)
{
if (dataGridView.SelectedRows.Count > 0)
{
dataGridView.Rows.RemoveAt(dataGridView.SelectedRows[0].Index);
}
}
- dataGridView.SelectedRows を確認し、選択された行を削除します。
5. すべての処理を一括実行(Execute)
「実行」ボタンをクリックすることで、作成(Insert)、更新(Update)、削除(Delete)を一括で適用 します。
private async void btnExecute_Click(object sender, EventArgs e)
{
foreach (DataRow row in _dt.Rows)
{
switch (row.RowState)
{
case DataRowState.Added:
await _fileServices.InsertFile(row["fileName"].ToString(), (byte[])row["fileData"], row["remarks"].ToString());
break;
case DataRowState.Modified:
await _fileServices.UpdateFile(int.Parse(row["fileNo"].ToString()), row["remarks"].ToString());
break;
case DataRowState.Deleted:
await _fileServices.DeleteFile(int.Parse(row["fileNo", DataRowVersion.Original].ToString()));
break;
}
}
_dt.Rows.Clear();
await LoadData();
_dt.AcceptChanges();
}
- RowState を判定し、追加(Added)、更新(Modified)、削除(Deleted)を分類。
- 変更があったデータのみ適用するため、効率的に処理。
- LoadData() を再実行し、データを最新の状態に更新。
FileServicesクラスの実装
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using WinFormsDemo.Common;
namespace WinFormsDemo.Services
{
/// <summary>
/// ファイル操作に関連するサービスクラスです。
/// </summary>
public class FileServices
{
/// <summary>
/// ファイル情報を取得します。
/// </summary>
/// <param name="where">SQLのWHERE句に追加する条件</param>
/// <returns>ファイル情報を含むDataTable</returns>
public async Task<DataTable> GetFile(string where)
{
try
{
// DB接続をラップするDBWrapperクラスを使用
using (var dbWrapper = new DBWrapper())
{
StringBuilder sqlCommand = new StringBuilder();
// SQLクエリを作成
sqlCommand.Append("SELECT * FROM FileMgmt ");
if (!string.IsNullOrEmpty(where))
{
sqlCommand.Append(where); // where句を条件に追加
}
// SQLクエリを実行して結果を返す
return await dbWrapper.ExecuteQuery(sqlCommand.ToString());
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// 新しいファイル情報をデータベースに挿入します。
/// </summary>
/// <param name="fileName">ファイル名</param>
/// <param name="fileData">ファイルデータ</param>
/// <param name="remarks">備考</param>
/// <returns>非同期タスク</returns>
public async Task InsertFile(string fileName, byte[] fileData, string remarks)
{
using (var dbWrapper = new DBWrapper())
{
try
{
await dbWrapper.BeginTransaction(); // トランザクション開始
StringBuilder sqlCommand = new StringBuilder();
sqlCommand.Append("INSERT INTO FileMgmt (fileData, fileName, remarks) VALUES (@fileData, @fileName, @remarks)");
// パラメータを辞書で作成
var parameters = new Dictionary<string, object>
{
{"@fileData", fileData},
{"@fileName", fileName},
{"@remarks", remarks},
};
// SQLコマンドを実行
await dbWrapper.ExecuteNonQuery(sqlCommand.ToString(), parameters);
await dbWrapper.CommitTransaction(); // コミット
}
catch (Exception ex)
{
// エラー処理
Console.WriteLine(ex.Message);
await dbWrapper.RollbackTransaction(); // ロールバック
}
}
}
/// <summary>
/// ファイル情報を更新します。
/// </summary>
/// <param name="fileNo">ファイル番号</param>
/// <param name="remarks">新しい備考</param>
/// <returns>非同期タスク</returns>
public async Task UpdateFile(int fileNo, string remarks)
{
using (var dbWrapper = new DBWrapper())
{
try
{
await dbWrapper.BeginTransaction(); // トランザクション開始
StringBuilder sqlCommand = new StringBuilder();
sqlCommand.Append("UPDATE FileMgmt SET remarks = @remarks WHERE fileNo = @fileNo");
// パラメータを辞書で作成
var parameters = new Dictionary<string, object>
{
{"@fileNo", fileNo},
{"@remarks", remarks},
};
// SQLコマンドを実行
await dbWrapper.ExecuteNonQuery(sqlCommand.ToString(), parameters);
await dbWrapper.CommitTransaction(); // コミット
}
catch (Exception ex)
{
// エラー処理
Console.WriteLine(ex.Message);
await dbWrapper.RollbackTransaction(); // ロールバック
}
}
}
/// <summary>
/// 指定したファイル情報を削除します。
/// </summary>
/// <param name="fileNo">削除するファイル番号</param>
/// <returns>非同期タスク</returns>
public async Task DeleteFile(int fileNo)
{
using (var dbWrapper = new DBWrapper())
{
try
{
await dbWrapper.BeginTransaction(); // トランザクション開始
StringBuilder sqlCommand = new StringBuilder();
sqlCommand.Append("DELETE FROM FileMgmt WHERE fileNo = @fileNo");
// パラメータを辞書で作成
var parameters = new Dictionary<string, object>
{
{"@fileNo", fileNo},
};
// SQLコマンドを実行
await dbWrapper.ExecuteNonQuery(sqlCommand.ToString(), parameters);
await dbWrapper.CommitTransaction(); // コミット
}
catch (Exception ex)
{
// エラー処理
Console.WriteLine(ex.Message);
await dbWrapper.RollbackTransaction(); // ロールバック
}
}
}
}
}
DBwrapperクラスの実装
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
namespace WinFormsDemo.Common
{
/// <summary>
/// データベース接続をラップするクラスです。
/// SQLクエリの実行、トランザクションの管理、接続のクローズなどを行います。
/// </summary>
public class DBWrapper : IDisposable
{
private string _connectionString;
private readonly SqlConnection _connection;
private SqlTransaction _transaction;
/// <summary>
/// デフォルトの接続文字列を使用してDBWrapperのインスタンスを初期化します。
/// </summary>
public DBWrapper() : this(ConfigurationManager.ConnectionStrings["DBConnectionString"].ToString()) { }
/// <summary>
/// 指定した接続文字列を使用してDBWrapperのインスタンスを初期化します。
/// </summary>
/// <param name="connectionString">接続文字列</param>
public DBWrapper(string connectionString)
{
_connection = new SqlConnection(connectionString);
_connectionString = connectionString;
}
/// <summary>
/// 新しいSQL接続を作成します。
/// </summary>
/// <returns>SqlConnectionのインスタンス</returns>
private SqlConnection CreateConnection()
{
return new SqlConnection(_connectionString);
}
/// <summary>
/// SQLクエリを非同期で実行し、結果をDataTableとして返します。
/// </summary>
/// <param name="sql">実行するSQLクエリ</param>
/// <param name="parameters">SQLパラメータ(任意)</param>
/// <returns>クエリ結果を含むDataTable</returns>
public async Task<DataTable> ExecuteQuery(string sql, SqlParameter[] parameters = null)
{
using (var connection = CreateConnection())
{
using (var command = new SqlCommand(sql, connection))
{
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
DataTable dataTable = new DataTable();
using (var adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataTable); // データをDataTableに埋め込む
}
return dataTable;
}
}
}
/// <summary>
/// SQLコマンドを非同期で実行し、影響を受けた行数を返します。
/// </summary>
/// <param name="sql">実行するSQLクエリ</param>
/// <param name="parameters">SQLパラメータ(任意)</param>
/// <returns>影響を受けた行数</returns>
public async Task<int> ExecuteNonQuery(string sql, Dictionary<string, object> parameters = null)
{
using (var command = _connection.CreateCommand())
{
command.Transaction = _transaction; // トランザクションを関連付け
command.CommandText = sql;
if (parameters != null)
{
// パラメータをコマンドに追加
foreach (var param in parameters)
{
var dbParam = command.CreateParameter();
dbParam.ParameterName = param.Key;
dbParam.Value = param.Value ?? DBNull.Value;
command.Parameters.Add(dbParam);
}
}
// 非同期でSQLコマンドを実行
return await command.ExecuteNonQueryAsync();
}
}
/// <summary>
/// トランザクションを開始します。
/// </summary>
/// <returns>非同期タスク</returns>
public async Task BeginTransaction()
{
if (_connection.State == ConnectionState.Closed)
{
await _connection.OpenAsync(); // 接続を開く
}
if (_transaction == null)
{
_transaction = _connection.BeginTransaction(); // トランザクション開始
}
}
/// <summary>
/// トランザクションをコミットします。
/// </summary>
/// <returns>非同期タスク</returns>
public async Task CommitTransaction()
{
try
{
await Task.Run(() => _transaction.Commit()); // トランザクションをコミット
}
finally
{
await DisposeTransactionAsync(); // トランザクションの破棄
}
}
/// <summary>
/// トランザクションをロールバックします。
/// </summary>
/// <returns>非同期タスク</returns>
public async Task RollbackTransaction()
{
if (_transaction == null)
{
throw new InvalidOperationException("トランザクションが開始されていません。");
}
try
{
await Task.Run(() => _transaction.Rollback()); // トランザクションをロールバック
}
finally
{
await DisposeTransactionAsync(); // トランザクションの破棄
}
}
/// <summary>
/// トランザクションのリソースを非同期で破棄します。
/// </summary>
/// <returns>非同期タスク</returns>
private async Task DisposeTransactionAsync()
{
if (_transaction != null)
{
await Task.Run(() => _transaction.Dispose()); // トランザクションを破棄
_transaction = null;
}
}
/// <summary>
/// DBWrapperのリソースを解放します。
/// </summary>
public void Dispose()
{
try
{
_transaction?.Dispose(); // トランザクションを破棄
_connection?.Dispose(); // 接続を破棄
}
catch (Exception ex)
{
Console.WriteLine("リソースの解放中にエラーが発生しました: " + ex.Message);
}
}
}
}
結果
おわりに
Windows Formsを活用したCRUD操作(作成・読み取り・更新・削除)の基本的な実装方法を紹介しました。
特に、「実行ボタンをクリックするだけでCRUD処理を一括適用できる」 仕様となっており、シンプルな操作性を実現しています。
この実装を参考に、実際の業務に役立つアプリケーションを作成してみてください!
参考:
-
DataGridView の公式ドキュメント: Microsoft
-
OpenFileDialog の使い方: Microsoft Docs