0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows Formsを活用したCRUDの操作(作成、読み取り、更新、削除)

Posted at

概要

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操作を実装します。

FileCRUD.cs
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 に表示します。

FileCRUD.cs
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)

新しいファイルを追加する機能を実装します。

FileCRUD.cs
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)

選択した行を削除する処理を実装します。

FileCRUD.cs
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)を一括で適用 します。

FileCRUD.cs
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クラスの実装

FileServices.cs
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クラスの実装

DBWrapper.cs
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);
            }
        }
    }
}

結果

image.png

おわりに

Windows Formsを活用したCRUD操作(作成・読み取り・更新・削除)の基本的な実装方法を紹介しました。
特に、「実行ボタンをクリックするだけでCRUD処理を一括適用できる」 仕様となっており、シンプルな操作性を実現しています。
この実装を参考に、実際の業務に役立つアプリケーションを作成してみてください!
参考:

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?