「C Sharpとデータベース - CRUDの実行」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

文字列「<source lang」を「<syntaxhighlight lang」に置換
 
(同じ利用者による、間の5版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
C#でSQL Serverに対して変更処理(INSERT, UPDATE, DELETE)を実行する方法をまとめる。<br><br>
C#でADO.NETを使用してデータベースに対するCRUD (Create / Read / Update / Delete) 操作を実行することができる。<br>
<br>
各DBMS (SQL Server, Oracle Database, MySQL) に対応するデータプロバイダを使用することにより、それぞれのデータベースに対して統一的なパターンでCRUD操作を実装できる。<br>
<br>
各DBMSに対応するデータプロバイダを下表に示す。<br>
<br>
<center>
{| class="wikitable"
! DBMS !! データプロバイダ !! 主な接続クラス
|-
| SQL Server || System.Data.SqlClient<br>または<br>Microsoft.Data.SqlClient || SqlConnection, SqlCommand, SqlDataReader
|-
| Oracle Database || Oracle.ManagedDataAccess.Client || OracleConnection, OracleCommand, OracleDataReader
|-
| MySQL || MySqlConnector<br>または<br>MySql.Data || MySqlConnection, MySqlCommand, MySqlDataReader
|}
</center>
<br>
下表に、CRUD操作で使用する主なメソッドを示す。<br>
<br>
<center>
{| class="wikitable"
! メソッド !! 用途
|-
| ExecuteNonQuery || INSERT / UPDATE / DELETE 文の実行<br>影響を受けた行数を返す。
|-
| ExecuteReader || SELECT文の実行<br>複数レコードを逐次的に読み取る場合に使用する。
|-
| ExecuteScalar || 単一の値を取得する場合に使用する。<br>COUNT(*)等の集計クエリに適している。
|}
</center>
<br>
SQLインジェクション攻撃を防ぐために、パラメタライズドクエリの使用は必須である。<br>
各DBMSのパラメータ形式は異なり、SQL Server / MySQL では <code>@パラメータ名</code> 形式、Oracle Database では <code>:パラメータ名</code> 形式を使用する。<br>
<br>
複数のテーブルを更新する場合や、複数の操作を一括で行う場合は、トランザクション制御 (BeginTransaction / Commit / Rollback) を適切に行う必要がある。<br>
トランザクションを使用することにより、途中でエラーが発生した場合にロールバックして、データの一貫性を保つことができる。<br>
<br>
本ページのサンプルコードでは、下表のT_USERテーブルを使用する。<br>
T_USERテーブルは、システムのユーザ認証・認可に使用される基本的なユーザ情報を管理するためのテーブルである。<br>
<br>
<center>
{| class="wikitable" | style="background-color:#fefefe;"
! 列名 !! データ型 !! NULL許可 !! キー !! 説明
|-
| ID || VARCHAR(50) || NO || PK || ユーザID<br>一意の識別子として使用する。
|-
| PASSWORD || VARCHAR(100) || NO || - || ユーザのパスワード<br>ハッシュ化された値を格納することを推奨する。
|-
| ROLE_NAME || VARCHAR(20) || NO || - || ユーザのロール名<br>(例: 'ADMIN'、'USER'、'MANAGER'等)
|}
</center>
<br><br>


== 1行のみ実行 ==
== SQL Server ==
単一テーブルにしか影響しないようなSQLは1行だけ実行することになる。<br>
==== 取得・抽出 ====
このようなクエリを実行する場合、トランザクションを考慮せずそのままExecuteNonQuery()メソッドを実行する方法が簡単である。<br>
パスワードの暗号化、SQLインジェクション対策 (パラメタライズドクエリ) を行うことを推奨する。<br>
<br>
データベースから取得した各レコードを任意のクラス (以下の例では、Userクラス) にマッピングすることもできる。<br>
<br>
以下の例で使用しているT_USERテーブルの定義を示す。<br>
T_USERテーブルは、システムのユーザ認証・認可に使用される基本的なユーザ情報を管理するためのものである。<br>
<br>
<syntaxhighlight lang="sql">
-- CREATE TABLE文
CREATE TABLE T_USER (
    ID VARCHAR(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR(100) NOT NULL,
    ROLE_NAME VARCHAR(20) NOT NULL
);
</syntaxhighlight>
<br>
パスワードカラムは、セキュリティ上の理由から、平文ではなくハッシュ化された値を保存することが推奨される。<br>
ROLE_NAMEは、アプリケーションで定義された権限レベルを表す。<br>
IDカラムは主キー (Primary Key) として設定され、重複を許可しないものとする。<br>
<br>
<syntaxhighlight lang="c#">
public class User
{
    public string Id      { get; set; }
    public string Password { get; set; }
    public string RoleName { get; set; }
}
</syntaxhighlight>
<br>
===== 1レコードのみ取得 =====
<syntaxhighlight lang="c#">
public User SelectById(string id)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    User user = null;
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = @ID";
          command.Parameters.Add(new SqlParameter("@ID", id));
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            if (reader.Read())
            {
                user = new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                };
            }
          }
      }
      catch (Exception exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return user;
}
</syntaxhighlight>
<br>
===== 全レコードの取得 =====
<syntaxhighlight lang="c#">
public List<User> SelectAll()
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    var users = new List<User>();
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            while (reader.Read())
            {
                users.Add(new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                });
            }
          }
      }
      catch (Exception exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return users;
}
</syntaxhighlight>
<br>
==== 挿入 ====
===== 1レコードのみ =====
単一テーブルにしか影響しないようなSQLは1レコードのみ実行することになる。<br>
<br>
このようなクエリを実行する場合、トランザクションを考慮せずそのままExecuteNonQueryメソッドを実行する方法が簡単である。<br>
<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  using System;
  using System;
