Quantcast
Channel: @jsakamoto
Viewing all 146 articles
Browse latest View live

ASP.NET SignalR を使った Web アプリに ngrok 経由でアクセスすると、サーバー側呼び出しが動作していない

$
0
0
ASP.NET SignalR を使ったリアルタイム Web サイト/アプリを開発中の話。

※ちなみに「SignalR」とは、「Node.js でいうところの Socket.io」に相当する、WebSocket などを活用した双方向リアルタイム通信を実現するための ASP.NET 用のライブラリ。

ngrok + SignalR が不調...前回の投稿で取り上げた、ローカル開発 Web サーバーを "中継" して、インターネット上からアクセス可能にするサービス「ngrok」。

この ngrok、公式サイトの説明によれば「Websocket にも対応してるはずだよ」とのこと。

なので、SignalR を使った Web アプリ開発においても、その動作確認などの目的で ngrok を経由してインターネット上からアクセスするようにしても動作するだろうと予想された。

しかし残念ながらそうはいかず、期待どおりに動作しないケースがあった。

ブラウザ側の SiganlR クライアントコードから、サーバー側の Hub メソッドの呼び出しが、なぜかサーバー側に到達しないのだ。

サーバー側 C# コードが以下のように、常に文字列 "Boo" を返す Foo メソッドを持つ SignalR Hub 実装だったとして、using Microsoft.AspNet.SignalR;
...
public class FooBarHub : Hub {
public string Foo() {
return "Bar";
}
}ブラウザ側 JavaScript コードから以下のように上記 Hub の Foo メソッドを呼び出し、その戻り値を表示するとする。let conn = $.hubConnection();
let hub = conn.createHubProxy("FooBarHub");
conn.start();
...
// 何かの DOM イベントのハンドラにて、下記を実行
hub.invoke('Foo')
.then(res => console.log(res);これらコードを、ローカル開発環境で実行したり、Azure Web Apps に配置してインターネット上からアクセスするぶんには、当たり前ではあるが、ちゃんとブラウザの開発コンソールに (Foo メソッドの戻り値である)「Bar」と表示される。


ところが、である。


このローカル開発環境では普通に動作するこのコードが、ngrok コマンドを起動してのインターネット上からのアクセスで試すと、どういうわけか Foo メソッド呼び出し結果のコールバックが呼び出されないのだ。

Visual Studio にてサーバー側の Foo メソッドにブレークポイントを設定して待ち構えても、もちろんローカル開発環境ではちゃんとブレークするが、ngrok 経由ではまったくもってブレークしない。

サーバー側からの push 通知だけは動作ちなみに、「conn.start()」メソッドの戻り値である jQuery の Promise オブジェクトについてコールバック関数を実装して様子を見てみると、ngrok 経由でもちゃんと接続成功のコールバックが発生している模様。

しかも、別の要因で発火させたサーバー側からの push 送信を購読していた場合、その push 送信ハンドラは、ngrok 経由の場合でも、正しく呼び出されるのだ。

ちょっと不思議な挙動である。

回避策それはさておき、予想としては、ngrok の WebSocket 対応と、IIS Express や ASP.NET SignalR における実装との関連で、なにか相性問題のようなことが起きているのかもしれない。
※ちなみに、ローカル開発環境および ngrok 経由の各々で、トランスポート層は WebSocket が使用されていることを確認済み。


そこで回避策として、ブラウザ側にて SignalR のサーバー側への接続を開くにあたり、使用するプロトコルを自動で決めさせずに、もっとも古典的であろうロングポーリングで接続するよう明示的に指定するようにしてみた。

コードとしては以下のとおり。
SignalR 接続オブジェクトの start メソッドを呼び出す際に、トランスポート層をロングポーリングに限定指定した設定を渡すようにする。let conn = $.hubConnection();
let hub = conn.createHubProxy("FooBarHub");
conn.start({
transport: 'longPolling'
});こうすると、ngrok 経由でのインターネット上からのアクセスでも正常に動作するようになった。

ちなみに SignalR 接続オブジェクトの start メソッドにて、トランスポート層指定にどんな選択肢があるかについては以下が参考になる。

http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#transport

補足なお、この回避策は、あくまでも ngrok を利用してローカル開発中の Web アプリの動作確認が目的である。
よって上記のようにロングポーリングが指定されたコードがそのままリリースされてしまうのは "事故" であり、そのような事態は避けておきたい。

そこで自分のコードでは、現在のページの URL を見て、そのホスト名が ".ngrok.io" で終わっているかどうかを正規表現で判定して、もしそうである場合に限り、トランスポート層のロングポーリング限定指定を行う実装とした。
 

ASP.NET Core 1.0 のアプリケーション構成 (オプション) を動的に読み取る

$
0
0
ASP.NET Core 1.0 ではアプリケーション構成は型付けされるASP.NET Core 1.0 からは、アプリケーション構成(オプション)を読み取る方法が大幅に改築された。

まずはオプション値を表すクラスを記述し、そのオプション値の型にアプリケーション構成を逆シリアライズして、そうして生成されたオプション値を DI 機構を経由して受け取る、という "型付け" された形態になった。

詳しくは下記が大変参考になる。

ASP.NET Core 1.0 でオプションを柔軟に扱えるようになった話
http://blog.shibayan.jp/entry/20160529/1464456800

型がついてまわるので、文字列でアプリケーション構成のエントリ名を記述するのとは異なり、インテリセンスが効くのはもちろん、オプション値を参照している個所の特定や
(同僚が書いたコード又は自分が3ヵ月前に書いたコードを保守するときは、これがとても心強い)、エントリ名の変更といった仕様変更も楽勝である。

※余談になるが、自分の場合、旧来の web.config 中の appSettings 構成セクションへのアクセスを静的型付けするために、SwissKnife.T4.AppSettings NuGet パッケージを多用してきた。

また、ASP.NET MVC コントローラクラスなどでは、DI 機構を通じてオプション値を入手するため、コントローラの単体テストの記述のしやすさに大きく貢献することだろう。

しかし実行時にエントリ名が決まることもあるこのように、ASP.NET Core 1.0 から型付けを前提にアクセスすることになったアプリケーション構成(オプション)。
しかし、そうやたらとあるわけではないにせよ、まれに、実行時にエントリ名が決定されるような要件、言い換えると、文字列でエントリ名を指定してオプション値を入手しなければならないケースがある。

さきのしばやん先生のブログ記事でも触れられているが、"裏技的な感じ" はするものの、 IConfiguration を DI に追加すればこの要件に対応できると予想された。

ということで、試してみた。

IConfiguration を DI に登録してみるDI 機構にて注入される "サービス" オブジェクトを DI 機構に登録するには、Startup クラスの ConfigureServices メソッド内でその登録処理を行う。

そこで、Startup クラスのコンストラクタにて別途 Configuration プロパティに初期化みの、IConfiguration インターフェースを備える "構成オブジェクト" を、ConfigureServices メソッド内で DI 機構に登録する。

具体的なコードは下記のとおり。public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IConfiguration>(this.Configuration);
}以上で、ASP.NET MVC コントローラクラスなどで、コンストラクタ引数に IConfiguration 型の引数を記述しておくと、フレームワーク側にてこのコントローラクラスをインスタンス化するときに、先に DI 機構に登録されていた IConfiguration 型に対するオブジェクトが、この引数に渡される次第である。

