[C#] 47. Nugetを使い方(外部ライブラリ)とデータベース(MariaDB(Mysql))を使い方、そしてトランザクション(Transaction)


Study / C#    作成日付 : 2021/10/08 18:58:57   修正日付 : 2021/10/08 19:00:07

こんにちは。明月です。


この投稿はC#でNugetを使い方(外部ライブラリ)とデータベース(MariaDB(Mysql))を使い方、そしてトランザクション(Transaction)に関する説明です。


C#が言語的で利点だと思う部分は.Net framework中で基本的なライブラリがすごく多く含めています。その以外にもVisual studioのIDEツールもすごく便利だし、同じ製品系(MS製品系)に関しても互換性がすごくいい利点があります。

でも、多いライブラリを含めていると思っても、実際に使ってされるすべてのライブラリを持っていることではありません。


例えば、基本的にC#ではMsSQLデータベースに関する接続ライブラリを持っています。別に外部ライブラリが必要ありません。なので別の種類のデータベースのライブラリは.Net frameworkでありません。

つまり、C#を使ったらMsSqlを使うことをお勧めすることです。でも、仕様により、あるいは様々な理由で開発言語はC#で開発してもデータベースはOracle(오라클)やMysql(MariaDB)を使わなければならない場合もあります。


そうなら外部ライブラリを使わなければならないですが、MariaDBホームページに接続するとC#ライブラリファイルがあります。

link - mariadb net-connectors


このようにライブラリをダウンロードしてプロジェクトに連結して使ってもよいですが、このようにするとバージョン管理や配布(Deploy)や他人とプロジェクトを共有する時、ライブラリ管理などの問題が発生します。


そのため、プロジェクトのライブラリ管理ツールがありますが、JavaにはmavenがあるみたいにC#にはNugetがあります。

NugetはVisual studioがインストールされていると別途にインストールする必要はありません。


プロジェクトでReferencesの項目でマウスの右クリックしてContextメニューをみればManage Nuget項目があります。


Browseタブで検索すればインストールができるライブラリが表示されます。

我々はMariaDBを使っているのでMySql.Data ライブラリをクリックしてVersionを確認してInstallボタンを押下します。


そうなら規約が表示されるし、Acceptを押下してインストールを開始します。


そして、Referencesの項目を開いてみるとライブラリが連結されていることを確認できます。

もし、ライブラリが依存性されているライブラリなら(つまり、ライブラリで他のライブラリを参照しているなら)必要なライブラリも自動に連結されます。


ここで我々はMariaDBを使いますが、なぜMysqlをダウンロードするかをみると実は過去の時にMysqlを無料データベースだったんですが、Oracle社に合併されて有料化になりました。

それで以前にMysqlを作ったチームがまた独立して無料時代のソースをフォークして無料データベースのMariaDBを作成しました。

なので、MySqlとMariaDBは似てますが、別のデータベースです。コードベースはMySqlとMariaDBは同じなので、コネクションは同じものを使います。


MariaDBをインストールする方法は別の投稿で説明しているので参考してください。

link - [Window] MariaDBをインストールする方法


データベースがインストールされたら、簡単なテーブルを作成してデータを入れてC#プログラムで検索してみましょう。

-- drop table Test;
-- テーブル作成
create table Test(
	idx bigint auto_increment,
	data varchar(1024),
	
	primary key(idx)
)
-- データインサート
insert into Test (data) values('Hello world - 1');
insert into Test (data) values('Hello world - 2');
insert into Test (data) values('Hello world - 3');
insert into Test (data) values('Hello world - 4');
insert into Test (data) values('Hello world - 5');
insert into Test (data) values('Hello world - 6');
insert into Test (data) values('Hello world - 7');
insert into Test (data) values('Hello world - 8');
insert into Test (data) values('Hello world - 9');
insert into Test (data) values('Hello world - 10');
insert into Test (data) values('Hello world - 11');
insert into Test (data) values('Hello world - 12');
-- 検索
select * from Test;


上みたいデータベースにテーブルを作成して簡単なデータを入力しました。


このデータを利用してC#プログラムでデータを取得しましょう。

using System;
using System.Collections.Generic;
using MySql.Data.MySqlClient;
using System.Data;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // SqlCommandクラスのインスタンス生成
      var cmd = new MySqlCommand();
      // 接続コネクションを生成する。
      cmd.Connection = new MySqlConnection("Data Source=localhost;Database=BlogExample;SSL Mode=None;User Id=root;Password=");
      // 検索するクエリを入れる。
      cmd.CommandText = "select * from Test";
      // 検索クエリタイプ、一般クエリはTextだし、プロシージャはStoredProcedureだ。
      cmd.CommandType = CommandType.Text;
      // 検索して格納するデータ変数
      List<List<object>> dataList = null;
      // 検索したテーブルのColumn変数
      string[] fieldList = null;
      // Close設定(スタック領域が終わったら、自動にコネクション終了)
      using (cmd.Connection)
      {
        // コネクション Open
        cmd.Connection.Open();
        // 上のクエリを接続してSqlDataReaderを取得
        var dr = cmd.ExecuteReader();
        // データ変数のインスタンス生成
        dataList = new List<List<object>>();
        // Column変数のインスタンス生成
        fieldList = new string[dr.FieldCount];
        // テーブルのフィールドほど
        for (var i = 0; i < fieldList.Length; i++)
        {
          // フィールド名を配列に格納
          fieldList[i] = dr.GetName(i);
        }
        // レコードの個数程にループ
        while (dr.Read())
        {
          // レコードデータを格納する変数リストを生成
          var entity = new List<object>();
          // データ変数リストに格納
          dataList.Add(entity);
          // Columnのサイズ程
          for (var i = 0; i < fieldList.Length; i++)
          {
            // カラムのタイプを取得
            var type = dr.GetFieldType(i);
            // intタイプなら
            if (type == typeof(int))
            {
              // intタイプで取得
              entity.Add(dr.GetInt32(i));
            }
            // stringタイプなら
            else if (type == typeof(string))
            {
              // stringタイプで取得
              entity.Add(dr.GetString(i));
            }
            else
            {
              // objectタイプで取得
              entity.Add(dr.GetValue(i));
            }
          }
        }
      }
      // コンソールにヘッダーにColumn名を出力する。
      foreach (var field in fieldList)
      {
        // コンソール出力
        Console.Write(field);
        // tabの二つをコンソールに出力
        Console.Write("\t\t");
      }
      // 改行
      Console.WriteLine();
      // テーブルColumn名とデータを区分するため、コンソール出力
      for (int i = 0; i < fieldList.Length; i++)
      {
        // コンソール出力
        Console.Write("-----------------");
      }
      // 改行
      Console.WriteLine();
      // データのサイズ程
      foreach (var entity in dataList)
      {
        // Columnのサイズ程
        foreach (var column in entity)
        {
          // コンソール出力
          Console.Write(column);
          // tabの二つをコンソールに出力
          Console.Write("\t\t");
        }
        // 改行
        Console.WriteLine();
      }

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


使い方はMsSQLで接続する方法と同じです。

ExecuteReaderで結果値を受け取ってExecuteNonQueryでinsertやupdate、deleteを実行する方法です。

link - [C#] 46. データベース(MSSQL)に接続する方法


そうなら少し知りたいことがあります。クエリくぉ実行中でエラーが発生するとどうでしょう?

using System;
using MySql.Data.MySqlClient;
using System.Data;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // MySqlCommandクラスのインスタンスを生成
      var cmd = new MySqlCommand();
      // 接続コネクションを生成する。
      cmd.Connection = new MySqlConnection("Data Source=localhost;Database=BlogExample;User Id=root;SSL Mode=None;Password=");
      // 検索クエリタイプ、一般クエリはTextだし、プロシージャはStoredProcedureだ。
      cmd.CommandType = CommandType.Text;
      // Close設定(スタック領域が終わったら、自動にコネクション終了)
      using (cmd.Connection)
      {
        // コネクション Open
        cmd.Connection.Open();
        // データ削除クエリ
        cmd.CommandText = "delete from Test";
        // 実行(返却値が必要ない。)
        cmd.ExecuteNonQuery();
        // エラーを発生!
        throw new Exception();
        // テーブルにデータを追加
        cmd.CommandText = "insert into Test (data) values(@data)";
        // @dataパラメータにデータを入力する。
        cmd.Parameters.Add(new MySqlParameter("@data", "Hello world - Addition"));
        // 実行(返却値が必要ない。)
        cmd.ExecuteNonQuery();
      }

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


わざとエラーを発生させました。でも、そのエラーがdeleteを実行してエラーを発生します。


プログラムでエラーが発生するので、プログラムで実行するすべてのクエリは実行取り消しにならなければならないのにテーブルでデータがすべて削除されました。

実際に業務でも処理中でエラーが発生するとプログラム上で実行されたクエリが取り消ししなければならないですが、そうではなければ途中でクエリ処理が止まった不完全なデータになりました。


それでトランザクションという機能がありますが、その領域中で完全に実行が完了するとすべてクエリが実行する機能です。

using System;
using MySql.Data.MySqlClient;
using System.Data;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // MySqlCommandクラスのインスタンスを生成
      var cmd = new MySqlCommand();
      // 接続コネクションを生成する。
      cmd.Connection = new MySqlConnection("Data Source=localhost;Database=BlogExample;User Id=root;SSL Mode=None;Password=");
      // 検索クエリタイプ、一般クエリはTextだし、プロシージャはStoredProcedureだ。
      cmd.CommandType = CommandType.Text;
      // Close設定(スタック領域が終わったら、自動にコネクション終了)
      using (cmd.Connection)
      {
        // コネクション Open
        cmd.Connection.Open();
        // トランザクション生成
        using (var transaction = cmd.Connection.BeginTransaction())
        {
          try
          {
            // テーブルにデータを追加
            cmd.CommandText = "insert into Test values(@data)";
            // @dataパラメータにデータを入力する。
            cmd.Parameters.Add(new MySqlParameter("@data", "Hello world - Addition"));
            // 実行(返却値が必要ない。)
            cmd.ExecuteNonQuery();
            // エラー発生
            throw new Exception();
            // トランザクションコミット(トランザクション中で実行したクエリをデータベースで実行)
            transaction.Commit();
          }
          catch
          {
            // トランザクションロールバック(トランザクション中で実行したクエリを取り消し)
            transaction.Rollback();
          }
        }
      }
      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


insertを実行しましたがトランザクションにより途中でエラーが発生すればinsert実行されないことを確認できます。

つまり、トランザクションは中でクエリをたくさん実行したと思ってもエラーが発生すればデータベースに反映されないような機能です。


ここまでがTransaction機能ですが上みたいなソースなら何かプログラムらしくない形です。

ここでObserverパターンでプログラムを作成してみましょう。

using System;
using MySql.Data.MySqlClient;
using System.Data;
using System.Text;

namespace Example
{
  class Program
  {
    // Transaction Observerパターン
    static T Transaction<T>(MySqlCommand cmd, Func<T> func)
    {
      // トランザクション生成
      using (var transaction = cmd.Connection.BeginTransaction())
      {
        try
        {
          // 関数実行
          T ret = func();
          // トランザクションコミット(トランザクション中で実行したクエリをデータベースで実行)
          transaction.Commit();
          // 関数の結果をリターン
          return ret;
        }
        catch
        {
          // トランザクションロールバック(トランザクション中で実行したクエリを取り消し)
          transaction.Rollback();
          // classならnull、structなら基本インスタンスをリターン
          return default(T);
        }
      }
    }
    // Transaction Observerパターン
    static void Transaction(MySqlCommand cmd, Action action)
    {
      // トランザクション生成
      using (var transaction = cmd.Connection.BeginTransaction())
      {
        try
        {
          // 関数実行
          action();
          // トランザクションコミット(トランザクション中で実行したクエリをデータベースで実行)
          transaction.Commit();
        }
        catch
        {
          // トランザクションロールバック(トランザクション中で実行したクエリを取り消し)
          transaction.Rollback();
        }
      }
    }
    // 実行関数
    static void Main(string[] args)
    {
      // MySqlCommandクラスのインスタンスを生成
      var cmd = new MySqlCommand();
      // 接続コネクションのインスタンスを生成する。
      cmd.Connection = new MySqlConnection("Data Source=localhost;Database=BlogExample;User Id=root;SSL Mode=None;Password=");
      // 検索クエリタイプ、一般クエリはTextだし、プロシージャはStoredProcedureだ。
      cmd.CommandType = CommandType.Text;
      // Close設定(スタック領域が終わったら、自動にコネクション終了)
      using (cmd.Connection)
      {
        // コネクション Open
        cmd.Connection.Open();
        // Transaction 実行
        Transaction(cmd, () =>
        {
          // テーブルにデータ追加
          cmd.CommandText = "insert into Test (data) values(@data)";
          // @dataパラメータにデータを入力する。
          cmd.Parameters.Add(new MySqlParameter("@data", "Hello world - Addition"));
          // 実行(返却値が必要ない。)
          cmd.ExecuteNonQuery();
        });
        // Transaction 実行
        var str = Transaction(cmd, () =>
        {
          // テーブルからデータを検索
          cmd.CommandText = "select * from Test";
          // バッファー
          var sb = new StringBuilder();
          // 実行
          using (var dr = cmd.ExecuteReader())
          {
            // レコード数程
            while (dr.Read())
            {
              // バッファーに格納
              sb.AppendLine($"{dr.GetValue(0)}\t{dr.GetValue(1)}");
            }
          }
          // バッファーリターン
          return sb.ToString();
        });
        // 結果をコンソールに出力
        Console.WriteLine(str);
      }
      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


上の例はパターンを使ってもっとプログラムらしいなプログラムを作りました。

C#にはORMフレームワークというEntityフレームワークがあります。直接にデータベースに接続するため、Transaction設定してOpen、Closeなどを作成する場合もありますが、上みたいにデータベースを効率的に接続するため、パターンで組み立ているフレームワークがあります。それがORM(Object reference mapping)フレームワークのEntityフレームワークがあります。 프레임워크가 있습니다.

Entityフレームワークに関しては別の投稿で詳細に説明します。


ここまでC#でNugetを使い方(外部ライブラリ)とデータベース(MariaDB(Mysql))を使い方、そしてトランザクション(Transaction)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