45行目: 230行目:
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
 
===== 複数レコード =====
== トランザクション処理 ==
複数のテーブルにINSERT / UPDATE / DELETEを行う場合、トランザクションを利用する場合が多い。<br>
複数のテーブルにINSERT / UPDATE / DELETEを行う場合、トランザクションを利用する場合が多い。<br>
<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  using System;
  using System;
112行目: 297行目:
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br>
==== 更新 ====
UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
指定しない場合は、全てのレコードが更新されることに注意する。<br>
<br>
また、更新対象のレコードが存在するかどうか確認することが推奨される。<br>
<br>
===== 1レコードのみ更新 =====
<syntaxhighlight lang="c#">
public void UpdateUser(string id, string password, string roleName)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = @PASSWORD, ROLE_NAME = @ROLE_NAME WHERE ID = @ID";
          command.Parameters.Add(new SqlParameter("@ID", id));
          command.Parameters.Add(new SqlParameter("@PASSWORD", password));
          command.Parameters.Add(new SqlParameter("@ROLE_NAME", roleName));
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
            throw new Exception($"ユーザID {id} が存在しません");
          }
      }
      catch (Exception exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
===== 複数レコードの更新 =====
<syntaxhighlight lang="c#">
public void UpdateUsersByRole(string oldRole, string newRole)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    {
      // トランザクションの宣言
      SqlTransaction transaction = null;
      try
      {
          // データベースの接続開始
          connection.Open();
          // トランザクションの開始
          transaction = connection.BeginTransaction();
          using (var command = connection.CreateCommand())
          {
            // コマンドにトランザクションを設定
            command.Transaction = transaction;
            // 更新対象の件数を確認
            command.CommandText = @"SELECT COUNT(*) FROM T_USER WHERE ROLE_NAME = @OLD_ROLE";
            command.Parameters.Add(new SqlParameter("@OLD_ROLE", oldRole));
            int targetCount = (int)command.ExecuteScalar();
            if (targetCount == 0)
            {
                throw new Exception($"No users found with role: {oldRole}");
            }
            // パラメータの追加
            command.Parameters.Add(new SqlParameter("@NEW_ROLE", newRole));
            // UPDATE文の実行
            command.CommandText = @"UPDATE T_USER SET ROLE_NAME = @NEW_ROLE WHERE ROLE_NAME = @OLD_ROLE";
            var affectedRows = command.ExecuteNonQuery();
            // 更新件数の確認
            if (affectedRows != targetCount)
            {
                throw new Exception($"更新予定件数は {targetCount} 件でしたが、実際の更新件数は {affectedRows} 件でした");
            }
            // 必要に応じて他のテーブルの更新等を実行
            // ...略
            // 全ての処理が成功した場合はコミット
            transaction.Commit();
          }
      }
      catch (Exception exception)
      {
          Console.WriteLine($"エラーが発生 : {exception.Message}");
          try
          {
            // エラーが発生した場合はロールバック
            if (transaction != null)
            {
                transaction.Rollback();
                Console.WriteLine("Transaction rolled back.");
            }
          }
          catch (Exception rollbackException)
          {
            Console.WriteLine($"Rollback failed: {rollbackException.Message}");
          }
          throw;  // 元の例外を再スロー
      }
      finally
      {
          // 接続のクローズ
          if (connection.State == System.Data.ConnectionState.Open)
          {
            connection.Close();
          }
      }
    }
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
// 一般的な使用例
try
{
    var userService = new UserService();
    userService.UpdateUsersByRole("USER", "PREMIUM_USER");
}
catch (Exception ex)
{
    // エラー処理
    Console.WriteLine($"更新処理に失敗 : {ex.Message}");
}
</syntaxhighlight>
<br>
また、TransactionScopeクラスを使用することにより、宣言的なトランザクション管理が可能となる。<br>
<br>
<syntaxhighlight lang="c#">
// トランザクションスコープを使用した使用例
public void UpdateUsersByRoleWithTransactionScope(string oldRole, string newRole)
{
    using (var scope = new TransactionScope())
    {
      // 接続文字列の取得
      var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
      using (var connection = new SqlConnection(connectionString))
      {
          try
          {
            // データベースの接続開始
            connection.Open();
            // SQLの準備
            using (var command = connection.CreateCommand())
            {
                command.CommandText = @"UPDATE T_USER SET ROLE_NAME = @NEW_ROLE WHERE ROLE_NAME = @OLD_ROLE";
                command.Parameters.Add(new SqlParameter("@OLD_ROLE", oldRole));
                command.Parameters.Add(new SqlParameter("@NEW_ROLE", newRole));
                // SQLの実行
                var affectedRows = command.ExecuteNonQuery();
                if (affectedRows == 0)
                {
                    throw new Exception($"{oldRole} を持つユーザが存在しません");
                }
            }
            // 全ての処理が成功した場合のみコミット
            scope.Complete();
          }
          catch
          {  // エラーが発生した場合は自動的にロールバック
            throw;
          }
      }
    }
}
</syntaxhighlight>
<br>
==== 削除 ====
DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
指定しない場合は、全てのレコードが削除されることに注意する。<br>
<br>
<syntaxhighlight lang="sql">
DELETE FROM T_USER WHERE ID=@ID;
</syntaxhighlight>
<br><br>
<br><br>


== その他のクエリ ==
== Oracle Database ==
'''INSERT'''
Oracle Databaseへの接続には、<code>Oracle.ManagedDataAccess.Client</code> 名前空間を使用する。<br>
  INSERT INTO T_USER(ID, PASSWORD) VALUES(@ID, @PASSWORD);
<br>
NuGetパッケージ <code>Oracle.ManagedDataAccess.Core</code> をプロジェクトに追加することで利用できる。<br>
<br>
Oracle Database向けのT_USERテーブル定義を以下に示す。<br>
<br>
<syntaxhighlight lang="sql">
-- CREATE TABLE文 (Oracle Database)
CREATE TABLE T_USER (
    ID VARCHAR2(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR2(100) NOT NULL,
    ROLE_NAME VARCHAR2(20) NOT NULL
);
</syntaxhighlight>
<br>
Oracle Database固有の注意事項を以下に示す。<br>
* パラメータは、<code>:パラメータ名</code> 形式 (コロン) を使用する。
*: SQL Server / MySQL の <code>@パラメータ名</code> 形式とは異なる。
* <code>command.BindByName = true;</code> を設定することで、パラメータ名による紐付けが有効になる。
*: 設定しない場合は、パラメータの追加順序で紐付けが行われる。
<br>
==== 取得・抽出 ====
===== 1レコードのみ取得 =====
<syntaxhighlight lang="c#">
using Oracle.ManagedDataAccess.Client;
public User SelectById(string id)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    User user = null;
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = :ID";
          command.Parameters.Add(new OracleParameter(":ID", id));
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            if (reader.Read())
            {
                user = new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                };
            }
          }
      }
      catch (OracleException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return user;
}
</syntaxhighlight>
<br>
===== 全レコードの取得 =====
<syntaxhighlight lang="c#">
using Oracle.ManagedDataAccess.Client;
public List<User> SelectAll()
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    var users = new List<User>();
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            while (reader.Read())
            {
                users.Add(new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                });
            }
          }
      }
      catch (OracleException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return users;
}
</syntaxhighlight>
<br>
==== 挿入 ====
===== 1レコードのみ =====
<syntaxhighlight lang="c#">
using Oracle.ManagedDataAccess.Client;
public void Insert(string id, string password, string role)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
          // SQLの準備
          command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD, ROLE_NAME) VALUES (:ID, :PASSWORD, :ROLE_NAME)";
          command.Parameters.Add(new OracleParameter(":ID", id));
          command.Parameters.Add(new OracleParameter(":PASSWORD", password));
          command.Parameters.Add(new OracleParameter(":ROLE_NAME", role));
          // SQLの実行
          command.ExecuteNonQuery();
      }
      catch (OracleException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
===== 複数レコード =====
複数のテーブルにINSERT / UPDATE / DELETEを行う場合、OracleTransactionを利用する。<br>
<br>
トランザクション処理のパターンは、SQL Serverとほぼ同様である。<br>
<br>
<syntaxhighlight lang="c#">
using Oracle.ManagedDataAccess.Client;
public void InsertWithTransaction(string id, string password, string phone, string address)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    using (var connection = new OracleConnection(connectionString))
    {
      try
      {
          // データベースの接続開始
          connection.Open();
   
          using (var transaction = connection.BeginTransaction())
          using (var command = connection.CreateCommand())
          {
            // トランザクションの設定
            command.Transaction = transaction;
            command.BindByName = true;
            try
            {
                // 親テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD) VALUES (:ID, :PASSWORD)";
                command.Parameters.Add(new OracleParameter(":ID", id));
                command.Parameters.Add(new OracleParameter(":PASSWORD", password));
                // 親テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
                // 子テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER_EXT (ID, PHONE, ADDRESS) VALUES (:ID, :PHONE, :ADDRESS)";
                command.Parameters.Add(new OracleParameter(":PHONE", phone));
                command.Parameters.Add(new OracleParameter(":ADDRESS", address));
                // 子テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
                // コミット
                transaction.Commit();
            }
            catch
            {
                // ロールバック
                transaction.Rollback();
                throw;
            }
          }
      }
      catch (OracleException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
==== 更新 ====
===== 1レコードのみ更新 =====
UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
<br>
affectedRowsで更新件数を確認することにより、対象レコードの存在チェックを行うことができる。<br>
<br>
<syntaxhighlight lang="c#">
using Oracle.ManagedDataAccess.Client;
public void UpdateUser(string id, string password, string roleName)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = :PASSWORD, ROLE_NAME = :ROLE_NAME WHERE ID = :ID";
          command.Parameters.Add(new OracleParameter(":ID", id));
          command.Parameters.Add(new OracleParameter(":PASSWORD", password));
          command.Parameters.Add(new OracleParameter(":ROLE_NAME", roleName));
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
            throw new Exception($"ユーザID {id} が存在しません");
          }
      }
      catch (OracleException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
==== 削除 ====
DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
<br>
<u>指定しない場合は、全てのレコードが削除されることに注意する。</u><br>
<br>
<syntaxhighlight lang="sql">
DELETE FROM T_USER WHERE ID = :ID;
</syntaxhighlight>
<br><br>


'''UPDATE'''
== MySQL ==
  UPDATE T_USER SET PASSWORD=@PASSWORD WHERE ID=@ID;
MySQLへの接続には、<code>MySqlConnector</code> 名前空間を使用する。<br>
<br>
NuGetパッケージ <u>MySqlConnector</u> をプロジェクトに追加することで利用できる。<br>
<br>
MySQL向けのT_USERテーブル定義を以下に示す。<br>
<br>
<syntaxhighlight lang="sql">
-- CREATE TABLE文 (MySQL)
CREATE TABLE T_USER (
    ID VARCHAR(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR(100) NOT NULL,
    ROLE_NAME VARCHAR(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
</syntaxhighlight>
<br>
MySQL固有の注意事項を以下に示す。<br>
* パラメータは、<code>@パラメータ名</code> 形式 (アットマーク) を使用する。
*: SQL Serverと同じ形式であるが、Oracleとは異なる。
* トランザクションを使用する場合、テーブルのストレージエンジンが InnoDBである必要がある。
*: MyISAMエンジンではトランザクションがサポートされない。
<br>
==== 取得・抽出 ====
===== 1レコードのみ取得 =====
<syntaxhighlight lang="c#">
using MySqlConnector;
public User SelectById(string id)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    User user = null;
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = @ID";
          command.Parameters.Add(new MySqlParameter("@ID", id));
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            if (reader.Read())
            {
                user = new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                };
            }
          }
      }
      catch (MySqlException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return user;
}
</syntaxhighlight>
<br>
===== 全レコードの取得 =====
<syntaxhighlight lang="c#">
using MySqlConnector;
public List<User> SelectAll()
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    var users = new List<User>();
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
            // レコードの取得
            while (reader.Read())
            {
                users.Add(new User
                {
                  Id = reader["ID"].ToString(),
                  Password = reader["PASSWORD"].ToString(),
                  RoleName = reader["ROLE_NAME"].ToString()
                });
            }
          }
      }
      catch (MySqlException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
    return users;
}
</syntaxhighlight>
<br>
==== 挿入 ====
===== 1レコードのみ =====
<syntaxhighlight lang="c#">
using MySqlConnector;
public void Insert(string id, string password, string role)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD, ROLE_NAME) VALUES (@ID, @PASSWORD, @ROLE_NAME)";
          command.Parameters.Add(new MySqlParameter("@ID", id));
          command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
          command.Parameters.Add(new MySqlParameter("@ROLE_NAME", role));
          // SQLの実行
          command.ExecuteNonQuery();
      }
      catch (MySqlException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
===== 複数レコード =====
複数のテーブルにINSERT / UPDATE / DELETEを行う場合、MySqlTransactionを利用する。<br>
<br>
<u>InnoDBエンジンを使用している場合のみ、トランザクションが有効である点に注意する。</u><br>
<br>
<syntaxhighlight lang="c#">
using MySqlConnector;
public void InsertWithTransaction(string id, string password, string phone, string address)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    using (var connection = new MySqlConnection(connectionString))
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          using (var transaction = connection.BeginTransaction())
          using (var command = connection.CreateCommand())
          {
            // トランザクションの設定
            command.Transaction = transaction;
            try
            {
                // 親テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD) VALUES (@ID, @PASSWORD)";
                command.Parameters.Add(new MySqlParameter("@ID", id));
                command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
                // 親テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
                // 子テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER_EXT (ID, PHONE, ADDRESS) VALUES (@ID, @PHONE, @ADDRESS)";
                command.Parameters.Add(new MySqlParameter("@PHONE", phone));
                command.Parameters.Add(new MySqlParameter("@ADDRESS", address));
                // 子テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
                // コミット
                transaction.Commit();
            }
            catch
            {
                // ロールバック
                transaction.Rollback();
                throw;
            }
          }
      }
      catch (MySqlException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
==== 更新 ====
===== 1レコードのみ更新 =====
UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
<br>
<u>指定しない場合は、全てのレコードが更新されることに注意する。</u><br>
<br>
<syntaxhighlight lang="c#">
using MySqlConnector;
  public void UpdateUser(string id, string password, string roleName)
{
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
      try
      {
          // データベースの接続開始
          connection.Open();
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = @PASSWORD, ROLE_NAME = @ROLE_NAME WHERE ID = @ID";
          command.Parameters.Add(new MySqlParameter("@ID", id));
          command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
          command.Parameters.Add(new MySqlParameter("@ROLE_NAME", roleName));
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
            throw new Exception($"ユーザID {id} が存在しません");
          }
      }
      catch (MySqlException exception)
      {
          Console.WriteLine(exception.Message);
          throw;
      }
      finally
      {
          // データベースの接続終了
          connection.Close();
      }
    }
}
</syntaxhighlight>
<br>
==== 削除 ====
DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。<br>
指定しない場合は、全てのレコードが削除されることに注意する。<br>
<br>
<syntaxhighlight lang="sql">
DELETE FROM T_USER WHERE ID = @ID;
</syntaxhighlight>
<br><br>


