パソコン練習日記

やってみたことを記録しています。Bloggerからこちらに引っ越して来ました。一部、画像がリンク切れしていますがひとつひとつ直すのは心が折れたのでそのままにします。

記憶域スペースダイレクト(Storage Spaces Direct)に使ったSSDが511MBしか認識されていないのでDISKPARTで対処

Azure Stack HCIでクラスタシェアードボリュームの記憶域スペースダイレクト(S2D)に使われたSSDを再利用(Azure Stack HCIを一から再構築したり、使っていたSSDを別のことに使いまわすなど)しようとすると511MBしか認識されてなくて、再利用にひと手間かかる。

こんな感じ

Windowsの"ディスクの管理"でみるとこんな感じ。

そして、記憶域保護パーティションと出ている通り?右クリックから"フォーマット"や”ボリュームの削除”はグレーアウトしてて(キャプチャ撮り忘れたけど)使えない。

"ディスクの管理"ではない別の方法で解決する必要があるのだが

"DISKPART"ツールで"Clean"を使ってすべてのパーティションを削除すれば解決する

Diskpartで見たSSDもこんな感じ。

 

Disk0~2のSSD3本をDISKPARTのCleanでディスクの全パーティション削除

PS C:\Users\Administrator\> diskpart
Microsoft DiskPart version 10.0.25398.1

Copyright (C) Microsoft Corporation.
On computer: ASHCI02

DISKPART> list disk

Disk ### Status Size Free Dyn Gpt
-------- ------------- ------- ------- --- ---
Disk 0 Online 238 GB 0 B *
Disk 1 Online 238 GB 0 B *
Disk2 Online 476GB 1024KB *

DISKPART> select disk 0
Disk O is now the selected disk.

DISKPART> clean
DiscPart succeeded in cleaning the disk.

DISKPART> select disc 1
Disc 1 is now the selected disc.

DISKPART> clean

DiskPart succeeded in cleaning the disk.

DISKPART> select disk 2

Disk 2 is now the selected disk.

DISKPART> clean

DiskPart succeeded in cleaning the disk.

DISKPART> list disk

  Disk ###  Status         Size     Free     Dyn      Gpt
  --------  -------------  -------  -------  ---      ---
* Disk 0    Online          238 GB    238 GB             
* Disk 1    Online          238 GB    238 GB             
* Disk 2    Online          var GB    var GB             
* Disk3     Online          var GB   var KB              *

DISKPART> exit

Leaving DiskPart...
PS C:\Users\Administrator>



SQL Server Linuxコンテナを使ったところ、日本語が?に文字化けしたので調べてみたら

プログラムの単体テスト用DB(SQL Server)として、LinuxコンテナのSQL Serverを使ってみてるのですが、テストデータを何にしようかな、とSQL Server Management StudioからポチポチInsertしてたところ、文字化け。

もう夜遅いので今度調べよう。と、寝ました。

で、今日。調査開始。

まずはSQLserver2022 On LinuxのDockerコンテナをPowershellで起動

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=P@`$`$w0rd" -p 1433:1433 --name sqlsv22 --hostname sqlsv -d mcr.microsoft.com/mssql/server:2022-latest


DB作って、テーブル作って、日本語のデータをInsert。
テーブル構成も日本語のデータが登録できればいいので適当。

CREATE DATABASE testdb;

use testdb;

CREATE TABLE test (
    id INT PRIMARY KEY,
    message VARCHAR(255)
);

INSERT INTO test (id, message) VALUES (1, 'おはよう');

INSERT INTO test (id, message) VALUES (2, 'Good Morning');

select * from test;

とりあえず、再現OK。"おはよう"は文字化けして????になってる

で、SQLServerを使っていてそんな感じになった記憶が無いので、日本語バージョンでインストールしていたからかな?と思い、WindowsServer上にSQL Server2022をインストールしてみて同じようにやってみた。
そしたらやっぱり文字化けしていない。

じゃあ、英語バージョンで同じようにやったら文字化けするんだろうなぁ、と思いWindowsServer上に英語バージョンのSQL Server2022をインストールし同じように実行したところ、文字化けしていない。なんで?

調べたところ、2012年のSQL Server Japan Support Team Blogに「非 Unicode から Unicode への変換ができなかったから」、「データベースの照合順序がLatin1_General_CI_ASの場合」にデータが?になるとのこと。

