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-SQLxmlを扱ったことがないなーと思ったので、とりあえず基本の基本を押さえるために簡単なサンプルを書いてみましたというお話です。

まず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);

メソッド引数の「.」がカレント要素、「@」が属性の省略構文ですね。

このクエリを実行すると次の結果になります。

f:id:ichiroku11:20160819180203p:plain

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つのリクエストを処理するだけでプログラムが終了しています。

ichiroku11.hatenablog.jp

これではさすがにサーバとは言えないと思うので、もう少しサーバっぽくしたいと思います。具体的には次のことに対応していきます。

  • 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で名前付きパイプを試す · GitHub

おしまい。

.NETで名前付きパイプを試す(3) - クライアントサーバ間で送受信する

前回に続いてもう少しNamedPipeClientStreamとNamedPipeServerStreamを使った名前付きパイプを試します。

ichiroku11.hatenablog.jp

今回はクライアント側のプログラムからサーバ側のプログラムにリクエストを送ってレスポンスを受け取るようにしてみます。クライアント側、サーバ側それぞれのプログラムの動きは次のようにしたいと思います。

クライアント側のプログラムの動き

  1. サーバに接続
  2. サーバにリクエストを送信する
  3. サーバからレスポンスを受信する

サーバ側のプログラムの動き

  1. クライアントからの接続を待つ
  2. クライアントからリクエストを受信する
  3. リクエストを処理してレスポンスを作る
  4. クライアントにレスポンスを送信する

これでちょっとはクライアントサーバっぽくなってくるかなと。

クライアント側のプログラム

まずはクライアント側からです。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 = "おえういあ" }

今回も目的をクリアできたのでこのへんで。