'''DELETE'''
DELETE FROM T_USER WHERE ID=@ID;
<br><br>


{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
[[カテゴリ:C_Sharp]]
[[カテゴリ:C_Sharp]]

2026年5月14日 (木) 00:56時点における最新版

概要

C#でADO.NETを使用してデータベースに対するCRUD (Create / Read / Update / Delete) 操作を実行することができる。

各DBMS (SQL Server, Oracle Database, MySQL) に対応するデータプロバイダを使用することにより、それぞれのデータベースに対して統一的なパターンでCRUD操作を実装できる。

各DBMSに対応するデータプロバイダを下表に示す。

DBMS データプロバイダ 主な接続クラス
SQL Server System.Data.SqlClient
または
Microsoft.Data.SqlClient
SqlConnection, SqlCommand, SqlDataReader
Oracle Database Oracle.ManagedDataAccess.Client OracleConnection, OracleCommand, OracleDataReader
MySQL MySqlConnector
または
MySql.Data
MySqlConnection, MySqlCommand, MySqlDataReader


下表に、CRUD操作で使用する主なメソッドを示す。

メソッド 用途
ExecuteNonQuery INSERT / UPDATE / DELETE 文の実行
影響を受けた行数を返す。
ExecuteReader SELECT文の実行
複数レコードを逐次的に読み取る場合に使用する。
ExecuteScalar 単一の値を取得する場合に使用する。
COUNT(*)等の集計クエリに適している。


SQLインジェクション攻撃を防ぐために、パラメタライズドクエリの使用は必須である。
各DBMSのパラメータ形式は異なり、SQL Server / MySQL では @パラメータ名 形式、Oracle Database では :パラメータ名 形式を使用する。

複数のテーブルを更新する場合や、複数の操作を一括で行う場合は、トランザクション制御 (BeginTransaction / Commit / Rollback) を適切に行う必要がある。
トランザクションを使用することにより、途中でエラーが発生した場合にロールバックして、データの一貫性を保つことができる。

本ページのサンプルコードでは、下表のT_USERテーブルを使用する。
T_USERテーブルは、システムのユーザ認証・認可に使用される基本的なユーザ情報を管理するためのテーブルである。

列名 データ型 NULL許可 キー 説明
ID VARCHAR(50) NO PK ユーザID
一意の識別子として使用する。
PASSWORD VARCHAR(100) NO - ユーザのパスワード
ハッシュ化された値を格納することを推奨する。
ROLE_NAME VARCHAR(20) NO - ユーザのロール名
(例: 'ADMIN'、'USER'、'MANAGER'等)



SQL Server

取得・抽出

パスワードの暗号化、SQLインジェクション対策 (パラメタライズドクエリ) を行うことを推奨する。

データベースから取得した各レコードを任意のクラス (以下の例では、Userクラス) にマッピングすることもできる。

以下の例で使用しているT_USERテーブルの定義を示す。
T_USERテーブルは、システムのユーザ認証・認可に使用される基本的なユーザ情報を管理するためのものである。

 -- CREATE TABLE文
 
 CREATE TABLE T_USER (
    ID VARCHAR(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR(100) NOT NULL,
    ROLE_NAME VARCHAR(20) NOT NULL
 );


パスワードカラムは、セキュリティ上の理由から、平文ではなくハッシュ化された値を保存することが推奨される。
ROLE_NAMEは、アプリケーションで定義された権限レベルを表す。
IDカラムは主キー (Primary Key) として設定され、重複を許可しないものとする。

 public class User
 {
    public string Id       { get; set; }
    public string Password { get; set; }
    public string RoleName { get; set; }
 }


1レコードのみ取得
 public User SelectById(string id)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    User user = null;
 
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = @ID";
          command.Parameters.Add(new SqlParameter("@ID", id));
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             if (reader.Read())
             {
                user = new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                };
             }
          }
       }
       catch (Exception exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return user;
 }