具体的なコード例としては下記のとおり。public class HomeController : Controller
{
private IConfiguration Config { get; }

public HomeController(IConfiguration config)
{
this.Config = config;
}

public IActionResult Index()
{
return View(this.Config["foo"]);
}
...このコードを実行してみたところ、appsettings.json に「{"foo":"bar"}」が記述されていると、「this.Config["foo"]」は文字列 "bar" を返すことを確認できた。

このように、裏技感は否めないものの、実行時にエントリ名を示す文字列を組み立てて "動的" にアプリケーション構成を読み取ることが、DI 機構に IConfiguration オブジェクトを直接登録してしまうことで実現できた。

階層化されたオプションは?ところで、appsettings.json が以下のように記述されていた場合、{
"layer1":{
"layer2":{
"key1":"foo",
"key2":"bar"
}
}
}いちばん末端の "key2" エントリの値 ( この例では "bar" ) を IConfiguration インターフェース経由で読み取るにはどうすればよいか?

先の HomeController の例でいうと、this.Config.GetSection("layer1").GetSection("layer2")["key2"]というコードで取得可能ではある。

しかしこれにはもっと簡易に書ける短縮構文があって、先の JSON の子要素をたどるのにコロン「:」でメンバ名を区切って記述すればそれでアクセス可能だ。

具体的にはthis.Config["layer1:layer2:key2"]で OK である。

GetValue 拡張メソッドも便利ここまでは IConfiguration のインデクサ構文でアプリケーション構成を参照していた。

しかし参照方法としてはほかにもあり、「GetValue」という拡張メソッドも用意されている。

ここまでの例はすべて文字列としてアプリケーション構成を読み取っていたが、GetValue 拡張メソッドを使えば、int や DateTime としてアプリケーション構成を読み取ってそれらの型で返してくれたり、指定した名前のエントリが実在しなかった場合の既定値を併せて指定できたりなど、便利なことも多い。

Microsoft.Extensions.Configuration 名前空間をインポート(using)すれば GetValue 拡張メソッドが使えるようになるので、いちどインテリセンスでどんなオーバーロードバージョンがあるのか眺めてみるのもよいかもしれない。

英字の大文字小文字は区別されない模様あと、これらアプリケーション構成(オプション)機構は、そのエントリ名に関して、大文字小文字を区別しないようだ。

先の例でいえば、this.Config["layer1:layer2:key2"]もthis.Config["LAYER1:Layer2:kEY2"]も、同じ結果を返す。

これは型付けしたオプション用クラスについても同じで、オプション用クラスのプロパティ名と、appsettings.json に記載されているプロパティ名とが、英大文字小文字が違っていてもバインドされる。

まとめIConfiguration オブジェクトを DI 機構に登録することで、実行時にエントリ名文字列を構築してアプリケーション構成を読み取るような要件にも対応できることが確かめられた。

一般的な用途では、基本的には型付けされたオプション機構を用いたほうが利が大きい。
とはいえ、場合・要件によってはここで紹介した技法も役に立つだろう。

以上、ご参考まで。
 

Windows10 の音声アシスタント「コルタナ」に話しかけて操縦するDCモーターカー

$
0
0
まずは以下の動画を参照いただきたい。



これは何かというと、Windows 10 Home が稼働している極小パソコン(スティック PC)を搭載した、タミヤ模型のショベルカーキットを改造した DC モーターカーである。

そして、この極小スティック PC 上で動作している音声アシスタント「コルタナ」に話しかけることで、この DC モーターカーを操縦する、という技術デモンストレーションなのだ。

「Hey Cortana, please go ahead.」と呼べば、(しばし認識に考え込んだのちに)「Okay, all right, I'll move forward.」と返事の上、DC モーターを制御して前進する、という次第。

また、この動画外の外部のコンピューターなどは使用しておらず (※まぁ、コルタナがクラウドに頼って動作しているらしいけど) 、スタンドアロンで動作しているのもポイントである。



制作のきっかけRaspberry Pi で遠隔操縦するリモコンカーは作ってあったもともとは、昨年 2015年 8月に開催された「Code 2015 in 定山渓温泉」で披露する目的で制作した、Raspberry Pi を載せて Wi-Fi 経由で遠隔操縦するリモートコントロールカーが発端だ。

以下に同年 2015年 10 月に北海道は北見市で開催された IT 勉強会「Ohotech (オホテック) 特盛#13」で再発表したときのスライドを示す。



これは、
「Raspberry Pi 上でも C# で書いた Web アプリサーバーが普通に動くよ」
「もちろん GPIO も簡単にいじれるよ」
「なので、Web アプリ開発技術にもう少し盛ることで、スマホの Web ブラウザから操縦できるラジコンカーも作れちゃうよ」
という技術デモンストレーションである。

Windows10 にコルタナ搭載、そして自作アプリとの連携そうこうしているうちに、2015年11月に入り、Windows 10 の大規模アップデートにて、音声アシスタント「コルタナ」が Windows 上に登場する。

続けて、Channel9 の特集「VS魂」シリーズのひとつにて、自作の UWP アプリがコルタナと連携できることを知る。



つまり、自作 UWP アプリからコルタナに音声コマンドを登録しておくことで、コルタナに話しかけた内容がその音声コマンドに合致したら、自作 UWP アプリを呼び出してくれる、という仕掛けが作れるとのこと。

そこで思いついたのが、
「あ、Raspberry Pi じゃなくても、最近はやりのスティック PC なら、あの DC モーターカーに乗るんじゃね?」
「スティック PC ってことは、Windows 10 が動くってことで、コルタナの音声コマンドで操縦できるのでは...!」
という流れ。

これを実証したのが今回の「コルタナ・カー」である次第。

アプリケーション連携の構造モーター制御Raspbery Pi をはじめとしたボードコンピュータと異なり、スティック PC にはモーター制御に使えるような GPIO の持ち合わせはない。

そこで USB 接続した Aruduino Nano と組み合わせることに。

スティック PC から Arduino Nano へはシリアル通信で指令を送る。
Arduino Nano 上ではその指令を待機しているスケッチが動いていて、指令に応じて GPIO の On/Off を制御し、これをモータードライバにつないで動かすようにした。

コルタナ連携さて、コルタナから自作 UWP アプリを呼び出してもらえることはわかったが、その自作 UWP から Arduino Nano へのシリアル通信の実装がうまくいかない。

「UWP からはシリアル通信は使えない」という情報もあれば、Windows.Devices.SerialCommunication.SerialDevice でいける、という情報もある。

後者を少し試してみたがどうにもうまく実装できない。

時間も押していたので、もうひとつ通常の Windows コンソールアプリを作成してしのぐことに。

古典的で強引な手法だが、UWP アプリが所定のルールで書き出すファイルを、コンソールアプリが監視することで、UWP アプリからコンソールアプリへ通知を送るようにするという方法だ。

こうしてコンソールアプリは普通にクラシックな API でシリアル通信できるので、これで最終的に全体が完成した。


※上図をクリックするとアニメーション表示が開きます。
日本語だとうまく認識しない?さて最後の最後に動作確認をしていると、どうにも、日本語だとなかなかうまく音声コマンドが認識されない。

コルタナがまったく聞き取れていないというわけではないが、なぜか自作 UWP の呼び出しにならずに Web 検索にいってしまったりする。

認識率はちゃんと数字で計測しなかったのでわからないが、感覚的には 10 回試して 1回成功するような程度の認識。

そしてこれまたどういうわけか、言語を英語に切り替えて試してみたところ、今度はかなりの確度で認識してくれる。

というわけで、冒頭デモ動画のとおり、英語版でのお披露目となった。

いまいち不人気?さてこの「コルタナ・カー」、先日開催されたオープンソースカンファレンス 2016 Hokkaido の CLR/H ブースに展示したのだが、ほかの展示物のほうが人気を集めていた。

会場は雑音が多く、「Hey, Cortana!」と呼びかけてもそもそも反応しないという状況で、ライブでデモができなかった、ということも人気不足の一因だったかもしれない。

また、「音声コマンドで操縦する DC モーターカー」は昨今ではとりたてて珍しいものではない、ということも原因にあったかもと思う。

確かに、同じ Windows でも、コルタナに頼らずとも、古くから Windows に備わってる音声認識エンジンを使う方法もある。

Raspberry Pi でも音声認識は普通にできるので、ネット上で検索すると、実際に Raspberry Pi を使って音声認識で操縦する DC モーターカーの作例も見つけることができる。

とはいえ、このデモンストレーションは
「コルタナさんと天気やニュース、予定の話に交えて、モーター制御もしちゃう」
という、あくまでも「コルタナと連携して」というところがポイントであるわけで。

その点をもっと訴求するべきだったかと反省である。

おまけ: 一番苦労したのは...余談だが、この「コルタナ・カー」の制作にあたり苦労したのは物理的な工作関連。

とくに今回は、スティック PC にモーター制御をお願いする Aruduino Nano音声を拾ってもらうためのマイク (今回はマイク内蔵の Web カメラ製品で代替、カメラ機能は使ってない)コルタナさんの声を再生するための音声再生デバイス (HDMIからも出力はされてるけど、スタンドアロン動作させる以上、HDMI 接続の TV とかにつなぐわけにもいかないので)といった周辺機器を USB でつなぐことになったのだが、その接続のための USB ケーブルが長すぎるのが一番苦労した。

USB ケーブルが長すぎて、タミヤ模型のショベルカーという筐体に収まりきらなかったり、とぐろを巻いてケーブル重量だけでもずいぶん重くなったり、と大変だったのである。

そこで
USB ケーブルはニッパーでぶった切って短く切り詰めてから、USB の線 x 4本 (外部シールドもあわせると5本) をちまちま半田付けしなおす
という地道な手作業を行わざるを得ず、これが一番の苦労だった。


ライトニングトーク用カウントダウンタイマーを更新 - Window10の仮想デスクトップ対応も

$
0
0
先月開催されたオープンソースカンファレンス 2016 Hokkaido の閉会式ライトニングトークに登壇させていただた際にも使用した、Windows 上で動作するアプリ「ライトニングトーク用カウントダウンタイマー」。
d0079457_1262977.png

このライトニングトーク用カウントダウンタイマーを 1年ぶりくらいにバージョンアップした。

主な更新内容は、出遅れ感があるが Windows 10 の仮想デスクトップ対応と、これまで対応を伸ばし伸ばしにしてしまっていたマルチモニタ対応。
Windows 10 仮想デスクトップ対応Windows 10 からは、ほかのアプリを追加インストールすることなく、OS標準の機能で MacOSX にもあるような仮想デスクトップ機能が使えるようになった。

しかし、ライトニングトーク用カウントダウンタイマーの旧バージョンでは、この Windows 10 仮想デスクトップ機能に対応していなかった。
すなわち、とある仮想デスクトップでライトニングトーク用カウントダウンタイマーを表示してのちに、別の仮想デスクトップに表示を切り替えてしまうと、タイマー画面が見えなくなってしまう、という欠点があった。

今回のバージョンアップにより、仮想デスクトップを切り替えても、常に現在表示されているデスクトップ上にタイマー画面を表示するようになった。
d0079457_11451780.gif



なお、この Windows 10 仮想デスクトップ対応にあたり、Grabacr07 さんの下記記事を参照した。

Windows 10 の仮想デスクトップを制御しようとして失敗した話
http://grabacr.net/archives/5601
(※補足すると、このあとの続編で完遂している)

この記事があったおかげで、ライトニングトーク用カウントダウンタイマーを Windows 10 仮想デスクトップに対応させることができた。
多謝。
マルチモニタ対応旧バージョンでは、時間終了時の「Time Up」画面が、常にプライマリディスプレイにのみ表示されていた。

Windows のマルチモニタ機能にて、手元のノートPC画面をプライマリ、プロジェクタへの出力をセカンダリモニタとしてライトニングトークを行っていた時に、時間満了しても、演者のノートPC画面内に「Time Up」と表示されるだけで、聴衆が見ているプロジェクタ画面には何の変化もなく、時間切れになった感がまったく伝わっていなかった。

今回のバージョンアップにより、マルチモニタが有効になっている場合は、すべてのモニタで各々に「Time Up」画面を表示するように改善した。
d0079457_11452220.gif


そのほかの改善点そのほかにも、以下の改善を実施している。設定変更内容が記憶され、次回起動時に復元するようになった (なぜ今までできなかったのか...)「Time Up」画面のフォントをかっこよく (Quantico font を使用)
最新版の入手最新版は ClickOnceGet 経由でインストールできる。

Lightning Talks Countdown Timer
http://clickonceget.azurewebsites.net/app/LTCountDownTimer/detail

あるいは GitHub から Zip 版をダウンロードして、自分で解凍・配置してもよい。

https://github.com/jsakamoto/Lightning-Talks-Countdown-Timer/releases

すでに ClickOnceGet 経由でインストール済みであれば、次回、ライトニングトーク用カウントダウンタイマーを起動するときに、最新版に更新するかどうか自動で尋ねられるはずだ。

ClickOnceGet 上で公開しているファイルを更新するにあたり、本当は、コード署名も GitHub 認証用の鍵で署名したかったが、鍵が変わってしまうと、ClickOnce による自動更新で「署名に使われている鍵が変わってるけど、これってマズくない!?」みたいな警告がでて感じよくないので諦めた。

おまけライトニングトーク用カウントダウンタイマー、はじめて開発に着手して以来、7年が経っていることに気付くなど。

いちばんはじめは Windows Mobile (!) で動く Web アプリとして、
そして今となっては黒歴史の Windows ガジェットとして、
その後ようやく Windows Form アプリとして仕立て直し、今日に至る。

バージョンアップも亀のように遅いけど、自分の手ごまの中では長い間保守継続し使い続けている数少ない長寿アプリのひとつである。

元の TypeScript ソースまでたどれるソースマップを伴う、JavaScript ファイルの Bundle & Minify 化の方法

$
0
0
前提条件Visual Studio (Visual Studio Code ではなく)上での Webアプリ開発、とくに TypeScript によるクライアント側スクリプト実装の話。

現在自分が抱えているプロダクトでは、TypeScript から JavaScript への変換は、1つの .ts ファイルにつき 1つの .js ファイルを生成する構成 ― プロジェクト内の .ts ファイル群からひとつの .js ファイルを生成するのではなく ― としている。

そしてこれら .ts から変換した .js ファイル群は、所定のグループごとに、ファイルサイズ削減を目的とした縮小化(Minify) を適用し、かつ、ひとつの .js ファイルへの単一化 (Bundle) を実施している。

Bundle & Minify を実施する具体的な手順としては、ASP.NET MVC が備える Bundle & Minify 機能 (Web アプリ実行時に動的に Bundle & Minify される)、ないしは、Visual Studio 拡張の「Bundle & Minifier」を使ってきた。

これまで実現できていなかったことだがしかし、この構成でひとつだけあきらめていたことがあった。

それは Bundle & Minify 化された JavaScript コードからの、
いちばん大元の TypeScript ファイルへたどることのできる
ソースマップファイル (.map) の生成
である。

TypeScript コンパイラは当然のことながら、.map ファイルを生成することができる。
実際、自分のプロダクトでも .map ファイルを生成するよう tsconfig.json を構成してある。

しかし前述の Bundle & Minify 化の手法だと、その Bundle & Minify 処理過程で新たに生成される .map ファイルは、Bundle & Minify 前の JavaScript コードにたどれるまでとなる。
TypeScript ソースコードにまで到達しないのだ。

なくてもどうにかならなくもないけど...もっとも TypeScript コンパイラが生成する JavaScript コードは、元の TypeScript コードと割と一対一で対応してたりする。
なので、Bundle & Minify 前の JavaScript コードにまで到達できれば、人力的探索で TypeScript ソースコードまでたどれなくもない。

また、IDE やブラウザが備えるデバッガ上で TypeScript ソースコード上でブレークポイントを張ったりしたいときも、実行時と開発時とで、HTML 上で読み込む JavaScript ファイルを違える工夫で回避できる。
つまりは、開発時は Bundle & Minify 前の JavaScript ファイル ― これには TypeScript コンパイラが生成した .map ファイルがついている ― を HTML で読み込むように、実行時とは構成を変更できるようにするのだ。

しかしそうはいっても、クラッシュ通知が来てるときに、そのスタックトレースが元の TypeScript コードにおけるソース行を直接指していないとなると、気分が焦っているのに余計な手間がかかって何とも煩わしい。


そこで重い腰を上げて、Bundle & Minify 化するときに TypeScript ソースコードまでたどれる .map ファイルを生成する方法はないものか、調べてみることにした。

Gulp 使えば楽勝だった!あれこれ検索したり試行錯誤を繰り返してる中で、Node.js 上で動作するタスク自動化ツールのひとつ、Gulp を使えばあっさり実現できることがわかった。

以下はその手順である。

必要なツールやパッケージのインストールNode.js のパッケージマネージャ、npm を使って、まずはプロジェクトフォルダ上で Gulp をインストール。
>npm init
...(package.json 新規作成のためにあれこれ問いに応答する)...
>npm install --save-dev gulpさらに、Gulp を使ってファイルを結合、つまり Bundle を実施するために gulp-concat、難読化を伴う JavaScript コードの縮小化のために gulp-uglify、いくつかの並行タスクを束ねるために merge-stream、そして今回の命題のキモとなる、ソースマップの生成のために gulp-sourcemapsを続けてインストールする。>npm install --save-dev gulp-concat
>npm install --save-dev gulp-uglify
>npm install --save-dev merge-stream
>npm install --save-dev gulp-sourcemapsBundle & Minify の設定ファイルそして話が前後するが、前述の Visual Studio 拡張にて Bundle & Minify 化するときに、どの JavaScript ファイルを Bundle & Minify するのかの設定ファイルとして、bundleconfig.json という JSON 形式のファイルで記述してある (内容は下記のような感じ)。[
{
"outputFileName": "lib.min.js",
"inputFiles":[
"lib/**/*.js
]
},
{
"outputFileName": "bundle.min.js",
"inputFiles":[
"src/**/*.js
]
}
]gulpfile.js の作成 - まずは最低限 Bundle & Minify するところまで前述の bundleconfig.json を読み込んで Bunlde & Minify するよう、gulpfile.js を組んでみる。
ソースマップ生成は抜きにしてまずは Bundle & Minify ができるところまでだと、こんな感じだ。
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var merge = require('merge-stream');

// bundleconfig.json の内容を読み込む
var bundleconfig = require('./bundleconfig.json');

gulp.task('min:js', function () {
// bundleconfig.json の記述から1要素ごとに、
// Bunlde & Minify 化のタスクに射影する
var tasks = bundleconfig.map(function (bundle) {
var task = gulp.src(bundle.inputFiles, { base: "." })
.pipe(concat(bundle.outputFileName)) // concat で連結(Bundle)
.pipe(uglify()) // uglify で難読&縮小化(Minify)
.pipe(gulp.dest("."));
return task;
});
// それらタスクを束ねて返して実行
return merge(tasks);
});ここまでの状態で、プロジェクトフォルダをカレントディレクトリとしたコマンドプロンプト上で「.\node_modules\.bin\gulp min:js」などと実行すると、bundleconfig.json の指定に従って Bundle & Minify 化が実行されるはずだ。

ただしここまでの記述ではまだ、ソースマップは生成されない。
ソースマップ生成ではいよいよ、ソースマップの生成を組み込む。

単に Bundle & Minify 前のソースコードまでたどれるだけであれば、ネットで検索したときによく見かける下記の記述 (太字の箇所) を書き足せばよい。
var gulp = require('gulp');
...
var sourcemaps = require('gulp-sourcemaps');
...
gulp.task('min:js', function () {
// bundleconfig.json の記述から1要素ごとに、
// Bunlde & Minify 化のタスクに射影する
var tasks = bundleconfig.map(function (bundle) {
var task = gulp.src(bundle.inputFiles, { base: "." })
.pipe(sourcemaps.init()) // ソースマップ生成機構を仕掛ける
.pipe(concat(bundle.outputFileName)) // concat で連結(Bundle)
.pipe(uglify()) // uglify で難読&縮小化(Minify)
.pipe(gulp.dest("."))
.pipe(sourcemaps.write("./")) // ソースマップを出力
.pipe(gulp.dest("."));
return task;
});
...だが今回の最終目標は、Bundle & Minify 前のコードまでではなく、さらにその前の TypeScript コードにまでたどれるソースマップの生成だ。

そのために、gulp-sourcemaps に対して、以下の指示を書き足す。

...
var task = gulp.src(bundle.inputFiles, { base: "." })
.pipe(sourcemaps.init({loadMaps: true})) // ソースマップ生成機構を仕掛ける
...上記のとおり、gulp-sourcemaps の init メソッド呼び出しの際に、
「{loadMaps: true}」のオプション指定を書き足す
のだ。

こうすることにより、gulp-sourcemaps は、TypeScript コンパイラが生成したソースマップをまずは読み込んで、この情報に基づいて最終的に出力される Bundle & Minify 化された JavaScript コードに対するソースマップを生成するようだ。

つまりここまでの手順の要領で Gulp による Bundle & Minify タスクを実装すれば、Bundle & Minify 化された JavaScript コードを開始地点として、いちばんはじめの TypeScript ソースコードにまで到達できるソースマップも同時生成できるのだ。
最後に、watch と Visual Studio 統合も最後に、.ts ファイルを保存して JavaScript コードが生成・更新されたら、自動で Bundle & Minify も実行されるよう watch タスク (下記) も加えておくとよいだろう。gulp.task('watch', function () {
bundleconfig.forEach(function (bundle) {
gulp.watch(bundle.inputFiles, ["min:js"]);
});
});Visual Studio 上の作業と統合するのであれば、Visual Studio のタスクエクスプローラーから設定すればよい。

Visual Studio のタスクエクスプローラーを開けば、ここまでで gulpfile.js に記述した「min:js」タスクと「watch」タスクが自動検出されてツリーに見えているはずだ。
あとは、
-「min:js」タスクをビルド後に実行するタスクに、
-「watch」タスクをプロジェクト読み込み後に実行するタスクに、
割り当てるなどしておくとよいだろう。

以上で、クラッシュ時スタックトーレスやデバッガ上での実行において、Bundle & Minify 化した JavaScript コードが読み込まれている場合でも、大元の TypeScript ソースコードに基づいて取り扱うことができる、快適な環境を構築できた。

※上記に加えて、後日の投稿である "AngularJS の依存性注釈の自動生成" も組み込んだコード全体は こちらの gist に置いてある。
 

せっかく Gulp に乗り換えたので、AngularJS v.1.x の minify 対策自動化もやっちゃう

$
0
0
JavaScript のフレームワーク AngularJS の話。

すでに Angular v.2 もリリース候補を迎えている最中、ちょっと時代遅れになりつつあるが AngularJS v.1.x の話である。

AngularJS での DIAngularJS では独自の依存性注入機構 (Dependency Injection, "DI")が用いられている。

例えば次のようなコンストラクタ関数があるとする。function FooController($http){
...
}コンストラクタ関数の第一引数の引数名が「$http」になっている点に注目。

これを AngulaJS にコントローラーとして登録したとする。angular
.module('myApp',[])
.constroller('fooController', FooController);すると、この FooController が new されるときに、ちゃんと第一引数に AngularJS の HTTP サービスオブジェクトが引き渡されるのだ。

これは、引数の "名前" をもとに注入すべきオブジェクトを引き当てて渡してくれる、という AngularJS の DI の仕組みである。

Minify したらダメ!では、どうやって関数の引数の名前を持ってきてるのか?

それは JavaScript ではユーザー定義の関数は toString で文字列化するとその関数のソース文字列が返ってくる、という仕組みを使っている。

しかしこのように、関数定義のソースコード文字列から引数の名前を判別して、これに基づいて依存性注入しようとすると、お察しのとおり JavaScript コードの縮小化 (Minify) がされると破たんしてしまう。

さきの FooController コンストラクタ関数も、minify すると下記のように引数名が "$http" から "t" に短縮化されてしまうだろう。function FooController(t) {
...
}短縮化されたあとの FooController 関数を toString() してそのソース定義の文字列を入手したとしても、わかる引数名は "t" だ。
こうなっては FooController の第1引数に $http サービスオブジェクトを渡さなくてはいけないという情報は失われてしまっている。

Minify するなら依存性注釈かかなきゃダメ!AngularJS v.1.x ではこの事態を回避するため、コントローラー等として登録する際に、引数の名前を文字列で列記することができるようになっている。angular
.module('myApp',[])
.constroller('fooController', ['$http', FooController]);この、コンストラクタ引数を別途文字列配列で指定しているのを、"依存性注釈" というらしい。

当然のことながら、文字列リテラルは minify しても変更されない。
AngularJS は、FooController の定義を文字列で入手することなく、controller メソッドに渡された配列内、すなわち依存性注釈から、引数名を安全に割り出すことができる、という寸法だ。

ということで、AngularJS でコントローラーやサービス、フィルタなどなどを登録する際は、minify されることを想定して、前述のとおり依存性注釈を記述することが推奨される。

人がやる作業じゃない!しかしこれって、けっこう面倒で不毛な作業に感じてしまう。

実際のところ、アプリケーション本体側の TypeScript コードをいじりまわっている最中は、コンストラクタ関数の引数はどんどん変更することが多い。
「あ、やっぱり $q サービスいるじゃん」といった具合に。

そして、コンストラクタ引数の変更のたびに、依存性注釈も編集するのは、二度手間で本当に不毛である。

しかも、依存性注釈ので引数の順番を書き誤るバグとかやらかしてしまうと、本当にフラストレーションが溜まる。

コンストラクタ関数の Minify 前の引数名を、手作業で文字列配列で記述するなんて、
こんな退屈な作業こそ機械化できないのか!?

救世主 "ng-annotate"...ということで、実はそのような不毛で退屈な作業を自動化してくれるツールがある。

ng-annotate というツールがそれだ。

ng-annotate を使えば、angular
.module('myApp',[])
.constroller('fooController', FooController);というコードを入力すると、angular
.module('myApp',[])
.constroller('fooController', ['$http', FooController]);に相当する(※)、依存性注釈付きのコードが自動生成できるのだ。

こうして自動生成したあとの依存性注釈付きコードを minify すればバッチリである。

※ ... 実際に自動生成されるコードはこれとはちょっと違ってて、"$inject" プロパティというものを使ったりしているのだが、詳細は割愛。

自分もようやく ng-annotateを導入...という大変便利な ng-annotate、実はその存在は 2014年頃から耳にしていた。

しかしついつい「いずれ、そのうち...」などと採用を先送りしてしまっていた。

さてところで、前回の投稿で紹介したとおり、最近になって、タスク自動化ツール「Gulp」を使って TypeScript ソースコードに対応するソースマップを同時生成する Bundle & Minify 構成を構築した。

そして前述の ng-annotate は、Gulp のプラグインも提供されている。

ということで、せっかく Gulp に宗旨替えしたのだからこれを機会にとばかり、依存性注釈の自動生成を私の Gulp による Bundle & Minify 機構に取り込んでみた。

まずは ng-annotate の gulp プラグインである gulp-ng-annotate をプロジェクトにインストールしておく。> npm install -save-dev gulp-ng-annotate次に、Bundle & Minify 機構を実装している gulpfile.js を編集。

JavaScript ファイル群を Bundle (= concat, 連結) したあと、Minify (= uglify) するまえのタイミングに、依存性注釈の追加 (= ng-annotate) 処理を挿入する (下記太字の箇所)。var gulp = require('gulp');
...
var ngAnnotate = require('gulp-ng-annotate');
...
gulp.task('min:js', function () {
// bundleconfig.json の記述から1要素ごとに、
// Bunlde & Minify 化のタスクに射影する
var tasks = bundleconfig.map(function (bundle) {
var task = gulp.src(bundle.inputFiles, { base: "." })
.pipe(sourcemaps.init()) // ソースマップ生成機構を仕掛ける
.pipe(concat(bundle.outputFileName)) // concat で連結(Bundle)
.pipe(ngAnnotate()) // 依存性注釈を追加
.pipe(uglify()) // uglify で難読&縮小化(Minify)
.pipe(gulp.dest("."))
.pipe(sourcemaps.write("./")) // ソースマップを出力
.pipe(gulp.dest("."));
return task;
});
...※コード全体は こちらの gist に置いてある。

これで、コンストラクタ引数の記述に加えて、AngularJS v.1.x への登録記述でも引数を書かなくてはいけない不毛な作業から解放された。

ほんとに今更感満載だが、とにかく、やってよかった。
 
 

せっかく Gulp に乗り換えたので、Grunt でやってた単体テスト実行も乗り換えちゃう

$
0
0
Web アプリケーション開発における、クライアント側スクリプト実装の話。

AngularJS を使った Web アプリクライアント側実装について、以前の投稿にて、Gulp の採用によって TypeScript ソースに対応するソースマップ付きでの Bundle & Minify を達成した。
さらに前回の投稿にて AngularJS における依存性注釈の自動生成を達成した。

このように、今更感あるものの、Gulp 採用に伴う開発環境改善を加速中の今日この頃なのだが、さて、単体テストの話。

過去の投稿にて紹介したとおり、クライアント側スクリプトの単体テストを実現するにあたって、テストランナー環境として Grunt とそのプラグイン ( grunt-contrib-jasmine ) を使用していた。

しかしここへ来て、前述のとおり、Bundle & Minify, 依存性注釈の自動生成のために Gulp とそのプラグインを使用するようになった。

となると、すでに Gulp でいろいろタスク実装してるのに、単体テストのためだけに Grunt を併用するのもなんだかなぁ、と思うようになってきた。
お前は Gulp 派なのか Grunt 派なのか、どっちかはっきりしろよ、みたいな。

ということで、その程度の理由・動機ではあったのだけれども、とにかく、Gulp に乗り換えに伴う Grunt 脱却を図ることにした。

Gulp で JavaScript 単体テスト実行を実現するプラグインは?乗り換えということで、grunt-contrib-jasmine で実現していたのと同じ環境を Gulp 上で再構築するには、どんな Gulp プラグインを採用するのがいいのかな、とネットでいろいろ検索。

そうやって情報収集しているうちに、Gulp のプラグインじゃなくて、
"karma" を使えばいいじゃん
ということがわかってきた。

"karma" とは、コマンドラインツールの名前。

実は "karma" の存在も以前から知っていた。

AngularJS を学習しはじめたときに、某書籍で「AngularJS 界隈では karma 使ってる事例あるよ」的なことが書かれてて、そこで "karma" なるものの存在を知った。

しかしその書籍からは "karma" とはなんなのかその本質がよく掴めなかった。
結果、自分の中では 「karma とは E2E テストの自動化に使う何からしい」という誤った認識で留まってしまっていた。

ところがこうして改めて、JavaScript 単体テストの実行環境を求めているうちに、ようやく見えてきたのだが、
karma って要するにテストランナーだったのですね。
※ほんっと、今更ですみません。

つまり、GUI 操作を伴う E2E テストに限らず、単体テストの実行にも普通に karma 使えるのだということがわかった。

ということで、karma を使った JavaScript 単体テストの実行環境を整えてみた。
karmaによるテスト実行環境の構築npm パッケージのインストールまずは必要となる下記 npm パッケージをプロジェクトにインストール。
    karma の本体(コア機能)と、ヘッドレスブラウザ (GUI を持たない、このようなテストや各種自動化を目的とした特殊な Web ブラウザ) である PhantomJS を karma から起動できるようにするためのランチャー、および、単体テストフレームワークの Jasmine を krama 上で扱うためのアドインそして、単体テストフレームワーク Jasmine それ自体
> npm install --save-dev karma
> npm install --save-dev karma-phantomjs-launcher
> npm install --save-dev karma-jasmine
> npm-install --save-dev jasmine-coreなお、ヘッドレスブラウザ PhantomJS も、karma-phantomjs-launcher の依存関係解決により、プロジェクトにインストールされる。

ここでちょっと嬉しかったのは、Grunt のプラグイン (grunt-contrib-jasmine) では PhantomJS v.1.x でしか動作しなかったのだが、上記 karma-phantomjs-launcher では PhantomJS v.2.0 がインストールされること。
karma の構成ファイル「karma.conf.js」の作成さて必要な npm パッケージのインストールが済んだら、karma に対し、どのようにテストを実行したらよいのかを示す設定ファイル「karma.conf.js」を新規作成して記述する。

下記のとおり karma コマンドを実行することで、コンソール上の対話形式で「karma.conf.js」を初期作成することができる。> .\node_modules\.bin\karma init上記を実行すると、karma コマンドから、どのような構成にするかコンソール上での対話が始まる。
下図の要領でこれに応えて、karma.conf.js の初回生成を完遂させる。
d0079457_217166.png

こうして karma.conf.js ができたら、続けて下記要領で内容を編集して完成させる。

karma.conf.js は設定項目が多く見た目で怯みがちだが、特記すべき点に絞ると下記の要領となる。module.exports = function(config) {
config.set({
// テストフレームワークに Jasmine を使う指定。
frameworks: ['jasmine'],

// テスト実行環境に読み込む JavaScript ファイルの指定。
// 下記は自分のプロジェクトのフォルダ構成に基づく例。
files: [
// vendor
'Scripts/jquery-2.2.4.js',
'Scripts/angular.js',
'Scripts/angular-mocks.js',
// src
'bundle.js',
// spec
'_spec/**/*Spec.js'
],

// Visual Studio の出力ウィンドウで表示するため、
// テスト進捗状況の表示は progress ではなく dots を指定。
reporters: ['dots'],

// Visual Studio の出力ウィンドウで表示するため、
// コンソール出力はモノクロを指定。
colors: false,

// テスト実行に使うブラウザは PhantomJS を指定。
browsers: ['PhantomJS'],

// テストを実行するごとに処理終了させる指定。
singleRun: true
})
}karma によるテストの実行以上でお膳立てが整ったら、いよいよ実行である。

コマンドプロンプトにて> .\node_modules\.bin\karma start karma.conf.jsというように、start コマンドと構成ファイル名 (= karma.conf.js) を引数に指定して karma コマンドを実行する。

すると
    karma コマンドが動き出して karma.conf.js を読み込み、これに記述されているとおり、PhantomJS を起動、単体テストを構成するコンテンツを読み込ませるPhangtmJS は読み込まされたコンテンツ、すなわち jasmine ベースのテストコードを実行
    ( 但し PhantomJS はヘッドレスブラウザということもあり、とくに GUI/ウィンドウが現れたりすることはない )、最後に karma が結果をすくい上げてレポート表示
された。

バッチリである。

これで grunt-contrib-jasmine による単体テスト実行環境から karma ベースの環境に完全移行できた。
もう grunt は使っていないので、「npm uninstall grunt --save-dev」して大丈夫である。

なお、単体テストフレームワークは jasmine のまま変更しなかったので、既存の単体テストコードは一切変更せずに済んでいる。

さらにソースマップ読み込みも!さらに、これは予想・期待していたものではなかったのだが、karma であれば、テスト失敗時のレポートなどで、
コンパイル元の TypeScript ソース行で該当箇所をレポートする
ように構成できることがわかった。

まず必要となるのは "karma-sourcemap-loader" という npm パッケージ。
これをプロジェクト構成に追加。> npm install --save-dev karma-sourcemap-loader次に、karma.conf.js 中にて、"プリプロセッサ" の指定に karma-sourcemap-loader を追加する。...
preprocessors: {
'**/*.js': ['sourcemap']
},
...こうすることで、TypeScript のコンパイル時に同時生成したソースマップファイルが読み込まれ、テスト実行後のレポート表示において、TypeScript ソースコード上でのファイル名と行番号とで表示されるようになった。

テスト失敗が報告された JavaScript の該当行から、コンパイル元の TypeScript ソースコード上での該当行を人力作業で特定するのは、不可能ではないとはいえまずまずの労力を要する。
そんな非生産的な、機械で解決できるはずの課題が、実際のところ今日まで grunt-contrib-jasmine 環境では解決できていなかったのだから、こうしてこの課題が解決され、大変晴れ晴れしい気分になった。

改めて、karma をはじめ各種ソフトウェアプロダクトに貢献されている方々に感謝である。
おまけこうして構築した karma による単体テスト実行環境だが、この単体テストを実行するにあたり、自分の場合、コマンドプロンプトから直接の karma コマンド実行をしていない。

代わりに、npm を一段階挟んでいる。

つまり、packages.json 内に、karma 実行コマンドを記載しておいてある (下記)。...
"scripts":{
"test":"karma --silent --no-color start karma.conf.js"
}
...こうしておくことで、「npm test」を実行することで単体テストが実行されるようになっている。

直接 karma を実行すればよいものを、わざわざ npm を経由しているのなぜか?
それは、自分の主たる開発環境が Visual Studio であることに由来している。

Visual Studio 上の設定で、Ctrl+R, Ctrl+J のキーコンビネーションが打鍵されると、「npm test」を実行して、結果を Visual Studio 内のウィンドウ (出力ウィンドウ) に表示するようにしてあるのだ。

そのため、npm 側で実際のテスト実行コマンドを隠蔽しておくと、従来の gurunt-contrib-jasmine で構築されたプロジェクトでも、今回新規構築した karma によるプロジェクトでも、どちらでも共通の「npm test」でテストを実行できる。

すなわち、どのプロジェクトを Visual Studio で開いても、共通のキーボード操作 Ctrl+R, Ctrl+J でテストが実行される、という仕掛けである。

なお補足として、上記 package.json に記述した karma コマンドに記載のオプションスイッチだが、「--silent」はテスト結果の表示上、よぶんなバナー表示などを抑止するもので、「--no-color」は、Visual Studio の出力ウィンドウにテスト結果表示をリダイレクトしてる関係上、コンソールの色指定コードは表示を乱してしまうことから、カラー表示コードの出力を抑止するスイッチである。

Gist にサンプルコードありますこれまでの内容と、実際に動作している構成フィル類とを、下記 Gist に置いてある。

Jasmine による TypeScript/JavaScript 単体テストを実行する環境を作る - karma 編
https://gist.github.com/jsakamoto/e4a957821216c38cdb7e9e20ba17e569

こちらも適宜参照されるとよいかもしれない。

以上、ご参考までに。
 

[解決] Windows10 を英語UIで使ってたらダイアログのフォントが汚くなってた件

$
0
0
Windows10 を英語 UI で使用中諸事情により、自分は Windows 10 を英語 UI で使用している。
但しもちろん、Windows の UI 表記が英語だというだけで、普通に日本語を含むコンテンツの表示や IME での入力などを行っている。

コマンドプロンプトでもコードページ 932 で日本語文字を正しく表示させるために、システムロケールの設定は日本語に設定してある。
d0079457_22242051.png

ある日、ダイアログのフォントが酷いことにしかし何のタイミングだったか、Anniversary Update をきっかけにしてだったような気がするのだが、とにかくとある日から、OS が表示する系のダイアログのフォントがめちゃくちゃ汚くなってしまった。
d0079457_22241256.png
特に数字なんかは、カンマとピリオドが混じった際など判読不能な場合も発生していた。

ただし軽くネットで検索した感じではすぐに解決策を見いだせなかったのと、いろいろと他の優先事項を消化する日々につれ、ついついこのフォント汚い問題を解決せずにいた。
重い腰をあげて解決策を探すしかしそろそろどうにかせねばと改めてネットで検索し歩いていたところ、どうも、フォント置換の仕組みで "MS Shell Dlg" という代替フォント名の置換で OS のダイアログ系のフォントが決まるらしいとの情報を掴む。

そういえばありましたね、"MS Shell Dlg"。

ということで早速、レジストリでフォント置換テーブルを開いてみる。
場所は「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes」だ。
d0079457_2224532.png
ここの "MS Shell Dlg" と "MS Shell Dlg 2" の両方を、"Segoe UI" に変更。
d0079457_221732100.png
いちどサインアウトしてからサインインしなおすと...


d0079457_22171216.png

きれいなフォントに戻った。
良かった。
 

Visual Studio 2017 RC インストール後、.NET2.0アプリを再ビルドするとローカライズが効かなくなる

$
0
0
.NET2.0 用 C# プログラム開発当方、とある .NET2.0 C# プロジェクトを Visual Studio 2015 で保守担当している。

今さら .NET2.0 かよ!という向きもあると思うが、ハードウェア故障などから再インストールや機体交換が発生した Windows 7 機、すなわち、まだ .NET 4系が未インストールである状態の Windows OS 上でブートストラッパ的に実行したいプログラムの要件などがあったりする次第。

さてところで、先日の Microsoft のイベント「Connect();」にて Visual Studio の次バージョン Visual Studio 2017 のリリース候補(RC)版が発表され、Visual Studio 2017 RC版を誰でもダウンロードして試用・評価できるようになった。

そんなわけで自分も、Visual Studio 2017 の新機能を評価してみたく、Visual Studio 2017 RC版をダウンロード、インストールして、いろいろ評価していた。

いつもどおりのプログラム修正のはずが...そんなある日、先述の .NET2.0 C# プロジェクトの保守が発生し、いつもどおり Visual Studio 2015 でコードを修正、ビルドしていた。

ところが、試験実行してみたところ、ローカライズの仕組みで定義してあった日本語版のメッセージが、日本語 OS 上で表示されないのだ。

参考までに、現象を再現するサンプルプログラムを下記に掲載しておく。

https://github.com/sample-by-jsakamoto/doNET20LocalizeApp/正常であれば Culture が "ja" だと日本語リソースが読み込まれて表示される。
d0079457_2119230.png
しかしVS2017RCインストール後に再ビルドすると日本語リソースが読み込まれてないように見える。
d0079457_21191741.png
Visual Studio 2017 RC のインストール前はちゃんと機能していたプログラムが、ただただ Visual Studio 2017 RC をインストールしたというだけで、その後に Visual Studio 2015 (2017 RCではなく!) でいったん再ビルドしてしまうと、ローカライズが効かなくなってしまうのだ。

なぜ!?

サテライトアセンブリに異常?このプログラムは日本語文字列はサテライトアセンブリ ― ja サブフォルダに収録される、~.resource.dll ― の埋め込みリソースとして収録されている。
日本語文字列が読み取れなくなったということは、このサテライトアセンブリに何か問題が発生したのだろうか。

そこで、Visual Studio 2017RC のインストール前後でビルドしたサテライトアセンブリに何か違いがあるのか、ILSpy で各々のアセンブリファイルを開いてみた。

すると、どうだろう、Visual Studio 2017RCインストール後に再ビルドして生成されたサテライトアセンブリは、なんと
".NET4.0用" とマーク
されていたのだ。VS2017RCインストール前にビルドしたサテライトアセンブリは .NET2.0 とマークされている
d0079457_21192547.png
しかし、VS2017RCインストール後にビルドしたサテライトアセンブリは .NET4.0 とマークされている
d0079457_21193022.png
おそらくは .NET2.0 のランタイム上で実行中のプロセスが、サテライトアセンブリを読み込もうとしても、そのサテライトアセンブリが .NET4.0 用とマークされてるので、そのサテライトアセンブリを読み込めずに終わってるのではないだろうか。

さらにビルドログを調べてみると....NET2.0 のプロジェクトなのに、どうしてサテライトアセンブリだけが .NET4.0 用とマークされてしまうのか、まだよくわからないものの、次なる調査として、Visual Studio 2017 RC インストール前後でのビルドのログを調べてみることにした。

Visual Studio 2015 の設定にて、出力ウィンドウへのビルドログの表示レベルを "Normal" に設定し、再ビルドを実行する。
d0079457_21195464.png
そして Visual Studio 2017 RC インストール前後でのビルドログを比較してみると、唯一、ビルド中に実行される
アセンブリリンカ (AL.exe) のパスが違っている
ことを発見した。

つまり、同じ Visual Studio 2015 上でのビルドであるが、

Visual Studio 2017 RC インストール前は
「C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin」
にある al.exe が実行されているのに対し、

Visual Studio 2017 RC インストール後は
「C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools」
にある al.exe が実行されているのだ。

試しに Visual Studio 2017 RC インストール後の環境にてビルド実行し、.NET4.0 用にマークされたサテライトアセンブリがいったんは生成されたあと、自前でコマンドプロンプトから再度「C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin」にある al.exe を明示的に使用してサテライトアセンブリを生成しなおすと、今度は期待どおり .NET2.0 用でマークされたアセンブリが生成できた。

ということで、実行される al.exe の違いが、この不具合現象の原因と考えてよさそうだ。

しかし
Visual Studio 2017 RC のインストールが、まさか Visual Studio 2015 のビルド内容に影響を及ぼすとは
想像できなかった。

いちおうフィードバック報告済み以上、突き止めた内容は、Visual Studio 2017 RC のフィードバックツールを経由して報告はしておいた。
報告についてはいちおう "Triaged" のタグは付いたようなので、いずれ修正されることを期待してもよさそうだ。
とはいえ、優先順位的には低いかもなので(だって、今更 .NET2.0 だし)、いつ修正されるかは何とも言えない。

当座の回避方法問題が根本解決されるまでの間、自分はビルド後イベントのコマンドライン指定にて、「C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin」にある al.exe を明示的に使用してサテライトアセンブリを生成しなおすようなコマンドを組んで回避することにした。

環境 ( OS のプロセッサアーキテクチャが 32bit か 64bit か、とか) やプロジェクトによってまちまちになるので "誰でもこれをコピペすればそれでOK!" とはならないが、参考までに先のサンプルプログラムにおける回避バッチコマンドを下記に記す。set al="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\al.exe"
pushd "$(ProjectDir)"
%al% /culture:ja /out:$(OutDir)ja\$(TargetName).resources.dll /template:$(OutDir)$(TargetFileName) /embed:obj\$(ConfigurationName)\$(TargetName)._Localize.ja.resources
d0079457_21362980.png

以上、どれだけ自分と同じ境遇の方がいらっしゃるのか甚だ怪しい超レアな感じの不具合案件だが、念のためここに情報公開としておく。
 

PowerShell でハッシュテーブルを "指定の順序で" JSON 化する方法

$
0
0
とある事情から、某M社の某製品Vに読み込ませるとある JSON ファイルを PowerShell で自動生成する案件が発生。

ということで、昨今の PowerShell で JSON ファイルを生成する方法をネットで検索してみる。
結果、次のような手法がよさそうであることがわかった。 PowerShell のハッシュテーブルでモデルを組み立てるそうして組み立てたハッシュテーブルを、PowerShell 備え付けの ConvertTo-Json コマンドレットで JSON 化これを踏まえて早速実装してみる。

まずはこんな感じでハッシュテーブルを生成して変数 $catalog に格納。$catalog = @{
manifestVersion = "1.1";
info = @{
id = "foobar";
};
packages= @();
}この変数 $catalog をパイプを経由して (パイプ経由じゃなくてもいいんだけどとりあえず) ConvertTo-Josn に読み込ませてみる。$catalog | ConvertTo-Jsonすると下記のような結果が得られた。{
"info": {
"id": "foobar"
},
"manifestVersion": "1.1",
"packages": []
}JSON 化すると、プロパティの出現順が、PowerShell コード上でハッシュテーブルを組み立てたときと違うのに気づく(manifestVersion プロパティと info プロパティの出現順が入れ替わってる)。

しかし、まぁ、ハッシュテーブルって、実装原理からしてそういうものであり、順序に意味はないコレクションなのだから、そんなもんだろ、と気に留めずにいた。

ではこの JSON を読み込ませてみましょうさてこうして生成した JSON をファイルに保存し、いよいよ某M社の某製品Vに読み込ませると...

オマエの JSON は壊れてて読めん

という趣旨のエラーを吐くではないか。
d0079457_2264360.png

※The catalog in the vsix is not valid. と言われてしまっている

経緯の詳細は省くが、小一時間ほど格闘した結果、どうやら、読み込ませる JSON 中、
manifestVersion プロパティがいちばんはじめに出現しないといけない
様子であるらしいことが見えてきた。

よもや、JSON でデータを供給するのに、そのプロパティ出現順序に制約があるとは思わなかった。

とにかく、某M社の某製品Vがそのような仕様・振る舞いになっている以上、こちらが生成する JSON をどうにかするしかなるまい。

しかしハッシュテーブルというものは、先に書いたとおり、たしか実装原理からしてプロパティ (というかハッシュテーブル上ではキー) の順序は指定や維持ができないのではなかったか。
これは面倒なことになった。

どうやって解決する?...と思いつつ、念のためと思い「powershell ハッシュ」で検索すると、その検索結果の中に...

PowerShellのHashtableの並び替えを制御したい

という題の、ぎたぱそ氏の記事が目に飛び込んできた。

PowerShellのHashtableの並び替えを制御したい (2013年3月20日)
http://tech.guitarrapc.com/entry/2013/03/20/200351

早速読んでみると、なになに、PowerShell v.3.0 で、"[ordered]" というキーワードが追加装備され、これをハッシュテーブルを記述する先頭に付記すれば、キーの順序が維持されるとのこと!

ということで実装を変えてみた。$catalog = [ordered]@{
manifestVersion = "1.1";
info = @{
id = "foobar";
};
packages= @();
}

$catalog | ConvertTo-Jsonその結果、下記のとおり、PowerShell のコード上で記述したのと同じプロパティの出現順序で JSON 上にも記載されるようになった。{
"manifestVersion": "1.1",
"info": {
"id": "foobar"
},
"packages": []
}こうして出来上がった JSON をファイルに保存し、某M社の某製品Vに読み込ませたところ、今度は無事、正しく認識されるようになった。

おまけなお、最終的に生成する JSON は、改行やインデントといった書式整形は不要である。
そのため、ConvertTo-Json コマンドレットに対し、-Compress スイッチを付けて JSON 生成を行っている。

すなわち、$catalog | ConvertTo-Json -Compressを実行することで、{"manifestVersion":"1.1","info":{"id":"foobar"},"packages":[]}を得ている。
 

Azure Functions を使って Selenium WebDriver の新バージョンリリースの通知を受け取る

$
0
0
Selenium と WebDriver"Selenium" とは、Web ブラウザをプログラムから "操作" できるようにする、主には自動化テストの実現などの目的で使用されるソフトウェア/ライブラリである。

そして "Selenium WebDriver" とは、個々の OS/ブラウザ種類に応じて提供される、ブラウザ操作の終端を担うプログラムを指す用語である。

自分の場合、対象 OS は Windows で、且つ、以下の Web ブラウザに対応した Selenium WebDriver にかかわる機会がある。

- Microsoft Edge (MicrosoftWebDriver.exe)
- Internet Explorer (IEDriverServer.exe)
- Google Chrome (chromedriver.exe)
- Mozilla Firefox (geckodriver.exe)
WebDriver の新バージョンリリースをタイムリーに捕捉したいさて、詳細は割愛させていただくが、とある経緯から、上記の Selenium WebDriver プログラムについて、新バージョンがリリースされたら最大で1日程度のタイムラグを目安に、その新バージョンリリースを知る必要がある、という要件に迫られた。

もちろん、自分が手作業で毎日先述の URL をブラウザで開いて目視でチェック、というやり方は除外。
どうにかコンピュータプログラムの力で解決したい。

ということで、各 WebDriver について、IT/プログラミングの力でどうやすれば新バージョンリリースを知ることができるか調べてみた。
FireFox 用は楽勝Mozilla Firefox 用の Windows 用 Selenium WebDriver "geckodriver.exe" は、開発元の GitHub リポジトリにて、"Release" としてアップロードされるようだ。

幸い、GitHub の Releae ページは、その URL の末尾に ".atom" を足せば、同ページの内容が Atom フィードとして入手できるとの情報を得た(下記 URL)。

https://github.com/mozilla/geckodriver/releases.atom

そこで自分の場合は "IFTTT" サービスを利用し、上記 Atom フィードについて新着更新があった場合は自分のメールアドレスに通知を送るように IFTTT を設定しておいた。

他にも Feedly をはじめとした RSS reader を利用してもよいだろうし、Release に限らず種々の通知を受け取って構わないならば単純に GitHub リポジトリの "Watch" を On にしておくだけでもよいだろう。

というわけで、以上で FireFox 用の WebDriver のリリースを検知する体制は整えることができた。
IE、Chrome の WebDriver はちょい面倒...Internet Explorer と Google Chrome 用の Windows 版 Selenium WebDriver (各々IEDriverServer.exe と chromedriver.exe) はそれぞれ下記 URL でアクセスできる Amazon S3 ストレージに格納されている。

- https://chromedriver.storage.googleapis.com/
- https://selenium-release.storage.googleapis.com/

上記 URL に HTTP GET 要求を送ると、XML 形式でストレージの収録内容が返ってくる。

但し返される XML のスキーマは Amazon S3 のもの。
Atom フィードをはじめとした公共(?)の規格ではない様子である。

いろいろ思案した限りでは、どうやらこの XML を何らかのプログラムで定期的に取得・監視して、新バージョンのリリースを検知するほかなさそう、との結論に至った。

Edge 用は構造化されたデータがない...さらには Microsoft Edge 用の Selenium WebDriver、"Microsoft WebDriver" であるが、こちらは、下記 URL のダウンロード用 Web サイトがあるのみ。
XML や Atom フィードのような、構造化されたデータとしては入手可能な感じではなかった。

https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver

となると残される道は、上記 URL で入手できる HTML コンテンツを、何かしらのプログラムによってどうにかスクレイピングしてリリース情報を抜き出すほかなさそうだ。

実装手段は?さてさて、ではでは、どういう形で先述の XML や HTML を定期的に監視し、新バージョンのリリースを検知して自分に知らせるようなプログラムを作成・運用できるだろうか?

やり方は本当にいろいろあると思う。
24時間通電稼働させている自宅サーバーで cron で監視プログラムを定期実行とかもありだろう。

しかし自分は
サーバー構築とかまっぴら御免なタイプ(!)
なので、できれば SaaS、せいぜい頑張ってパブリッククラウドの PaaS で何とかしたい、と考えた。

そこで目を付けたのが Azure Functions だ。

Azure FunctionsAzure Functions とは、別途構成された URL への HTTP 要求だとか、cron のような時間による指定とか、Blob に何か追加されたとか、そういった "トリガー" によって起動され、ある一定の処理上限時間やメモリ消費量制約のもと、自分で作成した小さなプログラムコード = ファンクション (関数) が実行されるという、ある種の PaaS である。

Microsoft のパブリッククラウドサービス「Microsoft Azure」に含まれる機能・サービスのうちのひとつだ。

"サーバーレス" という文脈で語られ、「Amazon Lambda の Azure 版」といえば通じる方もいるかと思う。

自分の場合は Azure の Web Apps サービスによる Web アプリ配置に慣れていることもあり、Amazon Lambda ではなく Azure Functions で実装してみることにした。
Azure Web Apps 上に自作 Web アプリを立てるのと比べると...Azure Web Apps でも同様のことは実現できる。
自分で立てた Azure Web Apps 上の Web アプリにて、所定の URL に HTTP GET 要求が届いたら監視/リリース検知処理を実行するようにしておき、この URL に対して SetCronJob (https://www.setcronjob.com/) などで定期的に HTTP GET 要求を送るようにしてもよい。

しかしながら Azure Functions で作っておけば、課金が Web Apps とは違い、「その Web アプリを稼働させてる時間」ではなく、「トリガーによって所定のコードが起動され、終了するまでの時間とメモリ消費量」及び「実行回数」に対して課金されるため、
フトコロに優しい
のだ。

さらに、
コード内容が「やりたいこと」に集中
できる。

自分の場合、得意なプログラミング言語が C# なので、Azure Web Apps 用に Web アプリを作るとなると ASP.NET という選択になる。
そうなると「Web.config がー」「URL ルーティングの登録をー」といったコード類を記述せざるを得ない。

その点、Azure Functions であれば、コードの "型" が決まっている代わりに、その定型コードの中身を埋めるかたちで自分のやりたいことを記述していくので、本来やりたいこと以外の余計なコードの記述量が各段に減るのだ。

実装結果ということで実践してみた結果がこちら。

https://github.com/jsakamoto/WebDriverUpdateDetector
実装のプログラミング言語 - C# スクリプトを選択Azure Functions では、ファンクションの実装・記述に使えるプログラミング言語として、C# はもちろんのこと、他にも
- Bash
- Batch
- F#
- JavaScript
- Php
- PowerShell
- Python
といった豊富な選択肢から選べる。

自分の場合は、先述のとおり得意なプログラミング言語は C# なので、C# (より厳密には C# スクリプト、という形になる) でファンクションを実装することにした。

実装内容としては、Edge用・IE用・Chrome用にサブフォルダ x 3つに分け、サブフォルダごとに新バージョンリリースを監視/検知するファンクション ("Run.csx") を配置してある。

Edge用・IE用・Chrome用のいずれのファンクションでも同じ内容となる共通コードは、それらサブフォルダと並べて Shared サブフォルダを用意し、こちらに .csx ファイルとして収録。
Edge用・IE用・Chrome用の各 Run.csx からは「#load "..\Shared\SendMail.csx"」のように記述して、これら共通コードの .csx を読み込むようにした。
リリース情報コンテンツの入手と解析各 Run.csx 内では、冒頭で解説した URL から XML 及び HTML を HTTP GET で入手。
入手した XML・HTML を解析して、それらコンテンツに掲載されている、目的の WebDriver ファイルの最新バージョン番号を読み取るようにした。

IE 用と Chrome 用は、Amazon S3 による XML で構造化されたデータが取得される。
この読み取った XML を LINQ to XML で内容探索する実装とした。

Edge 用はそのような構造化された状況とは言えない HTML コンテンツであった。
そこで真面目に HTML として解析することはやめて、その HTML コンテンツを単純な巨大な文字列として扱い、正規表現検索で WebDriver ファイルのダウンロードリンクを探索する実装とした。
NuGet パッケージの追加指定の URL から XML や HTML を HTTP GET で入手するために System.Net.Http NuGet パッケージの追加が必要になったりする。
そのように必要となる追加の NuGet パッケージは、各サブフォルダ内の project.json に記述しておく。
起動条件などファンクションの構成起動条件や出力結果などは、同じく各サブフォルダ内の function.json で指定する。
今回の要件は cron よろしく時間に基づいて定期的に実行したいわけだが、その内容を function.json に記載するわけだ。
とくに実行時間については、毎日、UTC 時刻にて 0:00 と 12:00 の1日2回の実行としてみた。
通知手段新バージョンリリースを検知した際の通知手段は e-mail での通知とすることにした。

もちろん、Slack 等のチャットシステムに Bot よろしくテキストを送信することもできるだろう。
「今時メール~!?」みたいなことも言われた。
けれど人それぞれ色々背景理由があるのであって、詳細は面倒くさいので割愛するが、自分の場合は e-mail による通知が最適だったので、このような選択とした。

しかしこの実装を進めていた当時、Azure Functions における実行結果としての SendGrid API との連携方法などがよくわからず、結局 System.Net.Mail.SmtpClient による古典的なメール送信で実装した。

今ならば、SendGrid API と連携させる方法については下記を参照されるとよいかと思う。

Azure Function Appから定期的にメールを送る
http://qiita.com/superriver/items/d245539b7652b7512f9b
状態の保存あと、新しいバージョンのリリースを検知するため、"前回チェックまでにおける最新バージョン" という「状態」を保存する必要に迫られた。

「Functions に状態持たせるの~!?」と白目をむかれたこともあるが、要件と先述の利点とを考えると別に悪い選択ではないと思うのだが...どうなんでしょう...。

...さておき、状態の保存/復元である。

これまた未だにそのような用途での何らかの記憶域との連携方法がわからず、最終的に Azure Table Storage を読み書きする実装とした。

なお、Azure Table Storage の読み書きにあたっては、Azure Functions だからといって特別なことはない。
普通に project.json に必要な NuGet パッケージを記載し、.csx ファイルに普通に Azure Table Storage の読み書き処理を記述するだけである。
アプリケーション構成値状態保存のための Azure Table Storage に接続するアカウント情報や、SmtpClient によるメール送信時の SMTP 認証情報といった情報は、.csx ファイル中に書いてはいけない。

代わりに、Azure ポータルページ経由で「アプリケーション構成」に設定しておき、.csx 内からは ConfigurationManager.AppSettings 経由で読み取るべきだ。

この点は Azure Functions もその土台は Azure App Services ベースということで Web Apps のポータル画面と操作感は変わらなかった。

下記の過去記事も参考までに。

ASP.NET Web アプリで、APIキーなどの "秘密のキー" をどこに保存するべきか?
http://devadjust.exblog.jp/20400427/
成果この Selenium WebDriver 新バージョンリリースの監視/検知システム、11月末頃から本格稼働を始めた。

以来、これまでに計2回ほど、IE 用 x 1回と Chrome 用 x 1回、の新バージョンリリースを検知している。

おかげて1日以内での新バージョンリリースを把握できるようになった。
しっかりと当初目標・要件を満たすことができ、満足である。

※Microsoft Edge 及び Mozilla Firefox に関しては、2016年11月末以降現時点まで、まだ新バージョンがリリースされた実績がない。

課金かれこれ1ヶ月ほど運用したわけだが、課金のほうはいかほどかというと、
この1ヶ月でざっくり、¥30 以下。

課金の内訳は、Azure Function が必要とする分と、先述の "状態保存" に使用する分の、双方の Azure Storage Account の料金がほぼすべてを占めていた。
ファンクションの実行時間/回数に関する課金は ¥1 に達していなかった。

そもそも実行時間/回数については無料枠がある。
今回のシステム程度の負荷では無料枠内で済んでしまうことだろう(下記リンク先参照)。

Functions の価格
https://azure.microsoft.com/ja-jp/pricing/details/functions/

まとめAzure Fuctions、ゆるーく1日数回の監視するような用途だと、料金がむちゃくちゃ安く済むコードが「やりたいこと」に集中できるという点で良かったと思う。

あと「よくなかった点」もあって、それは主に実装作業時・コーディング時におけるものなのだけど、それについては後日エントリを改めて触れたい。

そのほか、IE 用と Chrome 用に関しては、Amazon S3 での提供という形なので、もっと手抜きができる方法が実は存在するのではないかと気が気でならない。
「そんなにゴリゴリ書かなくても、ほら、こんな風にすれば新バージョンリリースの通知を受けられるよ!」
といった情報をお持ちの方がいらっしゃれば、本ブログへのコメントとか Twitter で触れていただくなど情報発信していただければ、自分の二の轍を踏む方が増えずに済むと思うのでぜひお願いしたい。

実際、@twit_ahf さんからも、Azure LogicApps/LogicFlow で実現する作例を教えていただいた。

多分、@jsakamoto さんに出された宿題はこんな感じでいける気がしている。1日1回、バージョン情報の xml 拾って、最終更新日が前日より新しければ通知する、って感じで。あくまでも多分こんな感じ。 pic.twitter.com/oASEQ5lMio— Ahf(Tomoyuki Obi) (@twit_ahf) November 27, 2016
LogicFlow で XML データを繰り返し処理する
http://blogahf.blogspot.jp/2016/11/logicflow-xml.html

せっかく教えてもらったのだが、自分の場合は贅沢にも、XML 全体の最終更新日での通知ではなく、特定アイテムの新バージョン出現に限って検知したく、Azure LogicApps/LogicFlow での上記作例の採用は見送ってしまったのだが、以上ご参考までに。
  

Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタ搭載の Windows PC をWi-Fiアクセスポイント化する

$
0
0
Windows OS の "Soft AP" 機能Windows OS は、Windows7 の頃から、その PC のワイヤレスネットワークアダプタを使用して、その PC を Wi-Fi アクセスポイント化することができる。

この機能は一般に「Soft AP」と呼ばれ、公式な用語としては「ワイヤレスホステッドネットワーク (Wireless Hosted Network)」と言うらしい。

アクセスポイント化の手順や仕組みなどについては、「Windows aoftap」などのキーワードでネットで検索するとたくさん良記事がヒットするのでそちらを参照されたし。

※余談かもだが、パーソナルコンピュータ向け OS で、このように Wi-Fi アクセスポイント化を可能としている OS は別段 Windows に限らない。OS の機能とは言えないが、Linux 各種ディストリビューションなどでも同様のことは実現可能だ。「raspberrypi ap」などのキーワードでネット検索してみると面白いかもしれない。

デバイスドライバの対応が必要さてこのワイヤレスホステッドネットワーク機能であるが、どんな Windows PC 機でも利用可能かというと、そうではない。

ワイヤレスネットワークアダプタのデバイスドライバが、ホステッドネットワーク機能に対応している必要があるのだ。

とある Windows PC 上で、その PC 機に装着されているワイヤレスネットワークアダプタのデバイスドライバがホステッドネットワーク機能に対応しているかどうかは、コマンドプロンプトから次のコマンドを実行することでわかる。netsh wlan show driver上記コマンドを実行するとワイヤレスネットワークアダプタの各種情報がコンソールに表示されるのだが、その中に「ホストされたネットワークのサポート」という項目があって、「はい」「いいえ」で、ホステッドネットワーク機能に対応しているか否かが表示される(下図は、ホステッドネットワーク機能に対応している、USB 外付け Wi-Fi ドングルを挿している状態でのコマンド実行結果)。
d0079457_20263620.png
Windows タブレット機を Wi-Fi アクセスポイント化したいで、ちょっと話は変わって。

俗に言う "スティックPC" や安価な Windows タブレット PC 機では、内蔵のワイヤレスネットワークアダプタとして「Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタ」が使われていることが多いらしい。

とある需要から、そのような安価な Windows PC 機材でホステッドネットワーク機能を利用したくなった。

機材調達し確認してみたところ、ご多分に漏れず、その機種も Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタを搭載していた。

早速 netsh コマンドにて、Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタのデバイスドライバがホステッドネットワーク機能に対応しているかどうかを調べてみた。

するとなんと残念なことに、ホステッドネットワーク機能には対応していないとのこと(下図)。
d0079457_20264173.png
より新しいバージョンのデバイスドライバであれば対応しているのではないかと考え、デバイスドライバの新バージョンを探したり入れ替えたりして試したが、結果は変わらず。

となると、ホステッドネットワーク機能に対応した USB 外付けの Wi-Fi ドングルを挿して実現せざるを得ない。
USB 外付け Wi-Fi ドングルはちょっとイヤだ...しかし往々にしてそのような Windows PC 機材では、OGT 対応の microUSB ポートが 1 個あるだけ。
A タイプのフルサイズの USB ポートなどついていないのがほとんどだ。
すなわち、microUSB - USB の変換ケーブルを介して挿すほかなく、取り回しが悪い。

おまけに、このたったひとつしかない microUSB ポートは充電ポートを兼用していることが多く、「ちょっとバッテリーが減ってきたら継ぎ足し充電」とかができなくなるのだ。

なんとかして内蔵の Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタだけで Wi-Fi アクセスポイント化したい。
Windows OS 標準機能に限らずなにか手段はないのか、いろいろネットで検索しまくってみた。

とある海外の掲示板で情報発見!するととある海外の掲示板で、Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタでも、Windows OS 標準のホステッドネットワークを使えたとの書き込みを見た。

その書き込みを読むと、な、なんと、

「デバイスドライバを "ダウングレード" せよ」

とのこと (!)。

ということで、恐る恐る、実際に自分の手元でも試してみた。

下図の手順で、Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタのデバイスドライバを、もっとも古い ver.3008.8.401.2015 (2015/04/10) に変更。
d0079457_20265313.gif
デバイスドライバのダウングレード後、netsh コマンドでホステッドネットワーク機能の対応状況を確認してみると...
d0079457_20264659.png
対応されていた!

そして実際にホステッドネットワーク機能を有効化して、Wi-Fi アクセスポイント化を実施し使ってみたが、無事 Wi-Fi アクセスポイントとして使うことができた。

まとめまさか、新しいバージョンのデバイスドライバにアップグレードするのではなく、ダウングレードで使えるようになるとは思わなかった。

なお、Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタの新しいバージョンのデバイスドライバでホステッドネットワーク機能に未対応なのは、何か深い理由があるのかもしれない。
また、古いデバイスドライバでは、新しいデバイスドライバでは修正されている不具合を抱えているかもしれない。

自分はさぼってて、これら Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタのデバイスドライバのリリースノートなど見ていないため(そもそもそのようなリリースノートがあるのかどうかも知らない)、どのようなリスクがあるのかわかっていない。

しかし今日までのところは、とくに不都合は感じていない。

あくまで注意の上の運用となると思うが、Realtek RTL8723BS SDIO ワイヤレスネットワークアダプタを搭載している Windows PC を Wi-Fi アクセスポイント化されたい場合にこの情報が役立てば幸いである。
 

C# スクリプトでインテリセンスが効かずむしゃくしゃして F# スクリプトにしたら幸せだった (オチあり)

$
0
0
前回、「Azure Functions を使って Selenium WebDriver の新バージョンリリースの通知を受け取る」という記事を投稿した。

Azure Functions を使って Selenium WebDriver の新バージョンリリースの通知を受け取る
http://devadjust.exblog.jp/23744108/

このように、プラットフォームとして Azure Functions を選択、プログラミング言語は自分の得意な・慣れている C# を採用することで、「Selenium WebDriver の新バージョンリリースの通知を受け取る」という目的を達成できた。


達成できた、のであるが...

C# "スクリプト" に悩まされるこの実装当初、プログラミング言語は C# とは言いつつも、Azure Functions では、拡張子が .csx の、「C# スクリプト」というちょっと変わった体裁で記述する必要があった。

しかしこの「C# スクリプト」がくせ者。
ナ、ナント、Visual Studio (当時、2015 及び 2017RC 使用) 及び Visual Studio Code (当時 v.1.8.1) のいずれでも、インテリセンスをはじめ

一切の IDE 支援が効かない

のだ。

まぁ、結局 IDE 支援なしで実装しきったのではある。
しかし疲労感というか、なんか、通常の3倍くらい実装時間がかかってしまった気がする (※計測したわけじゃないのであくまで感想です)。

自分でも信じられないのだけれど、文末のセミコロンを打ち忘れて Azure Function 上にデプロイしてからいざ実行してみたら構文エラー、みたいなパターンも頻発。
まさか自分が、IDE が画面上に示すインジケータを頼りにセミコロン打っていたとは自覚もなく、人間って不思議ー、とか思ったりもしたけれど、しかしそんなことはどうでもいい。

とにかく IDE 支援がないとやってられないのである。

こんな状況ならば、C# (スクリプト) ではなく、JavaScript で記述することを選択したほうが IDE 支援が得られてよかったのでは、などと思ってしまった。

※ Azure Functions は C# スクリプトのみならず、JavaScript ももちろんのこと、PHP や Bash シェルスクリプトなど、ほかの様々な言語・処理系でコードを記述・実装できる。

ただし、実際に JavaScript で記述するとなると、自分の場合、言語の問題ではなく、Node.js の各種 API に不慣れだというごく個人的な理由でことごと躓くことが目に見えている。
だからこそ慣れている C# を選んだのに、である。

C# スクリプトで IDE 支援を受ける方法は?なお、新生コンパイラサービス Roslyn をベースとした Microsoft 純正の「C# スクリプト」が登場する以前からも、「C# スクリプト」の実装・処理系は存在している。

そして、そうした旧来から存在する非純正「C# スクリプト」向けの Visual Studio 拡張もやはり存在する。
StackOverfolw などで、「.csx だとインテリセンス効かないんだけどどうしたらいい?」という質問に、その種の拡張を Visual Studio に入れたらいいよ、みたいな回答が付いてるのを見た覚えもある。

ということで、そういった拡張を入れれば良かったのかもしれない。

でも NuGet パッケージの参照とか対応しているのかなー、
拡張入れて試すのめんどくさいなー、
結局うまくいかなかったら徒労感はんぱないなー、
...とかいろいろ考えてしまって躊躇してしまった。

と、ふとここで大変遅まきながら思い出した。


そういえば F# スクリプトでよかったじゃん!


救世主 - F# スクリプトそう、Azure Functions は、その記述言語として、F# にも対応していたのであった。

F# はその登場時点から REPL 環境を提供し (fsiコマンド)、拡張子 .fsx によるスクリプト形式の体裁にも対応している。
そしてもちろん、F# スクリプト (.fsx) であっても、強力な IDE 支援は、コンパイル用の F# 記述 (.fs) と変わらず提供されているのだ。

そしてまた、ライブラリ類は C# と同じく .NET Framework を使い、C# と同じ NuGet パッケージで実装できる。
もちろん F# 固有のライブラリや構文を学ぶ必要はある。
しかし、こと自分の場合に限っては、過去に F# の習得を試みていたこともあり、Node.js の API をググりながら実装するよりは遥かに速く実装できることが予想された。

ということで「Azure Functions を使って Selenium WebDriver の新バージョンリリースの通知を受け取る」を、試しに F# スクリプトで実装することにしてみた。

使用する IDE は Visual Studio 2017 (2017年1月時点ではリリース候補版)。

で、コーディングを進めたところ...
d0079457_22302287.gif
(すっごく当たり前だけど) バッチリ IDE 支援が効いている!

Azure Table Storage を扱うところも、ちゃんとインテリセンスで候補が出るし、警告やエラーのインジケータも表示される。

かなりサクサクと実装を進めることができた。
最初から F# スクリプトを選択しておけばよかったかも、である。

Chrome Driver の新バージョンリリース検知のところだけ F# スクリプト化を試して満足。
成果物は GitHub に別ブランチとして掲載しておくことにした (下記 URL)。

https://github.com/jsakamoto/WebDriverUpdateDetector/blob/experimental/using-fsharp/ChromeDriverDetector/run.fsx


まとめIDE 支援のない C# スクリプトでコーディングするくらいなら、学習コストかけてでも事前に F# を少しでも習得しておいた上で、F# スクリプトを採用して Visual Studio or Visual Studio Code でコーディングしたほうが幸せになれそうな気がする。

ただし、もしかすると、何らかの Visual Studio 拡張を導入したうえでなら、C# スクリプトコーディングでも潤沢な IDE 支援を得られるのかもしれないが、未確認である。

おまけ...なんていう本記事を書こうと思ってたら、なんと、Azure Functions に、ビルド済みの .NET アセンブリをデプロイできるようになったとのこと。
Azure Functions のプリコンパイルサポート https://t.co/X6wqgouiSf #jazug #azurejp— こすもす.えび (@kosmosebi) January 6, 2017
すなわち、「C# スクリプト」という .csx ファイルで苦戦する必要なく、そしてまた F# が扱えなくとも、IDE 支援バリバリの、通常の C# プログラミングで開発を進められるということだ。

まだ自分は、この「ビルド済みの .NET アセンブリをデプロイ」方式は試していないが、こちらの方式のほうが本命かもしれない。
 

Azure Functions - 既存のファンクションに、SendGrid 連携によるメール送信をあとから付け足す

$
0
0
C# スクリプト、又は F# スクリプトで、「メールを送信する」という Azure Function App の "ファンクション" を実装する際の話。

Azure Functions では、"トリガー" (ファンクションを実行するきっかけ) やファンクションの実行結果として、様々な外部サービスと連携できるようになっている。

実行結果のひとつとして、メール送信サービスのひとつである SendGrid を使ってメール送信を行う、ということができる。

もちろん、C#/F# スクリプトでは、古くから .NET Framework に標準装備されている System.Net.Mail.SmtpClient クラスを用いることで、Azure Functions のファンクション中から任意の SMTP サーバに対してメール送信を行うことが可能だ。

とはいえそれはそれとして、Azure Functions が備える SendGrid 連携によるメール送信の実装がどのようなものか、試してみることにした。

新たなファンクションを作るなら...「実行結果として SendGrid 連携によるメール送信が行われる」という "ファンクション" を、Azure ポータル Web サイト画面上から新規に追加するのであれば、帝国兵さんの下記の Qiita 記事が詳しい。

Azure Function Appから定期的にメールを送る
http://qiita.com/superriver/items/d245539b7652b7512f9b

既存のファンクションに、ポータルから付け足すこともできそうだが...いっぽう、すでに実装済みの既存の "ファンクション" に対し、あとから「実行結果として SendGrid 連携によるメール送信が行われる」ようにすることも、当然可能である。

Azure ポータル Web サイト画面からは、当該 "ファンクション" のブレードを開いている状態から、「Integrate」カテゴリにて、「Outputs」の箇所で「+ New Output」をクリックすると、"ファンクション実行結果の出力内容" の選択肢がずらっと出てくる。
ここから「SendGrid」を選択すれば良いらしい。
d0079457_20245369.png
すると、画面に SendGrid 連携に関する必要構成項目の入力欄が現れ、宛先やら差出人アドレスやらが設定可能だ。
d0079457_2024579.png
ただこれにはちょっと落とし穴があって、ここまでの作業だけでは足りない。
詳しくはあとで説明するが、さらに Run.csx の変更も必要なのだ。

外部からデプロイしてるとポータル画面からは操作できないさらに言えば、ローカル開発環境で開発していてそれを「Web発行」や ローカル Git リポジトリからの "git push"、はたまた CI 連携などの方法で Azure Functions 上にデプロイしている場合は、Azure ポータル Web サイト画面上での上記操作はロックダウンされてしまう(当たり前だが)。

ということで、手作業で各種ファイルを編集することで、既存の "ファンクション" に、実行結果として SendGrid 連携によるメール送信が行われるように変更する手順を明らかにしてみた。

仕組み・構造改めて「"ファンクション" の実行結果として SendGrid 連携によるメール送信が行われる」仕組み・構造をまとめると以下のとおりだ。

"ファンクション" の戻り値に、SendGrid 用ライブラリに収録の Mail 型のオブジェクトを返すように構成することで、その "ファンクション" が実行された結果の戻り値で返却された Mail オブジェクトを用いてメール送信が実行される。


準備・前提条件何はともあれ SendGrid サービスを使うのだから、SendGrid にサインアップして SendGrid アカウントを取得しておく必要がある。

Azure Functions で~という話なので、Azure のアカウントからマーケットプレイスで特典アカウントとして SendGrid アカウントを作成するのがよいだろう。

SendGrid アカウントができたら、API キーを生成し入手。

さらに、その API キーについて「Mail Send」「Alerts」「Email Activity」「Stats」の機能アクセス可否を「Full Access」に設定 ( Full Access の選択肢がないものは 「Read Access」) しておく。

このあたりの手順は先に紹介した Qiita 記事を参照されるとよろしいかと (URL再掲)。

準備1: SendGridのアカウントを作る
http://qiita.com/superriver/items/d245539b7652b7512f9b#%E6%BA%96...

準備2: SendGridのAPIキーを得る
http://qiita.com/superriver/items/d245539b7652b7512f9b#%E6%BA%96...

実装手順すでにある "ファンクション" に、実行結果として SendGrid 連携によるメール送信が行われるようにする改造・変更手順は以下のとおり。

※なお、以下の説明では C# スクリプトで実装していることを前提に進めるが、F# スクリプトでも編集内容に大差ない。

"project.json" に、SendGrid NuGet パッケージを使用する旨を追記。{
"frameworks": {
"net46": {
"dependencies": {
...
"Sendgrid": "8.0.5"
}
}
}
}
※どうも、Azure ポータル Web サイト画面上での実装作業においては、project.json がなくても SendGrid のアセンブリが参照可能になっているように見える。が、とりあえずローカル開発環境でインテリセンス等の IDE 支援を得ることを前提として、上記のとおり NuGet パッケージを明示的に示す説明とする。

"function.json" に、SendGrid によるメール送信に関する構成を追記。{
"bindings": [
...,
{
"type": "sendGrid",
"name": "$return",
"direction": "out",
"apiKey": "SendGridApiKey",
"from": "Azure Functions <samples@functions.com>",
"to": "foo@example.com"
}
],
"disabled": false
}ちょっと落とし穴だったのが、"apiKey" プロパティの設定。

上記では「"apiKey": "SendGridApiKey"」としているように、
ここに実際の API キーを書いてはいけない。

このプロパティで指定しているのは、そのプロパティ名とは裏腹に API キーそのものではなく、
API キーを格納しているアプリケーション構成のエントリ名を指定
しているのだ。
(今さらだけど、なんでこんなプロパティ名にしたんだ...orz)

なので、後述するとおり、Azure ポータル Web サイトのアプリケーション構成設定画面にて、"SendGridApiKey" というエントリ名で SendGrid の API キーを設定追加する必要がある。

ちょっと話が逸れるがもうひとつ。

上記要領の "function.json" の編集が、どうやら、Azure ポータル Web サイト画面上から「+ New Output」したときの編集内容に該当する模様だ。

なので、もしも Azure ポータル Web サイト画面上でコーディングしてて、「Integrate」カテゴリで「+ New Output」で SendGrid を追加した場合は、ここまでの手順は不要で、このあとの Run.csx ( F# なら Run.fsx ) の編集以降の手順を行えばよい。

さて、話を戻して。

run.csx にて、SendGrid NuGetパッケージで提供される、SendGrid アセンブリへの参照設定を追記。
ついでにコードを略記できるよう名前空間「SendGrid.Helpers.Mail」も開いておく。#r "SendGrid"
using System;
using SendGrid.Helpers.Mail;
...

ファンクション本体である "Run" 静的メソッドの戻り値を、(SendGrid アセンブリが提供する、SendGrid.Helpers.Mail 名前空間の) Mail 型とするよう変更。public static Mail Run(...)
{
...

送信したいメール内容のとおり Mail オブジェクトを組み立てて、Run メソッドの戻り値として返却。public static Mail Run(...)
{
...
var message = new Mail()
{
Subject = "(件名)"
};
var content = new Content
{
Type = "text/plain",
Value = "(メール本文)"
};

message.AddContent(content);
return message;
}
最後に、Azure ポータル Web サイトの画面にて、「Function app setting」カテゴリから「Configure app settings」を開いて、アプリケーション構成に SendGrid の API キーを掲載したエントリを追加。
d0079457_202518.png
このときのエントリ名は、"function.json" 中で指定した、"apiKey" プロパティの指定に従う。
d0079457_2025633.png

以上で、このファンクションが実行されると SendGrid 連携によるメール送信が行われるようになる。

メール送信についてエトセトラここまでの説明で例示した "function.json" では、宛先と差出人アドレスだけ指定していたが、件名や本文も "function.json" 中にて指定可能だ。

たとえば "function.json" に{
"bindings": [
...,
{
"type": "sendGrid",
...
"from": "Azure Functions <samples@functions.com>",
"to": "foo@example.com",
"subject": "Hello",
"text": "World"
}
],
...
}というように subject プロパティと text プロパティを追記しておけば、Run メソッドからは下記のとおり Mail オブジェクトを new して返すだけで、ほかのコーディング不要で、"function.json" にて指定された宛先・差出人アドレス・件名・本文にてメール送信が行われるようになる。return new Mail();
はたまた、その逆というか、Run メソッド中でことごとメール送信内容を指定すれば、"function.json" での指定よりも優先してその指定内容が採用される。
なので、宛先や差出人アドレスも、Run メソッド中で実行時に指定可能だ (下記例)。return new Mail(
from: new Email("foo@example.com"),
subject: "(subject)",
to: new Email("bar@example.com"),
content: new Content(type: "text/plain", value: "(body)"));
あと、ファンクションが実行されてもメールを送信したくない場合は、Run メソッドの戻り値として null を返せばよいようだ。return null;
まとめ以上のとおり、Azure Functions が備える SendGrid 連携を使ってメール送信を行うよう、ファンクションを構成することができることがわかった。

System.Net.mail.SmtpClient を使った場合と比較して、Run メソッド中の記述量が減り、本来の「こういう内容をメール送信したい」という、
"やりたいこと" に集中して記述できる
のはよいと感じた。

とくにメール送信のサービスとして SendGrid を採用している場合は、API キーに基づいた認証・アクセス権管理となるので、セキュリティ面でも System.Net.mail.SmtpClient を使うよりは安全・柔軟だと思われる。

参考) SendGrid ブログ「APIキー認証でセキュリティを強化しよう」
https://sendgrid.kke.co.jp/blog/?p=3659


ただし、1回のファンクション実行によって、複数のメール送信を行いたい場合は、この方式では対応できなさそう、と感じた。

もっとも、ファンクションは単機能にして、複数のファンクションを連携・積み重ねてより複雑・大規模な構成へと構築していくのがベストプラクティスとは思うので、メール送信の粒度ごとにファンクションを分ければ解決するかもしれない。

それでもどうしても1回のファンクション実行で複数のメール送信の必要がある場合は、Azure Functions プラットフォームによる SendGrid 連携はやめて、System.Net.mail.SmtpClient を使った古典的な実装に戻すか、あるいはSendGrid.Helpers.Mail 名前空間下のクラスライブラリを自前で呼び出して SendGrid API 経由でのメール送信を自前実装するのがよいのだろう。
 

Azure Functions - Azure Table Storage との連携を試す

$
0
0
以前の投稿で、Azure Functions を活用することで、各種 Selenium WebDriver ファイルのバージョンアップを定期的に監視、メールで通知する、C# 実装による仕組みを作ったことを書いた。

Azure Functions を使って Selenium WebDriver の新バージョンリリースの通知を受け取る
http://devadjust.exblog.jp/23744108/

さて、新バージョンリリースを検知するためには、それまで時点でわかっている最新バージョンをどこかに保存・永続化しておく必要がある。

新バージョンリリースのチェック実行時、今回チェック時に取得したバージョン番号と、その保存・永続化してあったバージョン番号とを比較することで、新たなバージョンがリリースされたかどうかが判断できるためだ。

そのように最新バージョン番号を保存・永続化する具体的な手立てとして、安価で使い慣れている Azure Table Storage を採用したのであった。

そして前回投稿に書いたように、C# によるファンクション中から Azure Table Storage の読み書きを行うにあたっては、Azure Functions だからといって特別なことは何もない、いたって普通の C# による Azure Table Storage アクセスコードをゴリゴリと記述することで済ませていた。

しかし、である。

Azure ポータル Web サイト上での、Azure Functions の管理画面をいじっていると、入力 (Input) や出力 (Output) のバインディング(連携)先の選択肢に、"Table Storage" という選択肢が用意されているのだ。
d0079457_22452579.png

ということで、Azure Functions が予め用意してある、Azure Table Storage との連携方法について調べてみた。

...なお、先に断っておくと、本記事に記載の内容は、下記の公式ドキュメントサイトをなぞった内容が多くを占めているので、その点は悪しからず。

Azure Functions Storage table bindings
https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-table

連携その1. Azure Table Storage のエンティティを引数にもらうまずは入力から。

つまり、ファンクションの実行時に、(本稿は C# スクリプトでのファンクション実装を前提にしているので) Run 静的メソッドの引数に Azure Table Storage のエンティティ ( ≒ レコード ) を渡してもらえる、という連携だ。

Azure Functions は、Azure Table Storage の入力連携に関して、"条件に該当するエンティティを最大 n 件取得して引数に渡してもらう" など、いくつかのシナリオに対応している。
ここでは、予め決めておいた特定の 1 エンティティを引数に渡してもらう方法について記す。

とりあえずは何か適当なファンクション (タイマーで起動とか、手動で起動とか) がすでに作成済みであるとしよう。

※以降では、タイマー起動のテンプレートで作成した既存のファンクションに対し、編集する例で示す。
function.jsonまずはファンクションの引数や戻り値をどう外部と連携させるかなどを設定している JSON ファイル「function.json」を編集する。

※註: Azure ポータル Web サイト画面上からは、既定では "Standard editor" と称される GUI フォーム画面から function.jsonでの構成内容を編集可能となっている。しかし、この GUI フォーム画面は、本稿作成時点では不都合な振る舞いがあったりする (例えばここでの説明のように "読み取る最大件数" を省いて設定したい場合に、しかし GUI フォームの "Standard editor" では既定の "50" が再表示されてしまう、など) 。そのため、function.json をテキストとして直接編集する "Advanced editor" モードに切り替えて編集する (下図)。
d0079457_12425624.png
下記の要領で、bindings に type="table" の項目を追加する。{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 0 * * *"
},
{
"type": "table",
"name": "tableEntity",
"tableName": "Foo",
"partitionKey": "fizz",
"rowKey": "buzz",
"connection": "StorageConnectionString",
"direction": "in"
}
],
"disabled": true
}プロパティ名と上記記載例から概ねくみ取ってもらえると思うが、function.json にて、"1 エントリ" を特定する情報をすべて記載しているのがポイントだ。

上記例に基づき解説を加えると下記のとおりだ。ストレージアカウントへの接続文字列を記載したアプリケーション構成のエントリ名 = "StorageConnectionString"そのアカウント中の Table 名 = "Foo"その Table 中の1エンティティを特定するパーティションキー = "fizz" と行キー = "buzz"例によって、接続文字列などのパスワードやキーを含む設定内容は、アプリケーション構成のほうに設定しておき (下図)、function.json からは、その設定エントリ名で参照する仕組みだ。
d0079457_1233167.png
run.csxさて次は、対する C# (スクリプト) の実装。

まず、受け取るエンティティを表現するクラスを、run.csx の冒頭で記述する。
例えばこんな感じ。public class FooBar {
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string SomethingElse { get; set; }
}TableEntity クラスから派生する必要がないのは知らなかった。
その代わり、PartitionKey と RowKey のプロパティは必須である。
あとは、function.json で指定した連携対象の Azure Table と同じ構造となるようにプロパティを備えれば OK だ。

あとは Run 静的メソッドの引数に、function.json で指定したのと同じ引数名で、先述のクラスの型を引数を受け取るようにすればよい。public static void Run(
TimerInfo myTimer,
FooBar tableEntity,
TraceWriter log
){
...
}以上で、所定の接続文字列で接続したストレージアカウントの、"Foo" テーブル中、パーティションキー = "fizz"、行キー = "buzz" のエンティティが、FooBar 型のオブジェクトにマッピング・逆シリアル化されて、それが引数 tableEntity に渡されて Run 静的メソッドが実行されるようになる。
補足・感想なお、function.json で指定した条件に合致するエンティティが見つからない場合は、"Object reference not set to an instance of an object." という例外で落ちるようだ。

このようなシナリオ ( 予め読み取り条件が固定の "特定の1エントリ" だけを参照したい ) では、エンティティの特定条件を function.json に宣言的に記述するだけで、C# コード上は Azure Table Storage にアクセスするコードを一切書かずに、いきなりエンティティにマッピングされたオブジェクトとして入手・参照できる。

これはかなりシンプルで便利だと思う。

先述のとおり、公式ドキュメントサイトを見るとわかるように、入力連携にはほかにもいくつか対応シナリオがある。
目的・要件と、Azure Functions 側で用意してあるシナリオ・連携方式が合致する場合は、効率よく記述できそうだ。
連携その2. Azure Table Storage にエンティティを追加する次いで出力。

つまり、ファンクションの実行によって、Azure Table Storage にレコード追加される、という連携だ。

入力の例の場合と同様、何か作成済みのファンクションについて、話を進める。
function.jsonまずは function.json から。

下記要領で連携方法の記述を追加する。{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 0 * * *"
},
{
"type": "table",
"name": "outputTable",
"tableName": "Foo",
"connection": "StorageConnectionString",
"direction": "out"
}
],
"disabled": true
}コンテナへの接続文字列の指定と、エンティティ追加対象となるテーブル名までを function.json で指定しておく。
run.csx次いで C# コード (run.csx)。

何はともあれ、対象 Table のエンティティを表すクラスを定義しておく。
ここは入力連携のときと同じ。public class FooBar {
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string SomethingElese { get; set; }
}そして Run 静的メソッドの引数なのだが、function.json 中、name で指定したのと同じ引数名で、ICollector<エンティティにマッピングするクラス> 型の引数を追加する。public static void Run(
TimerInfo myTimer,
ICollector<FooBar> outputTable,
TraceWriter log
)
{
...
}あとは、この引数に渡された outputTable に対し、Add メソッドを使ってエンティティを表すクラスのインスタンスを追加するコードを書けばよい。public static void Run(
TimerInfo myTimer,
ICollector<FooBar> outputTable,
TraceWriter log
)
{
...
outputTable.Add(new FooBar{
PartitionKey = "fizz",
RowKey = "buzz",
SomethingElse = "wow!"
});
...
}こうすることで、引数 outputTable に追加されたオブジェクトが、function.json で指定した Azure Table へのエンティティとして追加される。

補足・感想1回の Run メソッド呼び出しでも、その Run メソッド中で繰り返し Add メソッドを呼び出して複数オブジェクトを追加すれば、追加したオブジェクトぶんの複数エンティティが Azure Table に追加される。

なお、パーティションキー・行キーが重複するオブジェクト(エンティティ)を追加した場合は、Run メソッドを抜けたあと、ICollector<T> の引数に追加した内容をフラッシュするタイミングで、下記のような例外が発生するようだ。

Error while handling parameter outputTable after function returned:. Microsoft.WindowsAzure.Storage: 0:The specified entity already exists

感想としては、Azure Table Storage との接続処理などをこまごま C# コード側に明記しなくて済むので、うれしい印象だ。
では、更新はどうやるの?さて、ここまでのシナリオでは、エンティティを読み取るか、追加するかだけで、既存のエンティティを更新するシナリオがなかった。

冒頭で紹介した公式ドキュメントサイトを見ても、本記事投稿時点では、なんと、
更新の方法について記載がない。

ということで、更新をやるにはどうしたらよいか、粘り強くネット上で検索してみた。
すると StackOverflow に有力情報のスレッドを見つけた。

Azure Functions Table Binding: How do I update a row?
http://stackoverflow.com/questions/36792547/azure-functions-table-binding-how-do-i-update-a-row

上記スレッドによれば、ある程度は Azure Functions による連携支援はあるものの、基本的には従来通りの Azure Table Storage にアクセスする典型的なコードを記述するしかないとのことだ。

実際にやってみよう。
function.json例によって、とりあえず何かファンクションはある状況から開始するとして。
function.json の記述はこうだ。{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 0 * * *"
},
{
"type": "table",
"name": "cloudTable",
"tableName": "Foo",
"connection": "StorageConnectionString",
"direction": "in"
}
],
"disabled": true
}出力連携のときとほぼ同じだが、"direction" (方向) の指定が "out" (出力) ではなく "in" (入力) になってることに注意。
run.csxそして C# コード (run.csx) 側であるが、まず冒頭に、Microsoft.WindowsAzure.Storage アセンブリへの参照が必要になる。#r "Microsoft.WindowsAzure.Storage"記述を短縮化するために、名前空間 Microsoft.WindowsAzure.Storage.Table も冒頭でオープン (using) しておこう。using Microsoft.WindowsAzure.Storage.Table;で、先述の連携方法では、何からも派生させることは不要だったエンティティを表すクラスだが、この更新系の実装においては TableEntry からの派生が必要となる。public class FooBar : TableEntity {
public string SomethingElese { get; set; };
public FooBar(){ }
public FooBar(string partionKey, string rowKey) : base(partionKey, rowKey){ }
}そして Run 静的メソッド。

functuon.json 中で指定した引数名で引数追加となるが、引数の型は CloudTable とする。public static void Run(
TimerInfo myTimer,
CloudTable cloudTable,
TraceWriter log
){
...
}あとは、こうして引数に渡された CkoudTable オブジェクトに対し、Azure Table Storage の読み書き処理を記述するだけだ。

例えば以下のような感じ。public static void Run(
TimerInfo myTimer,
CloudTable cloudTable,
TraceWriter log
){
// パーティションキー="fizz",行キー="buzz"のエンティティを検索
var query = TableOperation.Retrieve("fizz", "buzz");
var result = cloudTable.Execute(query);

// 該当エンティティがあればその参照を取得、なければ new で新規作成
var foobar = result.Result as FooBar ?? new FooBar("fizz", "buzz");

// エンティティの内容を書き換え
foobar.SomethingElese = "hey!";

// 無ければ追加、あれば更新
var upsert = TableOperation.InsertOrReplace(foobar);
cloudTable.Execute(upsert);
}補足・感想読み込みのみ、または新規追加のみの連携と比べると、抽象度がぐっと下がってしまい、"生の" Azure Table Storage 読み書きのコードとなってしまった。

とはいえ、接続を開設して CloudTable オブジェクトを用意するところまでは書かずに済む。
まとめAzure Table Storage に対し「読むだけ」「追加するだけ」の場合は、Azure Functions が用意してくれている連携の仕組み ( バインディング ) により、ファンクションを実装する側はずいぶんとシンプルに書けるようになっていい感じである。

いっぽうで、既存のエンティティを「更新」する仕組みはずいぶんと荒削りだ。
生々しい Azure Table Storage へのアクセスコードを書くしかないのが今日時点での実情である。

しかし先の StackOverflow のスレッドによれば、"These steps will get easier with our next release," とのこと。
今後の Azure Functions の進化・アップグレードにより、"更新" シナリオについてももっとシンプルな実装手段が提供されることが期待はできそうだ。

それまでの間は、Azure Table Stoarge に対し更新系の連携が必要な場合は、上記のとおり CloudTabel オブジェクトで連携する手法でしのぐのが無難そうである。

それでも接続文字列の取得からはじめてすべて自前で記述するよりはマシだろう。

以上、Azure Functions における Azure Table Storage バインディングのまとめとする。
 

Visual Studio Code で「保存時に自動整形」「タイプ時に自動整形」を有効にする

$
0
0
Visual Studio のソースコード自動整形自分は仕事や趣味で、C# や TypeScript によるプログラム作成を行うことが多い。

そしてその際に使用するエディタ、というか統合開発環境として Windows OS 上で Visual Studio を使う場合がほとんどだ。

※(本記事投稿時点では Visual Studio 2015 及び 2017 RC を専ら使用。

そしてその Visual Studio のエディタ機能であるが、"行末のセミコロンを打鍵" といった、タイピング上の所定のイベントごとに、そのタイピング中であったソースコード行を自動整形してくれる (下図)。
d0079457_21351480.gif

エディタによる自動整形は、開発メンバー間でソースコードの書式がブレず、誰が編集しても一貫した体裁になるので大変よろしい。
また、自動なので、いちいちスペースを空けたりなどなど、細かい作業に時間や集中力を奪われないのも利点だ。
"保存時に整形" は標準装備されてない但し、Visual Studio 標準では、ソースファイル保存時にソースコード全体をいっきに自動整形はやってくれない。

Ctrl + K, Ctrl + D のキーコンビネーションで、いつでもソースコード全体のいっき自動整形を発動することはできる。
しかしだからといって、Ctrl + K, Ctrl + D, Ctrl + S の 3 タイプキーコンビネーションを習慣づけるというのは、なんだかおかしい気がする。

ということで、そこは Visual Studio に追加の拡張 (アドイン) をインストールことで解決だ。

下記のアドインを Visual Studio にインストールすることで、Ctrl + S などによるソースコード保存時に、当該ソースコード全体をいっきに自動整形してくれる。

Format document on Save - Visual Studio Marketplace
https://marketplace.visualstudio.com/items?itemName=mynkow.FormatdocumentonSave

Visual Studio "Code" の場合さてそんな自分だが、たまに Visual Studio Code を使うこともある。

※一時期は、Sublime を評価したり Atom使ってた時期もあった。が、今は Visual Studio Code のみ。

しかしながら、自分が Visual Studio Code を使っている限りでは、「タイプ時に自動整形」や「保存時に自動整形」は発動せず、自動整形したければ Alt + Shift + F を打鍵する必要があった。

ちょっとこれは不便だなー、せめて「保存時に自動整形」は欲しいなー、と常々思っていた。

で、Visual Studio Code は拡張 ( アドイン ) の開発が盛んのような気がするので、「保存時に自動整形」してくれる Visual Studio Code 拡張も誰かが作ってくれてるに違いない!と気が付き、Visual Studio Marketplace で検索してみた。

そうすると、大変ありがたいことに、下記の Visual Studio Code 拡張が見つかった。

Format on Save - Visual Studio Marketplace
https://marketplace.visualstudio.com/items?itemName=gyuha.format-on-save

なるほど、ではこの拡張をインストールしてみようかなー、と思いつつ、レビューに目を通してみると...
d0079457_21472919.png
...どうやら、Ctrl + S 押す ⇒ ファイルが保存される ⇒ 自動整形が発動、という処理の流れになってしまっているようだ。

つまりこの VSCode 拡張では、たしかに Ctrl + S などの保存処理で自動整形が発動するものの、もう既にファイルに保存されたあとで整形されてしまっている、ということらしい。
なので、この拡張の利用者は、整形されたあとの内容を確実にファイルに保存するために、もういちど重ねて Ctrl +S するなどしてファイル保存を2回実行することで問題回避しているようだ。

レビューには「Ctrl + S x 2回押すの、何とかならんかねー」といった投稿が散見された。

他に手段は?そこで、性急にこの拡張をインストールするのは踏みとどまり、Visual Studio Marketplace での検索ではなく、Google 検索でほかに手段がないか検索してみた。

そしたらなんと。

Visual Studio Code の標準機能として「保存時に自動整形」機能が追加されたというではないか。

時期は September 2016 リリース、バージョン v.1.6 から、「保存時に自動整形」機能が Visual Studio Code 標準で搭載されているという (下記参照)。

Visual Studio Code Updates - September 2016 (version 1.6)
https://code.visualstudio.com/updates/v1_6#_format-on-save

ただ「保存時に自動整形」機能は標準で搭載はされているが、既定ではこの機能は Off になっているらしい。

そこで以下の手順で、「保存時に自動整形」を有効化することにした。

VSCode で "保存時に整形" を有効化するまずは Visual Studio Code を起動。

起動したら、Visual Studio Code のメニューから、[File]-[Preferences]-[User Settings] を選択。

※ここではユーザー設定で変更することにしたが、そのほうがよければ、ワークスペース設定で設定を適用しても構わない。

するとエディタ領域に、設定のための JSON ファイル (settings.json) が開く。
左の領域にはシステム既定値が表示され、右半分の領域に、ユーザー設定を記述するエディタ領域が開かれる。

左側の領域では検索が効く。
そこで、その検索ボックスに "formatOn" と検索語を入力して目的の設定項目に絞り込み。

これで "editor.formatOnSave" という、いかにもな名前の設定項目が見つかるはずだ。
その設定項目にマウスカーソルを当てるとエンピツアイコンがホバー表示される。
そのエンピツアイコンをクリックすると、true と false の選択しがポップアップする。
もちろんここでは true を選択。
d0079457_215958.png
すると画面右半分の編集領域に設定項目が転記されて、ユーザー設定として保存される。

もちろん手作業で、画面左側から目的の設定項目を選択してクリップボードにコピー、右半分の編集領域に貼り付けてから、書き換えても同じだ。

これで Visual Studio Code 本体の標準機能で「保存時に自動整形」が実施されるようになった。

快適!

もうひとつ、おまけが...!
...ということなのだが、よくよく見ると、"formatOn" で設定項目の絞り込みした結果に "editor.formatOnType" という項目がヒットしているではないか。

"format on type"、すなわち
「タイプ時に自動整形」
ではないのか!?

そこで早速、先の要領でこの "editor.formatOnType" も、ユーザー設定で true に設定してみた。

これでタイプしてみると...


d0079457_2135869.gif

おお、ちゃんと「タイプ時に自動整形」が発動している!

まとめ
ということで、Visual Studio Code は v.1.6 以降は「保存時に自動整形」が本体に標準装備されているとのこと。

ただし、既定では無効化されているので、お好みに応じてユーザー設定等で有効化するとよい。

また、どのバージョンからかはよく調べてないが、少なくとも v.1.8.1 では、「タイプ時に自動整形」の設定項目も用意されていた。

これもユーザー設定等で有効に設定すると、ちゃんと「タイプ時に自動整形」が機能する。

なお、先に発見した、Visual Studio Code 拡張であるが、Ctrl + S を2回押さないと自動整形後の内容が保存されないという点はあるものの、自動整形を発動させるファイル種別を、設定ファイルで拡張子を指定することで仕分けできるとのこと。

Visual Studio Code 標準装備の自動整形機能はファイル種別による発動有無の指定はできず、本機能を有効にしたならば、すべてのファイル種別に対して自動整形が発動する。

場合によっては、Ctrl + S x 2回押しでもなお、ファイル種別に応じた自動整形の発動有無の指定ができる VSCode 拡張を使ったたほうがよいかもしれないので、適宜使い分けるといいかもしれない。
 

Azure CLI 2.0 ("az"コマンド) を使って Azure SQL Database のファイヤーウォールを開閉する

$
0
0
SQL Database の IP アドレスによるアクセス制御自分は、仕事や趣味で Web アプリなどを作る際、その永続化層として SQL Database ― Azure 上で提供される、サービスとしての SQL Server ― を利用することが多々ある。

そしてこの SQL Database は、セキュリティ機能のひとつとして、予め指定した IP アドレスからのアクセスしか受け付けないようにする "ファイヤーウォール" の機能が付いている。

当然のことながら、普段は同じ Azure 上の App Services に配置してある Web アプリからしかアクセスできないように SQL Database のファイヤーウォールを構成して安全を保つようにしている。

しかし、もちろんのこと、自分の手元の PC から SQL Management Studio などを使って SQL Database に接続する必要に迫られるときもある。

そのような必要が発生したら、その都度、自分の PC (からのアクセス) のグローバルIPアドレスをアクセス許可するよう、SQL Database のファイヤーウォールに規則を追加し、用が済んだらそのファイヤーウォール規則を削除、という運用をしていた。

しかしこのファイヤーウォール規則の追加・削除を、Azure ポータル Web サイト上でぽちぽちやるのは結構めんどくさい。

こういう定型処理は GUI じゃなくて CUI でスクリプト化しておくと便利だ。

Azure CLIそこで活用するのが、"Azure CLI" と呼ばれる、コマンドラインから呼び出して Azure の諸々の操作を行うプログラムだ。

Azure をコマンドラインから操作するプログラムとしては、まず、Windows PowerShell モジュールとして配布されている「Azure PowerShell」がある。

Get started with Azure PowerShell cmdlets
https://docs.microsoft.com/en-us/powershell/

これに加えて、macOS や各種 Linux ディストリビューションでも使用できるクロスプラットフォーム版がある。

このクロスプラットフォーム版の Azure CLI、現在、第2世代が絶賛開発進行中。

Microsoft Azure CLI 2.0 - Preview
https://github.com/Azure/azure-cli

このブログ記事を書いている時点ではまだプレビュー版で、v.0.1.1b2 がつい先日リリースされたばかりだ。

この Azure CLI の第2世代バージョンであるが、実は、そのつい先日の v.0.1.1b2 がリリースされるまで、SQL Database のファイヤーウォールを操作する機能が未搭載であった。

しかしついに、v.0.1.1b2 の登場によって、Azure CLI 第2世代での SQL Database のファイヤーウォール操作ができるようになったのだ。

ということで早速、Azure CLI 2.0 を使用した、ファイヤーウォールの開閉処理をスクリプト化してみた。

Azure CLI 2.0 のインストールしかしまずは Azure CLI 2.0 をインストールからである。

この第2世代の Azure CLI は Python で実装されているらしい。
そのため、Pyhon とそのパッケージマネージャである "pip" コマンドが使える状態であれば、下記コマンドを実行することでインストールできる。> pip install --user azure-cliこれで Azure CLI 2.0 の主要な機能がインストールされ、Azure CLI、すなわち "az" コマンド が使えるようになる。

...使えるようになる、はずだった。

だが、どうも自分の環境では、"--user" オプション付きでインストールされる先のフォルダパスが、Python のインストール時に自動では環境変数 PATH に追加されていなかったようで、コマンドプロンプトで az コマンドを叩いても "'az' is not recognized...." とエラーになってしまった。

そこで環境変数 PATH に、%APPDATA%\Python\Python36\Scriptsを足すことで、コマンドプロンプトで az が実行できるようになった。

さらにサブコマンドの追加インストールしかしこれだけでは、残念ながら SQL Database を操作する機能は付いてこないようだった。

そこで、今一度、pip を使って、今度は azurre-cli-sql というパッケージを追加でインストールする。> pip install --user azure-cli-sqlこれで "az" コマンドに、"sql" というサブコマンドが追加され、SQL Database に対する操作 ― ファイヤーウォールの開閉操作も含む ― が可能となった。

あとは、"az sql -h" というように -h スイッチをつけながらサブコマンドに何があるか調べながら進めていくと、すぐに使い方はわかるであろう。

なお、実際に az コマンドを使ってなにか Azure に対する操作を行おうとする際は、予め az login コマンドを実行し、コンソールに表示される指示に従ってログインを済ませておく。

ファイヤーウォール規則を追加・削除するスクリプト例ファイヤーウォール規則に、自分の PC からのアクセスにおけるグローバル IP を許可する規則を追加する PowerShell スクリプトの例として allow.ps1、規則を削除する PowerShell スクリプト例として deny.ps1 を、下記に開示する。
# allow.ps1

$serverName = "(server name)"
$resourceGroupName = "(resource group name)"
$ruleName = "(rule name)"
$globalIPAddress = curl.exe -s http://inet-ip.info

echo "Global IP Address of this site: $globalIPAddress"

az sql server firewall create --firewall-rule-name $ruleName --server-name $serverName -g $resourceGroupName --start-ip-address $globalIPAddress --end-ip-address $globalIPAddress > $null
az sql server firewall list --server-name $serverName -g $resourceGroupName -o table

pause# deny.ps1
$serverName = "(server name)"
$resourceGroupName = "(resource group name)"
$ruleName = "(rule name)"

az sql server firewall delete --firewall-rule-name $ruleName --server-name $serverName -g $resourceGroupName %gt; $null
az sql server firewall list --server-name $serverName -g $resourceGroupName -o table

pause※事情は割愛しますが「PowerShell スクリプトで書くんだったら、はじめから Azure PowerShell 使えばいいのでは?」というツッコミはなしで。
補足: 自 PC からのアクセスでのグローバル IP を知るには?"自分の PC からのアクセスにおけるグローバル IP" であるが、固定IPアドレスが割り振られた環境であれば、その契約したIPアドレスは自明なので何の問題もないだろう。

しかし モバイル回線でテザリングしている場合など、グローバル側の IP アドレスはころころ変わる環境では、スクリプト中に IP アドレスを記載しておくわけにはいかない。

そこで上記のスクリプト例では、"自分の PC からのアクセスにおけるグローバル IP" を知るために、http://inet-ip.info に HTTP 要求を送る、という方式で実装してみた。

http://inet-ip.info は、HTTP 要求元の IP アドレス文字列を、HTTP 応答で返してくれるというサービスだ。

curl を使って http://inet-ip.info に HTTP 要求を送れば、自分の PC からのアクセスにおけるグローバル IP アドレスが標準出力に表示される、という寸法である。

※自分の環境は、Git for Windows をインストールしており、付属のコマンドにも PATH を通してあるので、curl コマンドをはじめ、多くの Unix コマンドが使えるようになっている。なお、PowerShell では "curl" というコマンドはエイリアスで PowerShell の Invoke-WebRequest コマンドレットが実体になっているので注意。そのため、上記スクリプト例では、拡張子の ".exe" まで指定して curl を呼び出すようにしてある。

まとめ...ということで、第2世代の Azure CLI でも、ついに SQL Database の操作がいろいろと可能になった。

引き続き、定型処理のスクリプト化に az コマンドの活用の幅を広げていきたいと思う次第。

SQL Server 2016 からエクスポートした bacpac を SQL Server 2014 にインポートしたらエラー

$
0
0
SQL Server 2016 Express 上で作成したデータベースを SQL Server 2014 Express 上にコピーしたい
SQL Server 2016 Express 上で作成したデータベースを、SQL Server 2014 Express 上にコピーする必要が発生した。

もちろんというか幸いというか、このケースでは、SQL Server 2016 固有の機能は使っていないデータベースなので、SQL Server 2014 上に "コピー" しても問題なく動作する算段は付いている。

ただし、SQL Server には "データベースのコピー" というそのまんまの機能は搭載されていない (...はず。ですよね?)。

代わりに (少なくとも自分にとって) 考えられる選択肢は以下の 3 つだ。
    データベースファイル (.mdf, .ldf) のデタッチ & ファイルコピー & アタッチデータベースのバックアップとリストアデータ層アプリケーションのエクスポートとインポート
但し上記手法の中でも、コピー元/先の SQL Server のバージョンが違っていても使える方法は、3 番目の "データ層アプリケーションのエクスポートとインポート" だけだ。

データ層アプリケーションのエクスポートとインポート"データ層アプリケーションのエクスポート" を実行すると、エクスポート対象のデータベースについて、そのスキーマやレコードがまるっとひととおり、(既定では) 拡張子 .bacpac のファイル (実体は Zip アーカイブ) に吐き出される。

"データ層アプリケーションのインポート" を行うと、インポート先の SQL Server 上に .bacpac の収録内容でデータベースを再現してくれる。

データ層アプリケーションのエクスポートとインポートは、SQL Server Management Studio 上から操作が可能だ。

具体的には、まずはエクスポートだが、SQL Server Management Studio のオブジェクトブラウザウィンドウ上で、エクスポートしたいデータベースを右クリックし、メニューから [タスク] > [データ層アプリケーションのエクスポート] を選択すればよい。
するとエクスポートタスクのウィザードウィンドウが開くので、あとはエクスポート先の .bacpac ファイルのパスを指定して実行すれば完了だ。

ついでインポートだが、SQL Server Management Studio のオブジェクトブラウザウィンドウ上で、データベースのノードを右クリックし [データ層アプリケーションのインポート] を選択する。
こちらもやはり、インポートタスクのウィザードウィンドウが開くので、インポートする .bacpac ファイルを選択して実行してやればよい。
エラー発生!さて、このように "データ層アプリケーションのエクスポートとインポート" を用いることでバージョン違いの SQL Server 間でもデータベースの "コピー" ができるはずなのだが、今回いざ実行してみたところ、なんと下記のようなエラーが発生してしまった。
d0079457_21322370.png
失敗した処理の右側には "エラー" と表示されてハイパーリンクになっており、これをクリックすると下記のとおりエラー発生の詳細内容が記されていた。
Could not import package.
Warning SQL0: A project which specifies SQL Server 2016 as the target platform may experience compatibility issues with SQL Server 2014.
Warning SQL72012: The object [FooBar_Data] exists in the target, but it will not be dropped even though you selected the 'Generate drop statements for objects that are in the target database but that are not in the source' check box.
Warning SQL72012: The object [FooBar_Log] exists in the target, but it will not be dropped even though you selected the 'Generate drop statements for objects that are in the target database but that are not in the source' check box.
Error SQL72014: .Net SqlClient Data Provider: Msg 102, Level 15, State 1, Line 1 'COLUMN' 付近に不適切な構文があります。
Error SQL72045: Script execution error. The executed script:
GRANT VIEW ANY COLUMN ENCRYPTION KEY DEFINITION TO PUBLIC;
このエラー内容詳細に最初は圧倒され、また、内容を読み進めても意味不明 ("元になくて先にあるオブジェクトを削除するステートメントを作成" チェックを選択しても無駄じゃ、とかいうけどそんなチェックボックス見なかったぞ) だったが、あきらめずに最後まで根気よく読むと、最後の最後にヒントがあった!

すなわち、

"GRANT VIEW ANY COLUMN ENCRYPTION KEY DEFINITION TO PUBLIC" の実行がエラー

っていうのがそもそものエラーの本体だった。

ん?

ENCRYPTION (暗号化)?

同じ SQL Server でも、Express エディションには "透過的なデータ暗号化" の機能は含まれておらず、それ関係のステートメントを実行しようとしてるからエラーになるのか?
兎にも角にも、そもそも今回のケースでは "透過的なデータ暗号化" は使っていないのだが。

...と推測を立てたので、エクスポート元のデータベースがどうなっているか、SQL Server Management Studio 上であちこち設定を探し回ってみた。

すると、ありましたありました!

データベースのプロパティ中、パーミッションのカテゴリに、"View any column encryption key definition" の項目があるのと、"Grant" のチェックが On になっているのが!
d0079457_21321627.png
あわせて "View any column master key definition" の Grant" がチェック On になっているのも見て取れた。

これら 2 つの "Grant" のチェックを外して OK ボタンをクリックして権限削除。

そのうえで今一度データ層アプリケーションのエクスポートとインポートを実施してみたところ、今度は無事に成功。

こうしてどうにか SQL Server 2016 Express 上から SQL Server 2014 Express 上へ、データベースを "コピー" することに成功した。
 

Azure Web App で利用可能な Node.js のバージョンを調べる

$
0
0
Azure Web App と Node.js
Microsoft Azure 上の PaaS である Azure Web App であるが、当然のことながら、Node.js による Web アプリも稼働可能である。

PaaS なので特定の仮想マシンに強く結びつけられることもないため、Node.js の新バージョンがリリースされると、程なくしてその Node.js 新バージョンが (自分が立てた) Azure Web App サイトで使えるようになる。


Azure Web App で Node.js のバージョンを指定する方法
ということで、自分が立てた Azure Web App サイトにて Node.js の Web アプリを稼働させるにあたり、どのバージョンの Node.js 上で実行するかを指定可能だ。

下記の記事が参考になる。


しばやん雑記 - Microsoft Azure Web サイトの Node.js バージョンを切り替える方法
Azure アプリケーションでの Node.js のバージョンの指定

よく聞く方法は、Azure ポータルにて、対象の Web App サイトの「アプリケーション構成」の設定ページから、「WEBSITE_NODE_DEFAULT_VERSION」というエントリに、好みの Node.js のバージョン番号を設定する方法だ。

他にも、上記リンク先に記述があるように、package.json に「"engines":{"node":version}」という項目を記載して設置することでもバージョン指定ができるらしい。
誰が git clone & push しても一貫したバージョンで動作させられるので、こちらの方法がよいかもしれない。




Azure Web App で使える Node.js のバージョンを知る方法

しかしところで、実行に使う Node.js のバージョンが指定できるとはいえ、いったいどんなバージョンが指定できるのか、言い換えると、Azure Web App 上にどんなバージョンの Node.js がインストール済みなのかがわからないと困るだろう。

先の「しばやん雑記」の記事に依れば、Kudu ( Azure Web App のデプロイエンジン...だけじゃない Azure Web App を陰で支えているシステム、の名称 ) の REST API に問い合わせすることで、その問い合わせ先の Azure Web App サイトにインストール済みの Node.js ランタイムのバージョンがわかる、とのこと。

ということで試しにやってみた。

Kudu の REST API に問い合わせてみるまず API のエンドポイントだが、「https://{Web App 名}.scm.azurewebsites.net/api/diagnostics/runtime」という URL になるとのこと。
この URL に GET 要求すると、その Azure Web App サイトにインストールされているランタイムの一覧が JSON 形式で手に入る。
なお、この API は Basic 認証で保護されているので、当該 Azure Web App サイトのデプロイ資格情報を添えてアクセスする必要がある。

curl を使って要求実行し、結果の JSON から Node.js ランタイムのバージョン番号を抜き出すのに jq を使ってフィルタするには下記のとおりとなる。

> curl -s -u {ユーザーID}:{パスワード} https://{Web App 名}.scm.azurewebsites.net/api/diagnostics/runtime | jq -r .nodejs[].version
すると、下図のように、インストールされている Node.js のバージョン番号一覧を表示することができた。

d0079457_23181012.png

その他の方法 - Azure ポータルサイトから調べるさて、Azure Web App サイト("on Linux" 版ではない方)は Windows OS 上に構築されている。
そして Node.js の各バージョンのランタイムが、その OS 上のどこに格納されているか調べたところ、D: ドライブの "\Program Files (x86)\nodejs" フォルダ以下に、バージョン番号ごとのサブフォルダを切って配置されていることがわかった。

ということで、Web ブラウザで Azure ポータルサイトを開き、目的の Azure Web App サイトの設定ブレードを開いて、"コンソール" を開け、そこで "D:\Program Files (x86)\nodejs"フォルダ以下にどんなサブフォルダがあるか調べてみても良い。

具体的にはコンソールにて下記コマンドを実行すればよいだろう。

ls "/d/Program Files (x86)/nodejs"
すると下図のとおり結果が表示され、インストールされている Node.js ランタイムのバージョン一式を知ることができた。

d0079457_23133729.png
おまけその1 - 希望のバージョンの Node.js がインストールされていない場合先にリンクを掲載した Microsoft 公式のドキュメントによれば、自分で準備した node.exe を Up して使うことも可能とのこと。
その2 - どうやって Azure Web App サイト上の Node.js ランタイムの格納先フォルダを知ったか?上図のコンソールにて「which node」コマンドを実行することで確認できた。
将来的には Node.js ランタイムの格納場所は変更となるかもしれないが、その際もこの方法で格納場所を知ることができると思う。
d0079457_19315574.png
その3 - 既定でどのバージョンの Node.js で実行されるのかを知るには?アプリケーション構成「WEBSITE_NODE_DEFAULT_VERSION」をみればわかるが、他にも、上図のコンソールにて「node -v」を実行してもわかると思う。

d0079457_19443505.png







.NET Core なクラスライブラリプロジェクトの xUnit.net 単体テストを AppVeyor で稼働させようとして躓いた話

$
0
0
先に結論書いておくと、

テストの実行設定は「Auto (自動)」ではなく、「Script」>「CMD」で
cd <テストプロジェクトがあるサブフォルダ>
dotnet testを指定しましょう。
d0079457_22420386.png
AppVeyor とはAppVeyor というのは、.NET 系フレンドリーなインターネット上の CI サービスである。

詳しくは下記など参照されるとよろしいかと思われる。


自分も AppVeyor の利用経験はある。
ただしそれは .NET3.x や .NET4.x といった従来からのフレームワーク用のプロジェクトのみ。
.NET Core なプロジェクトを AppVeyor に載せたことはなかった。

しかしながら今回初めて、.NET Core なクラスライブラリプロジェクト、そして単体テストのフレームワークに xUnit.net を採用したものを AppVeyor に載せる機会が生じたのでやってみた。楽勝! ...と思いきや対象となるプロジェクトはこちら。

AppVeyor の Web サイトにサインインして「NEW PROJECT」から新規プロジェクトを追加し、上記 GitHub リポジトリの URL を指定。

これで AppVeyor 上で git clone & ビルドが開始される。

...が、早速にビルドでコケる。

しかし大丈夫。
.NET Core なプロジェクトを AppVeyor でビルド成功させるには、MSBuild によるビルドが始まるより前にビルドパッケージの復元 (リストア) を明示的に行う必要があるだけだ。

下記にも事例が載っている。
...まぁ、@DarkCrash3 氏に「AppVeyor で dotnet コマンド使えるようになったらしいよ!」とお知らせしたのは自分だったりする :P

さておき、AppVeyor のプロジェクトの「SETTINGS」を開き、「Build」サブカテゴリを開いて「Before build script」の設定項目を既定の「OFF」から「CMD」に切り替え、「dotnet restore <.slnフィル名>」を設定して OK。
d0079457_22423235.png

なお、自分の場合は上記記事と異なり、「Build」サブカテゴリは既定の「MSBUILD」のままで済んだ。
代わりに「Environment」のカテゴリで「Build worker image」の選択肢を、既定の「Visual Studio 2015」から「Visual Studio 2017」に設定変更した。
このあたりは @DarkCrash3 氏の記事執筆当時と状況が変わっている点かもしれない。
d0079457_22430309.png
ビルドは通ったがテストが実行されない!さて以上で無事ビルドがとおり、いよいよ単体テストも実行され... と思いきや、テストの実行で何かコケている。

AppVeyor に記録されてるログを確認すると下記のような内容が。xUnit.net Console Runner (32-bit .NET 4.0.30319.42000)
System.InvalidOperationException: Unknown test framework: could not find xunit.dll (v1) or xunit.execution.*.dll (v2) in C:\projects\csharpprolog\CSProlog.Core.Test\bin\Debug\netcoreapp1.1「xunit.dll (v1)」または「xunit.execution.*.dll (v2)」が見つからない??

目を白黒させながら、改めて @DarkCrash3 氏の記事を見直すと、なになに、AppVeyor の「SETTINGS」、「Tests」サブカテゴリにて、テスト実行を自動検出させず、指定のスクリプト「dotenet test ~」を実行するようにせよ、とのこと。

ということで氏の記事に従い、以下のように設定して試してみた。
d0079457_22482745.png

しかしまたしてもテスト実行でコケる(ログは下記)。dotnet test CSProlog.Core.Test
Couldn't find a project to run test from. Ensure a project exists in C:\projects\csharpprolog.
Or pass the path to the projectなぜだ?
ログを見るとテスト対象のプロジェクトが見つけられないということらしい。ログではしきりに「~from .」とか「C:\projects\csharpprolog.」とか、ピリオド( . )、すなわちカレントディレクトリであろうことを示唆している。

でも、ちゃんと、単体テストプロジェクトのあるフォルダ「CSProlog.Core.Test」を指定したつもりなのだが。

そこで手当たり次第にいろいろなコマンド引数指定を試してみた。dotnet test .\CSProlog.Core.Test
dotnet test .\CSProlog.Core.Test\CSProlog.Core.Test
dotnet test CSProlog.Core.Test\CSProlog.Core.Test.csproj
dotnet test CSProlog.Core.sln
dotnet test CSProlog.Core.Test\bin\Debug\netcoreapp1.1\CSProlog.Core.Test.dllしかしどれひとつとして成功しない。

煮詰まってしまったので、氏のブログ記事ではなく、ブログ記事中で取り上げている AppVeyor プロジェクトのログを直接見に行ってみたところ...

https://ci.appveyor.com/project/darkcrash/bjd5-vc4ju
d0079457_22433192.png

え、cd コマンドと、追加引数なしの dotnet test コマンド!?

ということで自分のプロジェクトでもcd .\CSProlog.Core.Test
dotnet testをテスト実行用のスクリプトに設定したところ、あっさりテスト実行が成功した。

よかった、けど、なんか変に疲れた...。まとめ今後同じ轍を踏まぬよう、AppVeyor の構成ファイル「appvayor.yml」を以下のように作成してプロジェクトのリポジトリに添付することにした。version: 1.0.{build}
image: Visual Studio 2017
before_build:
- cmd: dotnet restore <.slnファイルへの相対パス>
test_script:
- cmd: >-
cd <単体テストのあるフォルダへの相対パス>
dotnet testあと、.NET Core な、そして xUnit.net を採用した単体テストプロジェクトを含むアプリやライブラリのプロジェクトを、AppVeyor ではなく Visual Studio Team Service で同じように CI 作ってみたらもっとすんなりできたりするのか、興味あるところである。
いずれ機会があれば試してみたい。
 
Viewing all 146 articles
Browse latest View live