C#でDBに接続する方法(SQL Server)

# はじめに
初投稿です。自分用メモの意味も込めて投稿します。

C#でDBのCURD操作ができるアプリを作りたくて、データベース接続方法調べました。
DB接続処理をサンプルを作って解説します。

何か間違っている所、あれば気軽に言ってください。

> 追記
最後に下記サンプルを静的クラスににまとめたサンプルコードを作成してみました。これをカスタマイズすれば、アプリ等で役立つと思うのでよかったら見てください。

## サンプル作成 手順
1. SQLServerにて、DBとユーザーを作成（今回は説明しない。）
1. プロジェクト立上げ（コンソールアプリ）
1. Nuget Packageライブラリのインストール（System.Data.SqlClient）
1.  データベース接続(サンプルコード)

## Productテーブル
今回使用したテーブルは下記です。

![スクリーンショット 2024-04-30 165709.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/bb73863d-8d4e-584f-0cc1-54d876794f8b.png)

テーブルデザイン
![テーブルデー座員.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/39dcae84-bb22-6fd6-51aa-95740616e87f.png)

サンプルコードを実際に試す場合は、上記のテーブルとDBにユーザーを作ってください。（試すだけならsaアカウントで十分かも？）を作ってください。

# プロジェクト立ち上げ
新しいプロジェクトの作成を選択。

![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/0f3d1219-352a-b539-5534-1aaa7a320a86.png)

コンソールアプリを選択。
![タイトルなし２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/7f47b985-d506-c3f5-0b1c-6e0bcab845c2.png)

アプリ名を決めて次へ
![タイトルなし4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/675313b8-afe4-01e4-888e-b96e655b567a.png)

アプリ作成。
![タイトルなし3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/173b7b48-f025-5cf4-5378-8f3a0ffeeef1.png)

# Nuget Packageライブラリのインストール（System.Data.SqlClient）
上記の手順でプロジェクトを立ち上げたら、下記の手順でNuGet Package管理画面を開き、「System.Data.SqlClient」をインストールします。

VisualStudioのタブ上部にあるメニューから「ツール」→「NuGetパッケージマネージャー」→「ソリューションのNuGetパッケージの管理」を選択。
![タイトルなし5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/9f78aea7-f7f4-6eef-0ddd-7f2dcc9aeb66.png)

開かれたタブの「参照」を選択。
↓
「System.Data.SqlClient」で検索。
↓
一番上部に表示された「System.Data.SqlClient」を選択。
![タイトルなし6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/94a09b8d-f333-eaf3-df38-041a19a1fa43.png)

作成したプロジェクト名にチェックを入れ、インストールする。
![タイトルなし7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/d46b5cf0-988c-ddb7-5178-ba52c4c4b59d.png)



# データベース接続(サンプルコード)
上記で準備は完了です。

接続を行うコードは下記です。今回はサンプルなのでProgram.csのmainメソッドにべた書きで作成しました。

下記を実行して、「接続成功。」と表示されれば成功です。

```C#:Program.cs
using System;
using System.Data.SqlClient; // 1　インポート

class Program
{
    static void Main()
    {
        // 2 接続文字列作成
        string connectionString = "Data Source=localhost;Initial Catalog=master;User ID=YourName;Password=YourPassword";
        // 接続成功時の処理
        try
        {
            // 3 SqlConnectionのインスタンスを作成
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                // 4 接続
                connection.Open();
                Console.WriteLine("接続成功。");
                
                // ここでデータベース操作を行う...
                
            }
        }
        // 接続失敗の処理
        catch (SqlException e)
        {
            Console.WriteLine("接続エラー: " + e.Message);
        }
        
        // 処理が終わってもすぐにコンソールが閉じないように 
        Console.ReadKey(true);
    }
}
```

### 解説
上記のコードの解説です。

#### 1 インポート
先程、インストールした「System.Data.SqlClient」のモジュールをインポートします。
```C#
using System.Data.SqlClient; // 1
```

#### 2 接続文字列作成
下記はデータベース接続に必要な情報を文字列変数に格納しています。
```C#
// 2 接続文字列作成
string connectionString = "Data Source=localhost;Initial Catalog=master;User ID=YourName;Password=YourPassword";
```