全レコードの取得
 public List<User> SelectAll()
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
    var users = new List<User>();
 
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             while (reader.Read())
             {
                users.Add(new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                });
             }
          }
       }
       catch (Exception exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return users;
 }


挿入

1レコードのみ

単一テーブルにしか影響しないようなSQLは1レコードのみ実行することになる。

このようなクエリを実行する場合、トランザクションを考慮せずそのままExecuteNonQueryメソッドを実行する方法が簡単である。

 using System;
 using System.Configuration;
 using System.Data.SqlClient;
 
 public void Insert1(string id, string password, string role)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
 
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
        try
        {
            // データベースの接続開始
            connection.Open();
 
            // SQLの準備
            command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD, ROLE_NAME) VALUES (@ID, @PASSWORD, @ROLE_NAME)";
            command.Parameters.Add(new SqlParameter("@ID", id));
            command.Parameters.Add(new SqlParameter("@PASSWORD", password));
            command.Parameters.Add(new SqlParameter("@ROLE_NAME", role));
 
            // SQLの実行
            command.ExecuteNonQuery();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception.Message);
            throw;
        }
        finally
        {
            // データベースの接続終了
            connection.Close();
        }
    }
 }


複数レコード

複数のテーブルにINSERT / UPDATE / DELETEを行う場合、トランザクションを利用する場合が多い。

 using System;
 using System.Configuration;
 using System.Data.SqlClient;
 
 public void Insert2(string id, string password, string phone, string address)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
 
    using (var connection = new SqlConnection(connectionString))
    {
        try
        {
            // データベースの接続開始
            connection.Open();
 
            using (var transaction = connection.BeginTransaction())
            using (var command = new SqlCommand() { Connection = connection, Transaction = transaction })
            {
                try
                {
                    // 親テーブルを挿入するSQLの準備
                    command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD) VALUES (@ID, @PASSWORD)";
                    command.Parameters.Add(new SqlParameter("@ID", id));
                    command.Parameters.Add(new SqlParameter("@PASSWORD", password));
 
                    // 親テーブルを挿入するSQLの実行
                    command.ExecuteNonQuery();
 
                    // 子テーブルを挿入するSQLの準備
                    command.CommandText = @"INSERT INTO T_USER_EXT (ID, PHONE, ADDRESS) VALUES (@ID, @PHONE, @ADDRESS)";
                    ////command.Parameters.Add(new SqlParameter("@ID", id));         // <- 上で既に @ID を追加済みのため再投入は不要。
                    command.Parameters.Add(new SqlParameter("@PHONE", phone));
                    command.Parameters.Add(new SqlParameter("@ADDRESS", address));
 
                    // 子テーブルを挿入するSQLの実行
                    command.ExecuteNonQuery();
 
                    // コミット
                    transaction.Commit();
                }
                catch
                {
                    // ロールバック
                    transaction.Rollback();
                    throw;
                }
            }
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception.Message);
            throw;
        }
        finally
        {
            // データベースの接続終了
            connection.Close();
        }
    }
 }