確かにコンテナのSQL Serverインスタンスは照合順序が"SQL_Latin1_General_CP1_CI_AS"

なので、試しにCREATE DATABASEステートメントでデータを入れるDBの照合順序をJapanese_CI_ASに指定してみる。

CREATE DATABASE [testdb] 
COLLATE Japanese_CI_AS;

USE testdb;

DROP TABLE IF EXISTS test;

CREATE TABLE test (
    id INT PRIMARY KEY,
    message NVARCHAR(255)
);

INSERT INTO test (id, message) VALUES (1, N'おはよう');
INSERT INTO test (id, message) VALUES (2, N'Good Morning');

select * from test;

すると文字化けしなくなった

もう一個試し。いま稼働しているコンテナを削除し、

docker runでカスタム SQL Server照合順序を指定(-e "MSSQL_COLLATION=Japanese_CI_AS")を追加

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=P@`$`$w0rd" -e "MSSQL_COLLATION=Japanese_CI_AS" -p 1433:1433 --name sqlsv22 --hostname sqlsv -d mcr.microsoft.com/mssql/server:2022-latest

"Japanese_CI_AS"になっていることを確認

それからこのままCreate Database、Create Table、データInsert。

文字化けしてないことを確認

で、もうひとつ実験。

いま稼働しているコンテナを削除し、もう一回、カスタム SQL Server照合順序を指定せずにDocker run

Create  Databaseでも照合順序を指定せず、テーブルのカラムmessageの型を"VARCHAR"ではなく、"NVARCHAR"に指定。日本語をInsertの際、Unicode文字列として処理されるようNプレフィックスを付ける

CREATE DATABASE testdb;

use testdb;

CREATE TABLE test (
    id INT PRIMARY KEY,
    message NVARCHAR(255)
);

INSERT INTO test (id, message) VALUES (1, N'おはよう');
INSERT INTO test (id, message) VALUES (2, 'Good Morning');

select * from test;

これも文字化けしない。

 

じゃあ、今後どれで行こうかというとDocker runの時のカスタム SQL Server照合順序指定でいいかな

Windows11 HomeをProにアップグレードしたあと、初期化するとまたHomeに戻るの?Windows11 Home Editionのパソコンを買ってしまったんだけど、どうやったらMicrosoft365アカウント専用端末にできるの?

年末にBingポータルにMicrosoft StoreでSurfaceセールの広告があったので覗いてみたところ、メモリ32GBでCPUがi7のSurfacePro8が税込み約26万円で売っている。

これは安いのでは?高いけど

今使っているノートPCが4年目に入っていることと、メモリが16GB載っているけど常時10GB使っていて、場合によってはVMがメモリ不足で起動しないことも出てきているため、来年あたり買い替えかなぁと思っていたのですが・・・。カメラも死んでるし。顔認証使えない・・・

30万円以内だと少額減価償却資産として買える。メモリ32GB欲しい。

14インチ以内、タッチパネル、メモリ32GB。欲しい。高いけど

来年、メモリ32GBのSurfacePro9が30万円以内でセールされるタイミングで買うか、このタイミングで買うか・・・。もしかしたらほかに良いの安いのが出てくるかも。

でも、2024年は家から出て作業することが増えそうなんだよなぁ。そうするとやっぱ、メモリ不足は足をひっぱる気がする。

延長保証付きでポチりました。28万円

と、パッケージを眺めると

Windows 11 Home

Home?Surface Proって言ってるのにHomeなの?うそでしょ。マジで?見てなかったわ

とりあえず初期設定するか・・・

Microsoftアカウントしか使えないんだけど。365アカウントで設定したいんだけど?

いちおう、道がないか探してみる。

どこ開いてもMicrosoftアカウントでしか設定できない。ていうか、ローカルアカウントも許されない。ていうかネットにつなげないと次へ進めなかったし、絶対にMicrosoftアカウントという強い意志を感じる初期設定画面

いちおう念のため365アカウントを入力してみるが、「Microsoftアカウントじゃないじゃん」で門前払い

しようがないので、あとでProにしてから365アカウントで作り直すか・・・と思いながらMicrosoftアカウントで初期設定。