上記の中で「ID=YourName」と「Password=YourPassword」の部分をSQLServerのUser名とパスワードに書き換えれば、DBへ接続できるはずです。

#### 3 SqlConnectionのインスタンスを作成
下記でSqlConnectionインスタンスを生成します。この時、2で作った「connectionString（接続文字列）」を渡します。

usingの中でインスタンスを生成している理由はリソース解放のためで、これをすることにより、データベースの接続をとじる.Close()や.Dispose()を省略できます。

```C#
// 3 SqlConnectionのインスタンスを作成
using (SqlConnection connection = new SqlConnection(connectionString))
```

#### 4 接続
 .Open()でDBへの接続ができます。こと後にSQL文を生成し、CURD操作をします。

```C#
// 4 接続
connection.Open();
Console.WriteLine("接続成功。");
```

# CURD操作
ではココから、CURD操作を行う方法を説明します。手順的には上記の4で接続を確立した後、SQL文を作成、SqlCommandクラスのインスタンスにSQL文を渡して、実行という流れになります。

コードがごちゃごちゃするとわかりにくいので、メソッドに分けて作ります。
下記の作ったメソッドを「// ここでデータベース操作を行う...」のところで使えば、処理がうまく実行されるはずです。


### Read処理
DBから値をとってくるのでSQLのSELECT文を使用します。SqlConnection connectionはさっき接続を確立したインスタンスです。このSQL文とSqlConnectionインスタンスをSqlCommandインスタンスに渡して値を取得することができます。

カラムごとに取り出すには下記のように「SqlDataReader」インスタンスに渡し、While文で回してあげる必要があります。レコードの並び順で取り出すときの要素数が決まり、0から始まります。

※取り出すときに「int id = reader.GetInt32(0);」みたいな形でDBのデータ型に合わせて取得する必要があります。

```C#:Program.cs
private static void GetProduct(SqlConnection connection)
{
    // SQLクエリを実行するためのコマンドを作成
    string sql = "SELECT * FROM [WpfDB].[dbo].[Product]";
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                // データ型に合わせて値を取得する
                int id = reader.GetInt32(0);
                string name = reader.GetString(1);
                Console.WriteLine($"ID: {id}, Name: {name}");
            }
        }
    }
}
```

上記をMainメソッドで呼び出したときの結果。
![SELECT文実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/63e1a093-f332-afc3-93f4-127386a1ba6a.png)

### Create処理
今度はProductテーブルに新しくレコードを登録するInsert処理です。

Create処理の流れもRead処理と基本的に同じですが、違うと所が登録する値をSQLに直接書くのではなく、AddWithValueを使用し、パラメータとして入れる必要があります。

```C#:Program.cs
private static void InsertProduct(SqlConnection connection)
{
    string insertSql = "INSERT INTO [WpfDB].[dbo].[Product] (Name, Price) VALUES (@Value1, @Value2)";
    using (SqlCommand command = new SqlCommand(insertSql, connection))
    {
        // パラメータを設定
        command.Parameters.AddWithValue("@Value1", "test5"); // Column1の値
        command.Parameters.AddWithValue("@Value2", 69.5); // Column2の値

        // INSERT文を実行
        int rowsAffected = command.ExecuteNonQuery();
        Console.WriteLine($"Insert処理: {rowsAffected}");
    }
}
```

### Update処理
Update処理です。この後のDelete処理も同じですが、Create処理とほぼ同じで違う所は、SQL文のみです。

```C#:Program.cs
private static void UpdateProduct(SqlConnection connection)
{
    string updateSql = "UPDATE [WpfDB].[dbo].[Product] SET Name = @NewValue1, Price = @NewValue2  WHERE Id = @ConditionValue";
    using (SqlCommand updateCommand = new SqlCommand(updateSql, connection))
    {
        // パラメータを設定
        updateCommand.Parameters.AddWithValue("@NewValue1", "新しい値"); // 更新する値
        updateCommand.Parameters.AddWithValue("@NewValue2", 50); // 更新する値
        updateCommand.Parameters.AddWithValue("@ConditionValue", 1); // 条件の値

        // UPDATE文を実行
        int rowsAffected = updateCommand.ExecuteNonQuery();
        Console.WriteLine($"Update処理: {rowsAffected}");
    }
}
```