更新

UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。
指定しない場合は、全てのレコードが更新されることに注意する。

また、更新対象のレコードが存在するかどうか確認することが推奨される。

1レコードのみ更新
 public void UpdateUser(string id, string password, string roleName)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
 
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = @PASSWORD, ROLE_NAME = @ROLE_NAME WHERE ID = @ID";
          command.Parameters.Add(new SqlParameter("@ID", id));
          command.Parameters.Add(new SqlParameter("@PASSWORD", password));
          command.Parameters.Add(new SqlParameter("@ROLE_NAME", roleName));
 
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
 
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
             throw new Exception($"ユーザID {id} が存在しません");
          }
       }
       catch (Exception exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          connection.Close();
       }
    }
 }


複数レコードの更新
 public void UpdateUsersByRole(string oldRole, string newRole)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
 
    using (var connection = new SqlConnection(connectionString))
    {
       // トランザクションの宣言
       SqlTransaction transaction = null;
 
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // トランザクションの開始
          transaction = connection.BeginTransaction();
 
          using (var command = connection.CreateCommand())
          {
             // コマンドにトランザクションを設定
             command.Transaction = transaction;
 
             // 更新対象の件数を確認
             command.CommandText = @"SELECT COUNT(*) FROM T_USER WHERE ROLE_NAME = @OLD_ROLE";
             command.Parameters.Add(new SqlParameter("@OLD_ROLE", oldRole));
 
             int targetCount = (int)command.ExecuteScalar();
             if (targetCount == 0)
             {
                throw new Exception($"No users found with role: {oldRole}");
             }
 
             // パラメータの追加
             command.Parameters.Add(new SqlParameter("@NEW_ROLE", newRole));
 
             // UPDATE文の実行
             command.CommandText = @"UPDATE T_USER SET ROLE_NAME = @NEW_ROLE WHERE ROLE_NAME = @OLD_ROLE";
 
             var affectedRows = command.ExecuteNonQuery();
 
             // 更新件数の確認
             if (affectedRows != targetCount)
             {
                throw new Exception($"更新予定件数は {targetCount} 件でしたが、実際の更新件数は {affectedRows} 件でした");
             }
 
             // 必要に応じて他のテーブルの更新等を実行
             // ...略
 
             // 全ての処理が成功した場合はコミット
             transaction.Commit();
          }
       }
       catch (Exception exception)
       {
          Console.WriteLine($"エラーが発生 : {exception.Message}");
 
          try
          {
             // エラーが発生した場合はロールバック
             if (transaction != null)
             {
                transaction.Rollback();
                Console.WriteLine("Transaction rolled back.");
             }
          }
          catch (Exception rollbackException)
          {
             Console.WriteLine($"Rollback failed: {rollbackException.Message}");
          }
 
          throw;  // 元の例外を再スロー
       }
       finally
       {
          // 接続のクローズ
          if (connection.State == System.Data.ConnectionState.Open)
          {
             connection.Close();
          }
       }
    }
 }


 // 一般的な使用例
 
 try
 {
    var userService = new UserService();
    userService.UpdateUsersByRole("USER", "PREMIUM_USER");
 }
 catch (Exception ex)
 {
    // エラー処理
    Console.WriteLine($"更新処理に失敗 : {ex.Message}");
 }