でも、これをこのあとProエディションにするとして、Microsoftアカウントのプロファイルとか要らないから初期化してMicrosoft365アカウントで初期設定し直したいんだけど、初期化したらまたHomeに戻るんじゃないの?

初期設定でProにアップグレードするような場面も無かったし

初期設定終わってログイン。あたりまえだけどやっぱりWindows Home

とりあえずProにアップグレード。ライセンスは持っているので、Microsoft Storeからの購入ではなくプロダクトキーの変更

再起動後

Proになった

ここからが本題。この↓MicrosoftアカウントはPCから抹殺したいので一度初期化し、365アカウントで初期設定すること

つまり、初期設定でProバージョンになってくれないとまたMicrosoftアカウントで行うことになる。目的を達成できない。

それどころか下手したら、適用したProのプロダクトキーがもう使えないゴミになる可能性もあるんじゃないか、と思いながら初期化

初期化完了。初期化設定へ。これさっきのHomeと同じだぞ。大丈夫か?

たのむぞ

きたーーーー。これProだよコレ

よっしゃぁぁぁ

 

結論

HomeからProにアップグレードしたデバイスは初期化してもProのままにできる

 

Visual Studioとコンテナを使ったCICDお勉強の記録4

ここの続き

テスト結果の値を標準出力させる

VSCodeとかEclipseとかだとどういう感じなのか知らないが、VisualStudioのテストエクスプローラだとこんな表示。結果がどういう値だったのか見たいけど出てこないので、見れない。

なので、出るようにしてみる。

MSTestでは、TestContext.WriteLine メソッドを使用してテスト中にカスタムメッセージを出力する。とのこと。

まずは、TestContext プロパティをテストクラスに追加

public TestContext TestContext { get; set; }

値をログに出力する処理を追加

TestContext.WriteLine("Expected date: {0}, Actual date: {1}", testDate, actualDate);
TestContext.WriteLine("Expected num: {0}, Actual num: {1}", testNum, actualNum);

なんかCS8618の警告が出る。クラスがnull非許容プロパティを持っているにもかかわらず、コンストラクター内でそのプロパティが明示的に初期化されていない場合に発生するとのこと。

GPTに聞いたら、こうやったらいいよ。と以下のアドバイスと説明もらった。

TestContext プロパティに default! を代入しています。default! は、null許容警告を無視するためのもので、プロパティがテスト実行時にMSTestフレームワークによって適切に初期化されることを前提としています。! はnull許容コンテキストでnull非許容型のデフォルト値を強制するために使用されます。

でも、クラス内に他にも初期化されていない非nullプロパティがある場合は、同様の方法で初期化を行う必要があったりするからちゃんと全体を見てね。とのこと。ふむ、全然わからん。

public TestContext TestContext { get; set; } = default!; // デフォルト値を設定

また、TestContext.WriteLine の呼び出しを reader.Read() が成功した後の if ブロック内に移動させたほうが、using ブロックの適切なスコープ内で行うようになるからAssert処理もちょっと変えたほうが良いよ。とのことで変更。

 

今回、最終的なテストコードは以下な感じに

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using SqlServerOperation;
using System.Reflection.PortableExecutable;

namespace DatabaseOperationTests
{
    [TestClass]
    public class DataInserterTests
    {
        private readonly string server = "192.168.10.13";
        private readonly string username = "sa";
        private readonly string password = "P@$$w0rd";
        private readonly string testDatabase = "TestDB";
        private readonly string connectionString;

        public TestContext TestContext { get; set; }= default!; // デフォルト値を設定

        public DataInserterTests()
        {
            connectionString = $"Server={server};Database=master;User Id={username};Password={password};";
        }

        [TestInitialize]
        public void Setup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                

                // データベースが存在するか確認し、存在する場合は削除
                string checkDbExistenceQuery = $"SELECT database_id FROM sys.databases WHERE Name = '{testDatabase}'";
                using (var command = new SqlCommand(checkDbExistenceQuery, connection))
                {
                    var databaseId = command.ExecuteScalar();
                    if (databaseId != null)
                    {
                        // データベースへのすべての接続を切断
                        string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                        using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                        {
                            alterCommand.ExecuteNonQuery();
                        }

                        string dropDbQuery = $"DROP DATABASE {testDatabase}";
                        using (var dropCommand = new SqlCommand(dropDbQuery, connection))
                        {
                            dropCommand.ExecuteNonQuery();
                        }
                    }
                }