### Delete処理
Delete処理です。Create処理やUpdate処理とほぼ同じで違う所は、SQL文のみです。
```C#:Program.cs
private static void DeleteProduct(SqlConnection connection)
{
    string deleteSql = "DELETE FROM [WpfDB].[dbo].[Product] WHERE Id = @ConditionValue";
    using (SqlCommand deleteCommand = new SqlCommand(deleteSql, connection))
    {
        // パラメータを設定
        deleteCommand.Parameters.AddWithValue("@ConditionValue", 7); // 削除の条件となる値

        // DELETE文を実行
        int rowsAffected = deleteCommand.ExecuteNonQuery();
        Console.WriteLine($"削除処理: {rowsAffected}");
    }
}
```

上記の方法を使えば、DB接続を行うことができます。

# 静的メソッドにまとめる（追記）
上記で作った、DB接続、CURD操作を行う処理を静的クラスにまとめてみました。SQLの条件文の所だけを引数にすれば、アプリなどで入力を受け取って、CURD操作ができると思います。

やってることは先ほどのコードとさほど変わりません。

## データベース接続文字列を別ファイルへ
　githubなどにソースコードを上げる場合、データベースへの接続情報が公開されてしまうのはあまりよろしくないと思ったため、C#でそういった情報を分ける方法を調べました。下記の方法で作ったファイルを.gitignoreファイルなどでファイルを除外するだけでOKです。

 まず、NuGetパッケージマネージャーで「System.Configuration.ConfigurationManager」をインストールします。
 ![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/c483cd63-fc9b-454b-4c68-5845f9cf509f.png)

プロジェクトのルートディレクトリに「App.config」という名前でxmlファイルを作ります。
![タイトルなし2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627984/0c6cb470-ff3f-0abe-fa2a-23833d5d9c10.png)

下記をコピペで、addタグのvalueプロパティに接続文字列を追加
```XML:App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<appSettings>
		<add key="DatabaseConnectionString" value="Data Source=localhost;Initial Catalog=master;User ID=YourUser;Password=YourPassword" />
		<!-- 他の設定値もここに追加できます -->
	</appSettings>
</configuration>
```


## 設定ファイルから接続文字列を取得する方法
下記をインポートして、作ったApp.configから文字列を取得できます
```C#
using System.Configuration;
```

下記のようにすると先ほどValueに設定した文字列が取得できます。
「DatabaseConnectionString」はApp.configファイルのaddタグのkeyプロパティを指定。
```C#
string? connectionString = ConfigurationManager.AppSettings["DatabaseConnectionString"];
```

上記でファイルを分けることができます。

## CURD操作ができる静的クラスのサンプルコード

後はクラスファイルを作成し、下記のようにコードを記載しました。

下記は5つのメソッドを用意しています。
- Connect: データベースの接続を行うインスタンスを返す
- GetProduct: データ取得処理
- InsertProduct: 登録処理
- UpdateProduct: 更新処理
- DeleteProduct: 削除処理

上記のメソッドのConnectをSqlConnectionインスタンスを返すように作り、各CURD操作を担当している4つのメソッド内で呼び出し、DBに接続したSqlConnectionインスタンスを受け取ってSQL文を実行することができます。

下記をMainメソッド内で呼び出せばDBと接続し、CURD操作ができます。