また、TransactionScopeクラスを使用することにより、宣言的なトランザクション管理が可能となる。

 // トランザクションスコープを使用した使用例
 
 public void UpdateUsersByRoleWithTransactionScope(string oldRole, string newRole)
 {
    using (var scope = new TransactionScope())
    {
       // 接続文字列の取得
       var connectionString = ConfigurationManager.ConnectionStrings["sqlsvr"].ConnectionString;
 
       using (var connection = new SqlConnection(connectionString))
       {
          try
          {
             // データベースの接続開始
             connection.Open();
 
             // SQLの準備
             using (var command = connection.CreateCommand())
             {
                command.CommandText = @"UPDATE T_USER SET ROLE_NAME = @NEW_ROLE WHERE ROLE_NAME = @OLD_ROLE";
                command.Parameters.Add(new SqlParameter("@OLD_ROLE", oldRole));
                command.Parameters.Add(new SqlParameter("@NEW_ROLE", newRole));
 
                // SQLの実行
                var affectedRows = command.ExecuteNonQuery();
                if (affectedRows == 0)
                {
                    throw new Exception($"{oldRole} を持つユーザが存在しません");
                }
             }
 
             // 全ての処理が成功した場合のみコミット
             scope.Complete();
          }
          catch
          {  // エラーが発生した場合は自動的にロールバック
             throw;
          }
       }
    }
 }