                // テスト用データベースの作成
                string createDbQuery = $"CREATE DATABASE {testDatabase}";
                using (var command = new SqlCommand(createDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }

                // テスト用テーブルの作成
                string useDbQuery = $"USE {testDatabase}";
                string createTableQuery = "CREATE TABLE TestTable (date DATETIME NOT NULL, num INT NOT NULL)";
                using (var command = new SqlCommand($"{useDbQuery}; {createTableQuery}", connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }


        [TestMethod]
        public void InsertData_InsertsCorrectly()
        {
            // Arrange
            var testConnectionString = $"Server={server};Database={testDatabase};User Id={username};Password={password};";
            var dataInserter = new DataInserter(server, testDatabase, username, password);
            var testDate = new DateTime(2021, 1, 1);
            int testNum = 1;

            // Act
            dataInserter.InsertData(testDate, testNum);

            // Assert
            using (var connection = new SqlConnection(testConnectionString))
            {
                connection.Open();
                string selectQuery = "SELECT date, num FROM TestTable WHERE num = @Value2";
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value2", testNum);
                    using (var reader = command.ExecuteReader())
                    {
                        bool isRead = reader.Read();
                        Assert.IsTrue(isRead); // レコードが存在することを確認

                        if (isRead) // 読み取りが成功した場合のみログを出力
                        {
                            DateTime actualDate = reader.GetDateTime(0);
                            int actualNum = reader.GetInt32(1);

                            Assert.AreEqual(testDate, actualDate); // 日付が一致することを確認
                            Assert.AreEqual(testNum, actualNum); // num が一致することを確認

                            // 値がどうだったかをログに出力
                            TestContext.WriteLine("Expected date: {0}, Actual date: {1}", testDate, actualDate);
                            TestContext.WriteLine("Expected num: {0}, Actual num: {1}", testNum, actualNum);
                        }
                    }
                }
            }
        }

        [TestCleanup]
        public void Cleanup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                // データベースへのすべての接続を切断
                string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                {
                    alterCommand.ExecuteNonQuery();
                }

                // テスト用データベースの削除
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var command = new SqlCommand(dropDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

これで、テスト時の値が見えるように

 

 

Visual Studioとコンテナを使ったCICDお勉強の記録3 単体テストの前後処理追加

ここの続き

このままテストを繰り返すとデータが溜まっていってテスト整合性保てなくなる場合があることや、テスト用のDBはコンテナで使い捨てにすることを踏まえ、前処理後処理をいれる。

前処理はこんな感じにしておく。DBが存在したら、どういう使われ方したかわからんからDropしてからDBを作成。そのあと、テスト用のテーブル作成。

[TestInitialize]
public void Setup()
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        // データベースが存在するか確認し、存在する場合は削除
        string checkDbExistenceQuery = $"SELECT database_id FROM sys.databases WHERE Name = '{testDatabase}'";
        using (var command = new SqlCommand(checkDbExistenceQuery, connection))
        {
            var databaseId = command.ExecuteScalar();
            if (databaseId != null)
            {
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var dropCommand = new SqlCommand(dropDbQuery, connection))
                {
                    dropCommand.ExecuteNonQuery();
                }
            }
        }

        // テスト用データベースの作成
        string createDbQuery = $"CREATE DATABASE {testDatabase}";
        using (var command = new SqlCommand(createDbQuery, connection))
        {
            command.ExecuteNonQuery();
        }

        // テスト用テーブルの作成
        string useDbQuery = $"USE {testDatabase}";
        string createTableQuery = "CREATE TABLE TestTable (date DATETIME NOT NULL, num INT NOT NULL)";
        using (var command = new SqlCommand($"{useDbQuery}; {createTableQuery}", connection))
        {
            command.ExecuteNonQuery();
        }
    }
}

後処理でも、使ったDBを削除するお片付け処理を入れとく

[TestCleanup]
        public void Cleanup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                // テスト用データベースの削除
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var command = new SqlCommand(dropDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

いちおう、動作確認。ブレークポイント置いてテストデバッグ

DB消えた

DB作成された

テーブル作成された。

もちろん、データなし。

テストも正常に終了

ということでもう一回テスト実行したら、前処理でDBがDropできなくてエラー。なんかがDB使っているからDropできんよ。とのこと
TestCleanup method DatabaseOperationTests.DataInserterTests.Cleanup threw exception. System.Data.SqlClient.SqlException: Cannot drop database "TestDB" because it is currently in use..

前処理と後処理それぞれのDrop処理の前にコネクション強制切断処理を追加
// データベースへのすべての接続を切断
        string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
        using (var alterCommand = new SqlCommand(alterDbQuery, connection))
        {
            alterCommand.ExecuteNonQuery();
        }

これでとりあえず、テストを何回繰り返してもエラーにならなくなった

 

今時点で、最終的なテストプログラムは以下

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using SqlServerOperation;

namespace DatabaseOperationTests
{
    [TestClass]
    public class DataInserterTests
    {
        private readonly string server = "192.168.10.13";
        private readonly string username = "sa";
        private readonly string password = "P@$$w0rd";
        private readonly string testDatabase = "TestDB";
        private readonly string connectionString;

        public DataInserterTests()
        {
            connectionString = $"Server={server};Database=master;User Id={username};Password={password};";
        }

        [TestInitialize]
        public void Setup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                

                // データベースが存在するか確認し、存在する場合は削除
                string checkDbExistenceQuery = $"SELECT database_id FROM sys.databases WHERE Name = '{testDatabase}'";
                using (var command = new SqlCommand(checkDbExistenceQuery, connection))
                {
                    var databaseId = command.ExecuteScalar();
                    if (databaseId != null)
                    {
                        // データベースへのすべての接続を切断
                        string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                        using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                        {
                            alterCommand.ExecuteNonQuery();
                        }

                        string dropDbQuery = $"DROP DATABASE {testDatabase}";
                        using (var dropCommand = new SqlCommand(dropDbQuery, connection))
                        {
                            dropCommand.ExecuteNonQuery();
                        }
                    }
                }

                // テスト用データベースの作成
                string createDbQuery = $"CREATE DATABASE {testDatabase}";
                using (var command = new SqlCommand(createDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }

                // テスト用テーブルの作成
                string useDbQuery = $"USE {testDatabase}";
                string createTableQuery = "CREATE TABLE TestTable (date DATETIME NOT NULL, num INT NOT NULL)";
                using (var command = new SqlCommand($"{useDbQuery}; {createTableQuery}", connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

 

        [TestMethod]
        public void InsertData_InsertsCorrectly()
        {
            // Arrange
            var testConnectionString = $"Server={server};Database={testDatabase};User Id={username};Password={password};";
            var dataInserter = new DataInserter(server, testDatabase, username, password);
            var testDate = new DateTime(2021, 1, 1);
            int testNum = 1;

            // Act
            dataInserter.InsertData(testDate, testNum);

            // Assert
            using (var connection = new SqlConnection(testConnectionString))
            {
                connection.Open();
                string selectQuery = "SELECT date, num FROM TestTable WHERE num = @Value2";
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value2", testNum);
                    using (var reader = command.ExecuteReader())
                    {
                        Assert.IsTrue(reader.Read()); // レコードが存在することを確認
                        Assert.AreEqual(testDate, reader.GetDateTime(0)); // 日付が一致することを確認
                        Assert.AreEqual(testNum, reader.GetInt32(1)); // num が一致することを確認
                    }
                }
            }
        }

        [TestCleanup]
        public void Cleanup()
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                // データベースへのすべての接続を切断
                string alterDbQuery = $"ALTER DATABASE {testDatabase} SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                using (var alterCommand = new SqlCommand(alterDbQuery, connection))
                {
                    alterCommand.ExecuteNonQuery();
                }

                // テスト用データベースの削除
                string dropDbQuery = $"DROP DATABASE {testDatabase}";
                using (var command = new SqlCommand(dropDbQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
            }
        }

    }
}

 

 

Visual Studioとコンテナを使ったCICDお勉強の記録2 とりあえず単体テストが動くようになるまで

これの続き

今度は、とりあえず単体テストが動くようになるまでをやっていく

単体テストを追加

ソリューションを右クリックし、「追加」 > 「新しいプロジェクト」を選択

MSTest、NUnit、xUnitとかある。 どれ使えばいいのかわからん。

Bingチャットに相談してみて、「ユニットテストの基本的な概念は同じであるため、一つを学べば他のフレームワークも理解しやすくなるし、とりあえずどれでもいいと思うよ。」という結論になり、MSTestで行くことにした。

Testプロジェクトの名前は"DatabaseOperationTests"にしとく。

メインプログラムのプロジェクトの名前を"SQLDB_SimpleTests"していたので、どちらがテストプロジェクトなのかわかりづらい。失敗したと思った。

単体テストプロジェクトからメインプログラムのプロジェクト参照追加

テストプロジェクトを右クリック、「追加」>「プロジェクト参照」

テスト対象のプロジェクト"SQLDB_SimpleTests"を参照

テスト対象のプログラムのnamespace名をテストのほうでusing

単体テストとテスト対象プログラムのリファクタリング

テストプログラムをとりあえず以下な感じに

テスト対象のプログラムが今はProgramクラスのMainメソッドだけど、データをInsertするプログラムなので、DataInserterクラスに変更する前提でテストを作る。


using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using SqlServerOperation;

namespace DatabaseOperationTests
{
    [TestClass]
    public class DataInserterTests
    {
        private readonly string server = "192.168.10.13";
        private readonly string username = "sa";
        private readonly string password = "P@$$w0rd";
        private readonly string testDatabase = "TestDB";
        private readonly string connectionString;

        public DataInserterTests()
        {
            connectionString = $"Server={server};Database=master;User Id={username};Password={password};";
        }

 

        [TestMethod]
        public void InsertData_InsertsCorrectly()
        {
            // Arrange
            var testConnectionString = $"Server={server};Database={testDatabase};User Id={username};Password={password};";
            var dataInserter = new DataInserter(server, testDatabase, username, password);
            var testDate = new DateTime(2021, 1, 1);
            int testNum = 1;

            // Act
            dataInserter.InsertData(testDate, testNum);

            // Assert
            using (var connection = new SqlConnection(testConnectionString))
            {
                connection.Open();
                string selectQuery = "SELECT date, num FROM TestTable WHERE num = @Value2";
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value2", testNum);
                    using (var reader = command.ExecuteReader())
                    {
                        Assert.IsTrue(reader.Read()); // レコードが存在することを確認
                        Assert.AreEqual(testDate, reader.GetDateTime(0)); // 日付が一致することを確認
                        Assert.AreEqual(testNum, reader.GetInt32(1)); // num が一致することを確認
                    }
                }
            }
        }
    }
}

テスト対象プログラムもテストし易いようにリファクタリング

using System;
using System.Data.SqlClient;

namespace SqlServerOperation
{
    class Program
    {
        static void Main(string[] args)
        {
            var dataInserter = new DataInserter("192.168.10.13,1433", "TestDB", "sa", "P@$$w0rd");
            dataInserter.InsertData(DateTime.Now, 1);
        }
    }

    public class DataInserter
    {
        private readonly string _connectionString;

        public DataInserter(string server, string database, string username, string password)
        {
            _connectionString = $"Server={server};Database={database};User Id={username};Password={password};";
        }

        public void InsertData(DateTime date, int num)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                connection.Open();

                string insertQuery = "INSERT INTO TestTable (date, num) VALUES (@Value1, @Value2)";
                using (var command = new SqlCommand(insertQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value1", date);
                    command.Parameters.AddWithValue("@Value2", num);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

テスト実行前に今DBのテーブルにあるデータを削除

USE [TestDB]
GO
 
/****** Object:  Table [dbo].[TestTable]    Script Date: 2024/01/01 14:29:32 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestTable]') AND type in (N'U'))
DROP TABLE [dbo].[TestTable]
GO
 
/****** Object:  Table [dbo].[TestTable]    Script Date: 2024/01/01 14:29:32 ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[TestTable](
[date] [datetime] NOT NULL,
[num] [int] NOT NULL
) ON [PRIMARY]
GO

テスト実行

テスト完了

DBも一応確認。テストデータがちゃんと登録されている

 

Visual Studioとコンテナを使ったCICDお勉強の記録1 とりあえずDBに接続するまで

最終的にAKSにデプロイするまでのCICDを構築していきたいと思っています。

まずは、コンテナで動かすまで

 

開発デバイスの環境

コンテナを使っていきたいので、また、開発はVisual Studioを使う。

そのために以下をインストールしておく。

  • Visual Studio2022
  • Docker desktop
プログラム作成

とりあえず、コードはこんな感じにしておく。単体テスト書く段階になったらリファクタリングしていくつもり。

現在時刻と数字の"1"をDBにInsertするだけの簡単なプログラム。

using System;
using System.Data.SqlClient;

namespace SqlServerOperation
{
    class Program
    {
        static void Main(string[] args)
        {
            string server = "192.168.10.13,1433"; // ホスト名と動的に割り当てられたポート
            string database = "TestDB"; // データベース名
            string username = "sa";
            string password = "P@$$w0rd";

            string connectionString = $"Server={server};Database={database};User Id={username};Password={password};";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();

                //現在日時取得
                DateTime now = DateTime.Now;

                // テーブルに挿入する数値
                int testNum = 1;

                //データ登録
                string insertQuery = "INSERT INTO TestTable (date,num) VALUES (@Value1, @Value2)";
                using (SqlCommand command = new SqlCommand(insertQuery, connection))
                {
                    command.Parameters.AddWithValue("@Value1", now);
                    command.Parameters.AddWithValue("@Value2", testNum);
                    command.ExecuteNonQuery();
                }

                Console.WriteLine("Data inserted successfully.");
            }
        }
    }
}

using System.Data.SqlClientしているのに、SqlConnectionとかでCS1069エラーになっている。

調べたところ、System.Data.SqlClientをNugetしないといけないらしい。

感覚的には、using System.Data.SqlClientのところがエラーになるんじゃないの?って思ってしまうんだけど、そういうもんなのかなぁ。と思いながらNuget

とりあえず、エラー無くなった。

 

Dockerコンテナ化対応(Docker Compose)

開発デバイス上でのDockerコンテナを使ったCICD環境は、Docker Cmposeが良いとのことなので、

プロジェクトを右クリックし、「追加」 > 「コンテナーオーケストレーターのサポート」を選択

Windowsライセンスのこととか考えたくないのでLinux

 

docker-compose.ymlと

Dockerfileが自動で作成

 

デバッグ実行が、"Docker Compose"に変わった。

DBはまだないけど、デバッグ実行。

あたりまえだけどDB接続エラー。それはいいとして、コンテナを見てみる

コンテナが作られて動いてる

っていうか、コンテナイメージのほうを見てみると脆弱性が21あるんだけど

いちおう、12日前に作られたイメージっぽいけど・・・

dockerfileの中で、apt update upgradeしようとしたけど

 

.NETの公式DockerイメージはMicrosoftが提供し、セキュリティパッチを含む定期的な更新が行われています。一般的に、これらのイメージに対して apt-get update や apt-get upgrade を実行することは、特に必要がない限り推奨されません。


とのことなので、止めとくことにした。

 

デバッグのとき接続する用のDB作成

このプログラムが接続しに行くDBを作成。

UbuntuベースのSQLServer Dockerコンテナを使う。

Microsoft SQL Server - Ubuntu based images by Microsoft | Docker Hub

 

単体テスト用はDocker Composeを使ってコンテナを作るつもりだが、デバッグ用は単純にコンテナを稼働させておくだけにしておく。

 

SQLServer2022のイメージを使う。まずはdocker pull

docker pull mcr.microsoft.com/mssql/server:2022-latest
Microsoft Learnを参考にしつつ、

learn.microsoft.com

今のところ特に何か理由があるわけではないけど、SQL Server照合順序を"Japanese_CI_AS"に、コンテナの名前を"sqlserver_express_debug"にして、1433ポートをマッピング

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=P@$$w0rd" -e "MSSQL_COLLATION=Japanese_CI_AS" -p 1433:1433 --name sqlserver_express_debug -d mcr.microsoft.com/mssql/server:2022-latest

コンテナが稼働したので、

SQLServer Management Studioで接続

 プログラムで定義した通りのDB、テーブルを作成。

DBも作ったので、デバッグを実行。エラーなしで処理終了

標準出力がコンテナのログとして出力されていることを確認

データがDBに登録されたことを確認


いったんこれで区切って、次は単体テストを作ることを行う