Services/DB_Connection.cs
``` C#:DB_Connection.cs
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;

namespace SqlServerConnectApp.services
{
    public static class DB_Connection
    {
        /// <summary>
        /// データベース接続
        /// </summary>
        /// <returns></returns>
        private static SqlConnection? Connect()
        {
            string? connectionString = ConfigurationManager.AppSettings["DatabaseConnectionString"];

            try
            {
                SqlConnection connection = new SqlConnection(connectionString);
                {
                    connection.Open();
                    Console.WriteLine("接続成功。");
                    
                    return connection;
                }
            }
            catch (SqlException e)
            {
                Console.WriteLine("接続エラー: " + e.Message);
                return null;
            }
        }

        /// <summary>
        /// Productデータ取得（一覧データ）
        /// </summary>
        /// <param name="connection"></param>
        public static void GetProduct()
        {
            SqlConnection? connection = Connect();

            if (connection is not null)
            {
                try
                {
                    // SQLクエリを実行するためのコマンドを作成
                    string sql = "SELECT * FROM [WpfDB].[dbo].[Product]";
                    using (SqlCommand command = new SqlCommand(sql, connection))
                    {
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                int id = reader.GetInt32(0);
                                string name = reader.GetString(1);
                                Console.WriteLine($"ID: {id}, Name: {name}");
                            }
                        }
                    }
                }
                catch (SqlException e)
                {
                    Console.WriteLine("SQLエラー：" + e.Message);
                }
                finally
                {
                    connection?.Dispose();
                }
            }
            else
            {
                Console.WriteLine("データベース接続に失敗しました。");
            }
        }


        /// <summary>
        /// Product登録処理
        /// </summary>
        public static void InsertProduct()
        {
            SqlConnection? connection = Connect();

            if (connection is not null)
            {
                try
                {
                    string insertSql = "INSERT INTO [WpfDB].[dbo].[Product] (Name, Price) VALUES (@Value1, @Value2)";
                    using (SqlCommand command = new SqlCommand(insertSql, connection))
                    {
                        // パラメータを設定
                        command.Parameters.AddWithValue("@Value1", "test4"); // Column1の値
                        command.Parameters.AddWithValue("@Value2", 69); // Column2の値

                        // INSERT文を実行
                        int rowsAffected = command.ExecuteNonQuery();
                        Console.WriteLine($"Insert処理: {rowsAffected}");
                    }
                }
                catch (SqlException e)
                {
                    Console.WriteLine("SQLエラー：" + e.Message);
                }
                finally
                {
                    connection?.Dispose();
                }
            }
            else
            {
                Console.WriteLine("データベース接続に失敗しました。");
            }
        }

        /// <summary>
        /// Product更新処理
        /// </summary>
        public static void UpdateProduct()
        {
            SqlConnection? connection = Connect();

            if (connection is not null)
            {
                try
                {
                    string updateSql = "UPDATE [WpfDB].[dbo].[Product] SET Name = @NewValue1, Price = @NewValue2  WHERE Id = @ConditionValue";
                    using (SqlCommand updateCommand = new SqlCommand(updateSql, connection))
                    {
                        // パラメータを設定
                        updateCommand.Parameters.AddWithValue("@NewValue1", "Update"); // 更新する値
                        updateCommand.Parameters.AddWithValue("@NewValue2", 50); // 更新する値
                        updateCommand.Parameters.AddWithValue("@ConditionValue", 2); // 条件の値

                        // UPDATE文を実行
                        int rowsAffected = updateCommand.ExecuteNonQuery();
                        Console.WriteLine($"Update処理: {rowsAffected}");
                    }
                }
                catch (SqlException e)
                {
                    Console.WriteLine("SQLエラー: " + e.Message);
                }
                finally
                {
                    // connectionの接続切断 & リソース解放
                    connection?.Dispose();
                }

            }
            else
            {
                Console.WriteLine("データベース接続に失敗しました。");
            }

        }

        /// <summary>
        /// Productの削除処理
        /// </summary>
        public static void DeleteProduct()
        {
            SqlConnection? connection = Connect();

            if (connection is not null)
            {
                try
                {
                    string deleteSql = "DELETE FROM [WpfDB].[dbo].[Product] WHERE Id = @ConditionValue";
                    using (SqlCommand deleteCommand = new SqlCommand(deleteSql, connection))
                    {
                        // パラメータを設定
                        deleteCommand.Parameters.AddWithValue("@ConditionValue", 2); // 削除の条件となる値

                        // DELETE文を実行
                        int rowsAffected = deleteCommand.ExecuteNonQuery();
                        Console.WriteLine($"削除処理: {rowsAffected}");
                    }
                }catch (SqlException e)
                {
                    Console.WriteLine("SQLエラー: " + e.Message);
                }
                finally
                {
                    connection?.Dispose();
                }
                
            }
            else
            {
                Console.WriteLine("データベース接続に失敗しました。");
            }
        }
    }
}

```