削除

DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。
指定しない場合は、全てのレコードが削除されることに注意する。

 DELETE FROM T_USER WHERE ID=@ID;



Oracle Database

Oracle Databaseへの接続には、Oracle.ManagedDataAccess.Client 名前空間を使用する。

NuGetパッケージ Oracle.ManagedDataAccess.Core をプロジェクトに追加することで利用できる。

Oracle Database向けのT_USERテーブル定義を以下に示す。

 -- CREATE TABLE文 (Oracle Database)
 
 CREATE TABLE T_USER (
    ID VARCHAR2(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR2(100) NOT NULL,
    ROLE_NAME VARCHAR2(20) NOT NULL
 );


Oracle Database固有の注意事項を以下に示す。

  • パラメータは、:パラメータ名 形式 (コロン) を使用する。
    SQL Server / MySQL の @パラメータ名 形式とは異なる。
  • command.BindByName = true; を設定することで、パラメータ名による紐付けが有効になる。
    設定しない場合は、パラメータの追加順序で紐付けが行われる。


取得・抽出

1レコードのみ取得
 using Oracle.ManagedDataAccess.Client;
 
 public User SelectById(string id)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    User user = null;
 
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = :ID";
          command.Parameters.Add(new OracleParameter(":ID", id));
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             if (reader.Read())
             {
                user = new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                };
             }
          }
       }
       catch (OracleException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return user;
 }


全レコードの取得
 using Oracle.ManagedDataAccess.Client;
 
 public List<User> SelectAll()
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
    var users = new List<User>();
 
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             while (reader.Read())
             {
                users.Add(new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                });
             }
          }
       }
       catch (OracleException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return users;
 }


挿入

1レコードのみ
 using Oracle.ManagedDataAccess.Client;
 
 public void Insert(string id, string password, string role)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
 
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
 
          // SQLの準備
          command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD, ROLE_NAME) VALUES (:ID, :PASSWORD, :ROLE_NAME)";
          command.Parameters.Add(new OracleParameter(":ID", id));
          command.Parameters.Add(new OracleParameter(":PASSWORD", password));
          command.Parameters.Add(new OracleParameter(":ROLE_NAME", role));
 
          // SQLの実行
          command.ExecuteNonQuery();
       }
       catch (OracleException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


複数レコード

複数のテーブルにINSERT / UPDATE / DELETEを行う場合、OracleTransactionを利用する。

トランザクション処理のパターンは、SQL Serverとほぼ同様である。

 using Oracle.ManagedDataAccess.Client;
 
 public void InsertWithTransaction(string id, string password, string phone, string address)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
 
    using (var connection = new OracleConnection(connectionString))
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          using (var transaction = connection.BeginTransaction())
          using (var command = connection.CreateCommand())
          {
             // トランザクションの設定
             command.Transaction = transaction;
             command.BindByName = true;
 
             try
             {
                // 親テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD) VALUES (:ID, :PASSWORD)";
                command.Parameters.Add(new OracleParameter(":ID", id));
                command.Parameters.Add(new OracleParameter(":PASSWORD", password));
 
                // 親テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
 
                // 子テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER_EXT (ID, PHONE, ADDRESS) VALUES (:ID, :PHONE, :ADDRESS)";
                command.Parameters.Add(new OracleParameter(":PHONE", phone));
                command.Parameters.Add(new OracleParameter(":ADDRESS", address));
 
                // 子テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
 
                // コミット
                transaction.Commit();
             }
             catch
             {
                // ロールバック
                transaction.Rollback();
                throw;
             }
          }
       }
       catch (OracleException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


更新

1レコードのみ更新

UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。

affectedRowsで更新件数を確認することにより、対象レコードの存在チェックを行うことができる。

 using Oracle.ManagedDataAccess.Client;
 
 public void UpdateUser(string id, string password, string roleName)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["oracle"].ConnectionString;
 
    using (var connection = new OracleConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // パラメータ名による紐付けを有効化
          command.BindByName = true;
 
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = :PASSWORD, ROLE_NAME = :ROLE_NAME WHERE ID = :ID";
          command.Parameters.Add(new OracleParameter(":ID", id));
          command.Parameters.Add(new OracleParameter(":PASSWORD", password));
          command.Parameters.Add(new OracleParameter(":ROLE_NAME", roleName));
 
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
 
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
             throw new Exception($"ユーザID {id} が存在しません");
          }
       }
       catch (OracleException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


削除

DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。

指定しない場合は、全てのレコードが削除されることに注意する。

 DELETE FROM T_USER WHERE ID = :ID;



MySQL

MySQLへの接続には、MySqlConnector 名前空間を使用する。

NuGetパッケージ MySqlConnector をプロジェクトに追加することで利用できる。

MySQL向けのT_USERテーブル定義を以下に示す。

 -- CREATE TABLE文 (MySQL)
 
 CREATE TABLE T_USER (
    ID VARCHAR(50) NOT NULL PRIMARY KEY,
    PASSWORD VARCHAR(100) NOT NULL,
    ROLE_NAME VARCHAR(20) NOT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


MySQL固有の注意事項を以下に示す。

  • パラメータは、@パラメータ名 形式 (アットマーク) を使用する。
    SQL Serverと同じ形式であるが、Oracleとは異なる。
  • トランザクションを使用する場合、テーブルのストレージエンジンが InnoDBである必要がある。
    MyISAMエンジンではトランザクションがサポートされない。


取得・抽出

1レコードのみ取得
 using MySqlConnector;
 
 public User SelectById(string id)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    User user = null;
 
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER WHERE ID = @ID";
          command.Parameters.Add(new MySqlParameter("@ID", id));
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             if (reader.Read())
             {
                user = new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                };
             }
          }
       }
       catch (MySqlException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return user;
 }


