DateTime.AddMonthsとDateTime.AddYearsのメモ
ちょっとした発見。
DateTime.AddMonthsメソッドは、結果の月にその日が存在しない場合はその月の末日に調整してくれます。
文章にするといまいちなのでコードで例を。
// 2016/08/31 + 1ヶ月 // 2016年9月は30日までなので結果は2016/09/30になる new DateTime(2016, 8, 31).AddMonths(1); // 結果 // 2016/09/30 0:00:00
DateTime.AddYearsも同じ動き。
// 2016/02/29 + 1年 // 2017年2月は28日までなので結果は2017/02/08になる new DateTime(2016, 2, 29).AddYears(1); // 結果 // 2017/02/28 0:00:00
知らなかった。
MSDNにも書いてありました。
結果として得られる日が有効な結果として得られる、月 1 日でないと、結果として得られる月の最終有効日が使用されます。例では、3 月 31 日 + 1 か月 = 4 月 30 日です。
ちょっと訳が微妙な部分もあるので英語のほうがわかりやすいかなって気もします。
If the resulting day is not a valid day in the resulting month, the last valid day of the resulting month is used. For example, March 31st + 1 month = April 30th, and March 31st - 1 month = February 28 for a non-leap year and February 29 for a leap year.
T-SQL - BETWEENのメモ
BETWEENは自分ではあまり使わないのでたまに見かけるとあれ?含むの?含まないの?どっちだっけ?ってなります。
カップル専用アプリのことではないです。
between A and B
A以上かつB以下。AもBも含む。
select * from (values(1), (2), (3), (4), (5)) as t(c) where c between 2 and 4; -- 結果 /* c ----------- 2 3 4 */
not between A and B
Aより小さいまたはBより大きい。
select * from (values(1), (2), (3), (4), (5)) as t(c) where c not between 2 and 4; -- 結果 /* c ----------- 1 5 */
一度試しておくと記憶に残るかな?と。まあ忘れてたらこれを見る。
SQL Server - xmlデータ型のメソッド(query、value、nodes)を試す
そういえばT-SQLでxmlを扱ったことがないなーと思ったので、とりあえず基本の基本を押さえるために簡単なサンプルを書いてみましたというお話です。
まずxmlデータ型のメソッドはこれだけあります。
そのうちよく使うんじゃないかなと勝手に思うqueryメソッド、valueメソッド、nodesメソッドを使ってみます。
構文 | 簡単な説明 | 詳細 |
---|---|---|
query('XQuery') |
xmlからxmlを取得する | query() メソッド (xml データ型) |
value('XQuery', 'SQLType') |
xmlからスカラー値を取得する | value() メソッド (xml データ型) |
nodes('XQuery') as Table(Column) |
xmlからxmlの行セットを取得する | nodes() メソッド (xml データ型) |
それぞれのメソッドの引数にはXQueryというxmlに問い合わせる文字列を渡します。この文字列は要素の位置をさすパスになることが多いのかなと思います。またXQueryのパスには省略構文があります。省略構文はよく使うのかなと思います。
queryメソッド、valueメソッド、nodesメソッドを使ってみる
ドキュメントを読むよりクエリを見た方がわかりやすいので早速見ていきましょう。
-- xmlを変数に用意 declare @xml xml = N' <root> <item id="1">abc</item> <item id="3">efg</item> <item id="2">xyz</item> <item id="5">lmn</item> </root>'; -- 変数@xmlに各メソッドを使ってみる select item.query('.') as Item, -- item要素自身をxmlとして取得 item.value('@id', 'tinyint') as Id, -- item要素のid属性の値をtinyintとして取得 item.value('.', 'nvarchar(3)') as Content -- item要素の中身をnvarchar(3)として取得 from -- root要素の子のitem要素をt(item)というテーブル(カラム)の行セットとして取得 @xml.nodes('/root/item') as t(item);
メソッド引数の「.」がカレント要素、「@」が属性の省略構文ですね。
このクエリを実行すると次の結果になります。
cross applyでnodesメソッド
上記ではxml型の変数に対してnodesメソッドを呼び出しました。テーブルのカラムにxml型のデータがある場合はどうしたらいいかというと、from句でcross applyを使ってnodesメソッドを呼び出すといいみたいです。
-- from句のcross applyでnodesメソッドを呼び出す select item.query('.') as Item, item.value('@id', 'tinyint') as Id, item.value('.', 'nvarchar(3)') as Content from (values(@xml)) as src(xml) -- srcテーブルのxmlカラムにxmlデータがあるとして cross apply xml.nodes('/root/item') as t(item);
このクエリを実行すると1つめのクエリと同じ結果が得られます。
これでとりあえずとっかかりができたかなと思います。
.NETで名前付きパイプを試す(4) - 複数のクライアントに対応したサーバにする
もう少しNamedPipeClientStreamとNamedPipeServerStreamを試してみます。
前回のサーバは1つのクライアントの1つのリクエストを処理するだけでプログラムが終了しています。
これではさすがにサーバとは言えないと思うので、もう少しサーバっぽくしたいと思います。具体的には次のことに対応していきます。
- 1つのサーバで複数のクライアントからのリクエストを処理できるようにする
- 複数のリクエストを並列処理できるようにする
また前回までは「プロセス間」通信ということを確認したかったのでプロジェクトを分けましたが、今回は実行結果がわかりやすいように1つのプロジェクトにしてスレッド間で試しています。
複数のクライアントに対応したサーバ
まずはサーバのプログラムを終了しないようにして、複数のクライアントからのリクエストを処理できるようにします。
Serverメソッド内の処理全体をwhile文で囲んでいます。当然なのかもしれませんが、クライアントの NamedPipeClientStreamで接続が閉じられるとサーバのNamedPipeServerStreamも接続が閉じられるようで、NamedPipeServerStreamのインスタンスはwhile文の中で都度生成しています。ストリームのインスタンスは使い回すものじゃないのかなと。
serverIdは識別用です。後半でサーバを複数作るときに使います。
// サーバ private static async Task Server(int serverId, Func<Message, Message> process) { while (true) { using (var stream = new NamedPipeServerStream("testpipe")) { // クライアントからの接続を待つ Console.WriteLine($"Server#{serverId} waiting"); await stream.WaitForConnectionAsync(); Console.WriteLine($"Server#{serverId} connected"); // クライアントからリクエストを受信する var request = default(Message); using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) { request = reader.ReadObject<Message>(); } Console.WriteLine($"Server#{serverId} {nameof(request)}: {request}"); // リクエストを処理してレスポンスを作る var response = process(request); // クライアントにレスポンスを送信する Console.WriteLine($"Server#{serverId} {nameof(response)}: {response}"); using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) { writer.WriteObject(response); } } } } // サーバでの処理(Serverメソッドの引数に渡す) // Contentの文字列を逆順にする処理 private static Message ServerProcess(Message message) { return new Message { Id = message.Id, Content = new string(message.Content.Reverse().ToArray()), }; }
複数のクライアントから接続
続いてクライアントです。clientIdは識別用です。
// クライアント private static async Task<Message> Client(int clientId, Message request) { using (var stream = new NamedPipeClientStream("testpipe")) { // サーバに接続 Console.WriteLine($"Client#{clientId} connecting"); await stream.ConnectAsync(); Console.WriteLine($"Client#{clientId} connected"); // サーバにリクエストを送信する Console.WriteLine($"Client#{clientId} {nameof(request)}: {request}"); using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) { writer.WriteObject(request); } // サーバからレスポンスを受信する var response = default(Message); using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) { response = reader.ReadObject<Message>(); } Console.WriteLine($"Client#{clientId} {nameof(response)}: {response}"); return response; } }
実行する
クライアントを2つとサーバを1つ動かしてみます。
サーバが1つなのでクライアントは同時に接続できていないことが確認できます。サーバではリクエストを1つずつ順に処理していることもわかります。
static void Main(string[] args) { Task.WaitAll(new[] { // クライアント Client(1, new Message { Id = 10, Content = "あいうえお", }), Client(2, new Message { Id = 20, Content = "かきくけこ", }), // サーバ Server(1, ServerProcess), }); } // 実行結果(実行するごとに変わる部分もありますが) /* Client#1 connecting Client#2 connecting Server#1 waiting Client#2 connected // Client#2が接続(Client#1はまだ接続待ち) Server#1 connected Client#2 request: { Id = 20, Content = "かきくけこ" } Server#1 request: { Id = 20, Content = "かきくけこ" } Server#1 response: { Id = 20, Content = "こけくきか" } Client#2 response: { Id = 20, Content = "こけくきか" } Server#1 waiting Server#1 connected Client#1 connected // ここでやっとClient#1が接続できる Client#1 request: { Id = 10, Content = "あいうえお" } Server#1 request: { Id = 10, Content = "あいうえお" } Server#1 response: { Id = 10, Content = "おえういあ" } Client#1 response: { Id = 10, Content = "おえういあ" } Server#1 waiting */
複数のリクエストを並列に処理するサーバ
さらにサーバでは複数のリクエストを並列に処理できるようにしてみます。
NamedPipeServerStreamのコンストラクタにはサーバインスタンスの最大数を指定できるオーバーロードがあります。
このコンストラクタを使って複数のサーバインスタンスを用意できるように修正します。今回は最大数を2にしてみます。(ちなみに上記のサーバはサーバインスタンスの最大数は1でした。)
// サーバ private static async Task Server(int serverId, Func<Message, Message> process) { while (true) { // サーバインスタンスを2に // using (var stream = new NamedPipeServerStream("testpipe")) { using (var stream = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 2)) { // この部分は上記のServerメソッドと同じなので省略 } } }
実行する
クライアントとサーバを2つずつ動かしてみます。それぞれのサーバでリクエストが処理されていることを確認できます。
static void Main(string[] args) { Task.WaitAll(new[] { // クライアント Client(1, new Message { Id = 10, Content = "あいうえお", }), Client(2, new Message { Id = 20, Content = "かきくけこ", }), // サーバ Server(1, ServerProcess), Server(2, ServerProcess), }); } // 実行結果(実行するごとに変わる部分もありますが) /* Client#1 connecting Client#2 connecting Server#1 waiting Client#1 connected // Client#1が接続 Server#1 connected Server#2 waiting Server#2 connected Client#2 connected // Client#2が接続 Client#2 request: { Id = 20, Content = "かきくけこ" } Server#2 request: { Id = 20, Content = "かきくけこ" } Server#2 response: { Id = 20, Content = "こけくきか" } Client#2 response: { Id = 20, Content = "こけくきか" } Server#2 waiting Client#1 request: { Id = 10, Content = "あいうえお" } Server#1 request: { Id = 10, Content = "あいうえお" } Server#1 response: { Id = 10, Content = "おえういあ" } Client#1 response: { Id = 10, Content = "おえういあ" } Server#1 waiting */
これでNamedPipeClientStreamとNamedPipeServerStreamの基本的な使い方は把握できたかなーと思います。
ソース一式はこちらに。
おしまい。
.NETで名前付きパイプを試す(3) - クライアントサーバ間で送受信する
前回に続いてもう少しNamedPipeClientStreamとNamedPipeServerStreamを使った名前付きパイプを試します。
今回はクライアント側のプログラムからサーバ側のプログラムにリクエストを送ってレスポンスを受け取るようにしてみます。クライアント側、サーバ側それぞれのプログラムの動きは次のようにしたいと思います。
クライアント側のプログラムの動き
- サーバに接続
- サーバにリクエストを送信する
- サーバからレスポンスを受信する
サーバ側のプログラムの動き
- クライアントからの接続を待つ
- クライアントからリクエストを受信する
- リクエストを処理してレスポンスを作る
- クライアントにレスポンスを送信する
これでちょっとはクライアントサーバっぽくなってくるかなと。
クライアント側のプログラム
まずはクライアント側からです。ReadObjectとWriteObject、Messageは前回のエントリで使ったものと同じです。またBinaryWriter、BinaryReaderのusingの部分でパイプのストリームを閉じないようにしています。
// クライアント側 class Program { private static async Task<Message> Client(Message request) { using (var stream = new NamedPipeClientStream("testpipe")) { // 1. サーバに接続 await stream.ConnectAsync(); // 2. サーバにリクエストを送信する Console.WriteLine($"{nameof(request)}: {request}"); using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) { writer.WriteObject(request); } // 3. サーバからレスポンスを受信する var response = default(Message); using(var reader = new BinaryReader(stream, Encoding.UTF8, true)) { response = reader.ReadObject<Message>(); } Console.WriteLine($"{nameof(response)}: {response}"); return response; } } static void Main(string[] args) { Console.WriteLine("Client"); Client(new Message { Id = 1, Content = "あいうえお", }).Wait(); } }
サーバ側のプログラム
続いてサーバ側です。リクエストからレスポンスを作る部分は、受け取ったリクエストの文字列をそのまま返すのも寂しいので文字列を逆順にしてレスポンスを作っています。
// サーバ側 class Program { private static async Task Server(Func<Message, Message> process) { using (var stream = new NamedPipeServerStream("testpipe")) { // 1. クライアントからの接続を待つ await stream.WaitForConnectionAsync(); // 2. クライアントからリクエストを受信する var request = default(Message); using(var reader = new BinaryReader(stream, Encoding.UTF8, true)) { request = reader.ReadObject<Message>(); } Console.WriteLine($"{nameof(request)}: {request}"); // 3. リクエストを処理してレスポンスを作る var response = process(request); // 4. クライアントにレスポンスを送信する Console.WriteLine($"{nameof(response)}: {response}"); using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) { writer.WriteObject(response); } } } // Contentの文字列を逆順にする処理 private static Message Process(Message message) { return new Message { Id = message.Id, Content = new string(message.Content.Reverse().ToArray()), }; } static void Main(string[] args) { Console.WriteLine("Server"); Server(Process).Wait(); } }
実行する
2つのプログラムを実行すると送受信できることを確認できます。
// クライアント側の実行結果 Client request: { Id = 1, Content = "あいうえお" } response: { Id = 1, Content = "おえういあ" } // サーバ側の実行結果 Server request: { Id = 1, Content = "あいうえお" } response: { Id = 1, Content = "おえういあ" }
今回も目的をクリアできたのでこのへんで。