全レコードの取得
 using MySqlConnector;
 
 public List<User> SelectAll()
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
    var users = new List<User>();
 
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = "SELECT ID, PASSWORD, ROLE_NAME FROM T_USER";
 
          // SQLの実行
          using (var reader = command.ExecuteReader())
          {
             // レコードの取得
             while (reader.Read())
             {
                users.Add(new User
                {
                   Id = reader["ID"].ToString(),
                   Password = reader["PASSWORD"].ToString(),
                   RoleName = reader["ROLE_NAME"].ToString()
                });
             }
          }
       }
       catch (MySqlException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
    return users;
 }


挿入

1レコードのみ
 using MySqlConnector;
 
 public void Insert(string id, string password, string role)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
 
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD, ROLE_NAME) VALUES (@ID, @PASSWORD, @ROLE_NAME)";
          command.Parameters.Add(new MySqlParameter("@ID", id));
          command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
          command.Parameters.Add(new MySqlParameter("@ROLE_NAME", role));
 
          // SQLの実行
          command.ExecuteNonQuery();
       }
       catch (MySqlException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


複数レコード

複数のテーブルにINSERT / UPDATE / DELETEを行う場合、MySqlTransactionを利用する。

InnoDBエンジンを使用している場合のみ、トランザクションが有効である点に注意する。

 using MySqlConnector;
 
 public void InsertWithTransaction(string id, string password, string phone, string address)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
 
    using (var connection = new MySqlConnection(connectionString))
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          using (var transaction = connection.BeginTransaction())
          using (var command = connection.CreateCommand())
          {
             // トランザクションの設定
             command.Transaction = transaction;
 
             try
             {
                // 親テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER (ID, PASSWORD) VALUES (@ID, @PASSWORD)";
                command.Parameters.Add(new MySqlParameter("@ID", id));
                command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
 
                // 親テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
 
                // 子テーブルを挿入するSQLの準備
                command.CommandText = @"INSERT INTO T_USER_EXT (ID, PHONE, ADDRESS) VALUES (@ID, @PHONE, @ADDRESS)";
                command.Parameters.Add(new MySqlParameter("@PHONE", phone));
                command.Parameters.Add(new MySqlParameter("@ADDRESS", address));
 
                // 子テーブルを挿入するSQLの実行
                command.ExecuteNonQuery();
 
                // コミット
                transaction.Commit();
             }
             catch
             {
                // ロールバック
                transaction.Rollback();
                throw;
             }
          }
       }
       catch (MySqlException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


更新

1レコードのみ更新

UPDATE文を使用する場合、WHERE句を必ず指定することが推奨される。

指定しない場合は、全てのレコードが更新されることに注意する。

 using MySqlConnector;
 
 public void UpdateUser(string id, string password, string roleName)
 {
    // 接続文字列の取得
    var connectionString = ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
 
    using (var connection = new MySqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
       try
       {
          // データベースの接続開始
          connection.Open();
 
          // SQLの準備
          command.CommandText = @"UPDATE T_USER SET PASSWORD = @PASSWORD, ROLE_NAME = @ROLE_NAME WHERE ID = @ID";
          command.Parameters.Add(new MySqlParameter("@ID", id));
          command.Parameters.Add(new MySqlParameter("@PASSWORD", password));
          command.Parameters.Add(new MySqlParameter("@ROLE_NAME", roleName));
 
          // SQLの実行
          var affectedRows = command.ExecuteNonQuery();
 
          // 更新対象のレコードが存在しない場合
          if (affectedRows == 0)
          {
             throw new Exception($"ユーザID {id} が存在しません");
          }
       }
       catch (MySqlException exception)
       {
          Console.WriteLine(exception.Message);
          throw;
       }
       finally
       {
          // データベースの接続終了
          connection.Close();
       }
    }
 }


削除

DELETE文を使用する場合、WHERE句を必ず指定することが推奨される。
指定しない場合は、全てのレコードが削除されることに注意する。

 DELETE FROM T_USER WHERE ID = @ID;