だるろぐ

明日できることは、今日しない。

WebMatrix:キーワード 'file access retry timeout' はサポートされていません。

f:id:daruyanagi:20140717044635p:plain

WebMatrix.Data.Database.Open("MYDB");

ローカルと Azure ではイケるのに、ExpressWeb では WebMatrix.Data.DatabaseOpen() が失敗する件について。

解決策その一

New connection string keyword: “File Access Retry Timeout” – will reattempt connection open from 0 – 30 seconds, with a default value of 0.

Everything SQL Server Compact: SQL Server Compact 4.0 SP1 CTP1 available

SQL Server Compact 4.0 SP1 CTP1 で追加された要素らしいので、それ以前のバージョンを利用する。

解決策その二

WebMatrix.Data.Database.OpenConnectionString(
    "Data Source=|DataDirectory|\\MYDB.sdf",
    "System.Data.SqlServerCe.4.0"
);

.NET Framework データ プロバイダーを指定して、接続文字列で開く。

WebMatrix:Web.config の appSettings を使いこなす

WebMatrix:ローカルとリモートで異なる設定を利用する方法を考えてみた - だるろぐ でちょっと興味がわいたので、Web.config について少しいろいろ試してみた。

基本

f:id:daruyanagi:20140717005913p:plain

Default.cshtml

<!DOCTYPE html>

@using System.Configuration

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>マイ サイトのタイトル</title>
    </head>
    <body>
        @ObjectInfo.Print(ConfigurationManager.AppSettings)
    </body>
</html>

Web.config の appSettings セクションに記述したアプリケーション設定を読み込む。

@ObjectInfo.Print は WebMatrix でちょっとしたデバッグをするときに便利なのでぜひ覚えておこう。

Web.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>

    <appSettings file="Web2.config">
        <add key="A" value="a"/>
        <add key="B" value="b"/>
        <add key="C" value="c"/>
        <add key="D" value="d"/>
        <add key="E" value="e"/>
        <add key="F" value="f"/>
        <add key="G" value="g"/>
    </appSettings>
</configuration>

appSettings セクションにアプリケーション設定を記述。file="Web2.config" は後述。

結果

f:id:daruyanagi:20140717011748p:plain

たとえば、

var a = System.Configuration.ConfigurationManager.AppSettings["A"];

などとすることで、Web.config の appSettings セクションに記述したアプリケーション設定が得られる。

ちょっとめんどくさいけれど、API キーなどの静的値はなるべくハードコードせず、appSettings に書いておくべき。

外部 .config ファイルを利用したアプリケーション設定のオーバーライド

f:id:daruyanagi:20140717011257p:plain

以下のような Web2.config ファイルを追加。

Web2.config

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <add key="E" value="1"/>
    <add key="F" value="2"/>
    <add key="G" value="3"/>
</appSettings>

結果

f:id:daruyanagi:20140717010123p:plain

ConfigurationManager.AppSettings["E"] 以降の値が Web2.config の内容によって書き換えられる。

Web.config の file 属性で指定した外部 .config ファイルが存在しない場合は、読み込み処理がスキップされる。

外部 .config ファイルを利用したアプリケーション設定の強制削除

ちょっと Web2.config をイジってみる。

Web2.config

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <clear /> // <- 追加
    <add key="E" value="1"/>
    <add key="F" value="2"/>
    <add key="G" value="3"/>
</appSettings>

結果

f:id:daruyanagi:20140717010556p:plain

Web.config で記述したアプリケーション設定がクリアされる。

要するに、ASP.NET は .config を読んで、appSettings セクションに書かれた add や clear といったコマンドを AppSettings(Dictionary 型)に対して行っているだけ。

なので、

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <remove key="A" />
</appSettings>

とすれば、特定のキーだけを消すことも可能。これを使えば、Web.config で行ったアプリケーション設定を Web2.config から自由にイジれる。使い方間違うとハマるかもだけど。

外部 .config ファイルを隠しファイルにする

結論を先に言うと、Web2.config を隠しファイルにしても、appSettings はちゃんと読み込まれる。

なので WebMatrix:特定のファイルを発行対象に含まない - だるろぐ と組み合わせることで、

  • Web.config
  • Web2.config(隠しファイル、リモートに発行されない)

という構成にすれば、

  • ローカル:Web.config + Web2.config のアプリケーション設定で動作
  • リモート:Web.config のアプリケーション設定で動作

という風に運用できる(WebMatrix:ローカルとリモートで異なる設定を利用する方法を考えてみた - だるろぐ は発想を逆にすればよかった!)。

WebMatrix:特定のファイルを発行対象に含まない

f:id:daruyanagi:20140717003700p:plain

これな。

リモートへ発行したくないローカルファイルがある場合の対処方法。

隠しファイルを発行しない

f:id:daruyanagi:20140717003817p:plain

[設定]-[発行オプション]で“ソース コントロール ファイルなどの非表示のファイルおよびフォルダーを発行しない”を有効化。

たぶん初期状態で有効になっているはず。

発行したくないフォルダーやファイルを隠しファイルに

f:id:daruyanagi:20140717004004p:plain

フォルダーを隠すときは、子ファイルまで隠しフォルダにしなくていい。

――これで設定は完了。もう一度[発行]すると……

f:id:daruyanagi:20140717004047p:plain

デキました。

WebMatrix:ローカルとリモートで異なる設定を利用する方法を考えてみた

たとえば Twitter BOT を作っている場合に、

  • ローカルテスト中は、テスト用に作成したアカウントでポスト
  • リモート展開中は、本番用のアカウントでポスト

したい。Visual Studio だったらデプロイ時に XDT で Web.config を書き換えられるので簡単だけど、わしはそれを WebMatrix でやりたいのさ。

で、AppSettings についていろいろ調べてたのだけど……

file 属性

カスタム アプリケーションの構成設定を含む外部ファイルへの相対パスを指定します。指定されたファイルには、、および の各要素で指定されたものと同じ種類の設定が含まれ、これらの要素と同じキーと値のペア形式を使用します。

指定するパスはメイン構成ファイルへの相対パスです。Windows フォーム アプリケーションの場合は、アプリケーションの構成ファイルの場所ではなく、バイナリ フォルダ (/bin/debug など) になります。Web フォーム アプリケーションの場合は、web.config ファイルが置かれているアプリケーション ルートへの相対パスになります。
指定されたファイルが見つからない場合、ランタイムはこの属性を無視します。

<appSettings> 要素

これは使えそう。方針はこんな感じ。

  • ローカルテスト時:Web.config(テスト用の Twitter アカウントの設定を記述)
  • リモートテスト時:Web.config + RemoteOnly.config(本番用の Twitter アカウントの設定で上書き)

発行のときにリモートの RemoteOnly.config を消さないように気を付けなきゃいけない運用上の注意はあるけれど、これは割とイケるのではないか。

Web.config

appSettings にテスト用アカウントの設定を記述。

<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
        <customErrors mode="Off" />
    </system.web>

    <appSettings file="RemoteOnly.config"> // <-- file 属性を記述
        <add key="twitterAccessToken" value=""/>
        <add key="twitterAccessTokenSecret" value=""/>
        <add key="twitterConsumerKey" value=""/>
        <add key="twitterConsumerSecret" value=""/>
    </appSettings>
</configuration>

RemoteOnly.config がない場合は、ConfigurationManager.AppSettings["***"] こちらの設定が取得される。

RemoteOnly.config

本番用アカウントの設定を記述。

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <add key="twitterAccessToken" value=""/>
    <add key="twitterAccessTokenSecret" value=""/>
    <add key="twitterConsumerKey" value=""/>
    <add key="twitterConsumerSecret" value=""/>
</appSettings>

上書きする appSettings をルートに書けばいいんだね。

WebMatrix:ルビを振るためのヘルパーを作ってみる

f:id:daruyanagi:20140703034425p:plain

とりあえずオーソドックスに App_Code フォルダ以下に Ruby.cshtml を作ってこんな感じに記述。

# ~/Ruby.cshtml

@helper GetHtml(string text, string ruby){
    <ruby><rb>@text</rb><rp>(</rp><rt>@ruby</rt><rp>)</rp></ruby>
}

使い方はこんな感じ .cshtml の名前がそのまま静的クラスの名前になっていて、定義したヘルパー関数が呼べる。

# ~/Test.cshtml

@{
    
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        @Ruby.GetHtml("柳 英俊", "やなぎ ひでとし")
    </body>
</html>

f:id:daruyanagi:20140703034801p:plain

(※見やすいように拡大してある)

自分はルビのタグなんか覚えるの面倒だし、ましてやルビタグが解釈できない Firefox のことまで考えてコーディングするのは面倒なので、こういうヘルパーを作るのが好み。

ちょっとハッテン

# ~/App_Code/HtmlHelperExtensions.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages.Html;

public static class HtmlHelperExtenstion
{
   public static IHtmlString Ruby(this HtmlHelper helper, string text, string ruby)
   {
       return new HtmlString(string.Format(
          "<ruby><rb>{0}</rb><rp>(</rp><rt>{1}</rt><rp>)</rp></ruby>",
          text, ruby
       ));
   }
}

こうやって HtmlHelper の拡張メソッドとして定義しておけば

@Html.Ruby("柳 英俊", "やなぎ ひでとし")

で同様のことができるようになる。

Google の「Web Starter Kit」を WebMatrix で

f:id:daruyanagi:20140624213306p:plain

Twitter で小耳にはさんだ「Web Starter Kit」を少し試してみました(別に WebMatrix で、じゃなくていいんだが)。いわゆるボイラープレート(テンプレみたいなもん)ですね。

  • Mobile-optimized HTML boilerplate
  • Responsive multi-device layout
  • Visual component style guide
  • gulp.js build tooling (optional)
  • LiveReload
  • Cross-device synchronization of clicks, scrolls, navigation, and form-filling
  • Image optimization
  • JavaScript minification and optimization
  • CSS optimization
  • HTML minification
  • PageSpeed performance reporting
  • CSS autoprefixing

というのが特徴らしい。ブラウザーサポートはこんな感じ。

  • IE10, IE11, IE Mobile 10
  • FF 30, 31
  • Chrome 34, 35
  • Safari 7, 8
  • Opera 23, 24
  • iOS Safari 7, 8
  • Opera Coast
  • Android / Chrome 4.4, 4.4.3
  • Blackberry 1.0

IE11 で少しずれてるところがあった気もするけどキニシナイ。

f:id:daruyanagi:20140624213625p:plain

さっそくダウンロードし、アーカイブを展開しました。よくわからんのがぐちゃぐちゃ入ってる(gulp.js - the streaming build system のためのファイルなんだろうか?)けど、メインは app フォルダの中身みたい。このフォルダをさっそく[Microsoft WebMatrix で Web サイトとして開く]。

f:id:daruyanagi:20140624214238p:plain

スクリプトはメニュー関連のが最小限度。スタイルシートは SCSS で書かれていて、CSS にコンパイルされています。ほかに Web フォントやらアイコンやらが少し。

basic.html は HTML5 Boilerplate CSS を読み込んだだけのシンプルなスタイル。

<!-- build:css styles/components/main.min.css -->
<link rel="stylesheet" href="styles/h5bp.css">
<!-- endbuild -->

inde.html はいろいろ読み込んで Google デザインっぽくしたやつ。

<!-- build:css styles/components/main.min.css -->
<link rel="stylesheet" href="styles/h5bp.css">
<link rel="stylesheet" href="styles/components/components.css">
<link rel="stylesheet" href="styles/main.css">
<!-- endbuild -->

とりあえず実行。

f:id:daruyanagi:20140624214811p:plain

Google っぽい。日本語フォントで使うなら、少しいろいろいじった方がバランスがいいかもしれないかなー。そういうセンスないからよくわかんないけど。

スタイルガイドものぞいてみました。

f:id:daruyanagi:20140624214745p:plain

ボタン。とっても……Google っぽいです……。

f:id:daruyanagi:20140624214917p:plain

グリッド。ここらへんは Bootstrap とかでもお馴染み。ざっとしか見てないけど、2段~4段組みまで割りと柔軟にレイアウトできるみたい。

f:id:daruyanagi:20140624215143p:plain

カラー。色のセンスないので、こういうところだけでも真似してみたい。前景と背景だけ色決めて、ほかはアクセントカラーとその薄いバージョンをうまく使う感じで……。

f:id:daruyanagi:20140624215243p:plainf:id:daruyanagi:20140624215247p:plainf:id:daruyanagi:20140624215251p:plain

あと、こういう機能紹介っぽい感じのページもいいなぁと思った。まるまる使うと Google 臭がスゴいのであんまり使わない気がするけれど、エッセンスだけでも取り込みたい。

Dropbox + Microsoft Azure でサイトを管理する

f:id:daruyanagi:20140503080329p:plain

Windows Microsoft Azure Web サイト(MAWS)では、Dropbox のフォルダーとコードを同期して、サイトをデプロイすることができます。

  • 複数の環境でソースコードを同期しておきたい
  • Git とかよくわかんねーけど、Dropbox なら毎日使ってるぜ

といったユーザーにお勧めかも。

サイトの作成

f:id:daruyanagi:20140503080713p:plain

まず MAWS で新規サイトを作成してみました。[Web サイト]-[簡易作成]を選択し、

f:id:daruyanagi:20140503080722p:plain

URL(サブドメイン)を決めるだけ(ほかはよくわからんのでそのままにした)。

f:id:daruyanagi:20140503080729p:plain

できあがり。簡単すぎて鼻血が出そうでした。

Dropbox との連携

f:id:daruyanagi:20140503080329p:plain

さっきの画面の下の方にある[ソース管理の統合]-[ソース管理からのデプロイの設定]を選択すると、どこに保存されているコードをサイトへデプロイするかが選べます。もちろん、今回は Dropbox を選択。

f:id:daruyanagi:20140503081210p:plain

指示されるがままに Dropbox との連携処理を行うと……

f:id:daruyanagi:20140503081309p:plain

なんかフォルダが作成されます。

f:id:daruyanagi:20140503081429p:plain

確認してみると DROPBPBOX_ROOT\アプリ\Azure にサイトのフォルダがありますね(カタカナ!?)。これを WebMatrix でサイトとして開きます。で、なんでもいいので適当にファイルを置いてみてください。今回は“テストだよ!”と表示するだけの Default.cshtml をルートにおいています。

f:id:daruyanagi:20140503081735p:plain

んで、サイトの管理画面(ブラウザー)に戻り、下の方にある[同期]ボタンを押してデプローイ!

f:id:daruyanagi:20140503081919p:plain

無事サイトも動き出しました。

たぶん、デプロイってコマンドでもできるよね。静的ページ(動的なのでもいいけど)を Dropbox で管理して、コマンドでデプロイするようにすればいい感じかもしれない。

WebMatrix と Microsoft Azure 日本リージョン

日本マイクロソフト株式会社(本社:東京都港区、代表執行役 社長:樋口 泰行)は、パブリッククラウドサービス「Microsoft Windows Azure」をユーザーに提供する、新たな主要リージョンを日本に展開することを発表します。この「日本リージョン」には、日本国内の2か所(首都圏と関西圏)のサブリージョンが含まれ、Windows Azure のサービスを国内のデータセンターから提供することを可能にします。

「Microsoft Windows Azure」の国内ビジネス強化に向けて 日本リージョンの開設計画を公開

遅ればせながら、Windows Microsoft Azure の日本リージョン開設おめでとうございます。これで Azure の普及が日本でも進めばいいですね。

f:id:daruyanagi:20140401033649p:plain

この日本リージョンですが、WebMatrix からでも利用できるみたいです。Windows Microsoft Azure にサイトを作成する際に現れるドロップダウンリストで“空白”を選びましょう。

f:id:daruyanagi:20140401034101p:plain

この“空白”、二つありますが、手元で試してみたところ上が“日本 (西)”下が“日本 (東)”みたいですね? 違ったらごめんなさい。

WebMatrix: Gmail 経由でアラートメールを送る(2)

見事 WebMatrix で Gmail 経由のアラートメールが送れたわけだが(WebMatrix: Gmail 経由でアラートメールを送る - だるろぐ)、このやり方には一つ欠点(? というか、仕様だな)があった。

たとえば、以下のようなコード。タイマーでとあるジョブ(必ず失敗する)を処理し、エラーが発生したらエラーメールを送る。

@{
    WebMail.SmtpServer="smtp.gmail.com";
    WebMail.SmtpPort = 587;
    WebMail.EnableSsl=true;  
    WebMail.UserName="***";
    WebMail.From="***@gmail.com";  
    WebMail.Password="***";

    var timer = new System.Timers.Timer(1000 * 60);

    timer.Elapsed += (sender, args) =>
    {
        try
        {
            throw new Exception();
        }
        catch (Exception e)
        {
            WebMail.Send("***@***.com", e.Message, e.StackTrace);
        }
    };

    timer.Start();
}

これは失敗する。しかも、WebMatrix だと例外が捕捉できないので気づきにくい。

f:id:daruyanagi:20140314181207p:plain

よくわからんけれど、WebMail は System.Web.Helpers Namespace () | Microsoft Docs に属するみたいで、Page のスレッドじゃないと動作しないのかもしれない。そういえば昔、こういうシチュエーションで Server.MapPath() が使えなかった覚えがある。

解決策

System.Net.Mail.MailMessage と System.Net.Mail.SmtpClient を使ってみた。

@{
    var timer = new System.Timers.Timer(1000 * 60);

    timer.Elapsed += (sender, args) =>
    {
        try
        {
            throw new Exception();
        }
        catch (Exception e)
        {
            SendMail(e.Message, e.StackTrace);
        }
    };

    timer.Start();
}

@functions
{
    public void SendMail(string title, string body)
    {
        using (var mail = new System.Net.Mail.MailMessage())
        using (var smtp = new System.Net.Mail.SmtpClient())
        {
            try
            { 
                mail.From = new System.Net.Mail.MailAddress("***@***.com");
                mail.To.Add(new System.Net.Mail.MailAddress("***@***.com"));
                mail.Subject = title;
                mail.Body = body;

                smtp.Host = "smtp.gmail.com";
                smtp.Port = 587;
                smtp.EnableSsl = true;
                smtp.UseDefaultCredentials = false;
                smtp.Credentials = new System.Net.NetworkCredential("***", "***");
                smtp.Send(mail);
                
                System.Diagnostics.Debug.WriteLine("The mail has been sent successfully.");
            }
            catch(Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.Message);
            }
        }
    }
}

これは成功。

f:id:daruyanagi:20140314181811p:plain

成功したのはいいけどアプリを止め忘れて、気が付いたら 100 通以上メールがきてた /(^o^)\

WebMatrix: Gmail 経由でアラートメールを送る

Hidetoshi Yanagi(無職) (YanagiHidetoshi) on Twitter に仕掛けておいた Twitter Bot が少しおかしいことに気が付いた。このブログのフィードだけ配信されていない。アプリが吐いたログを見ると、どうやらツイートが長すぎたようだ。修正、修正っと。

で、この問題は解決したのだけど、こういうことはメールでお知らせしてほしいなと思う。WebMatrix/ASP.NET Web Pages では簡単にメールが遅れるので、使わないなんて損だ。

以下は Gmail の SMTP サーバーを利用する場合。最初に WebMail クラスの設定をしておく。

// _AppStart.cshtml

@{
    WebMail.SmtpServer="smtp.gmail.com";
    WebMail.SmtpPort = 587;
    WebMail.EnableSsl=true;  
    WebMail.UserName="***";
    WebMail.From="***@gmail.com";  
    WebMail.Password="***";
}

使い方はこんな感じ。

@{
    try
    {
        (なんかエラーの起こりそうな処理)
    }
    catch (Exception e)
    {
         WebMail.Send(
             to: "***@***.**",
             subject: "Error が起こったで!",
             body: e.Message
         );
    }
}

f:id:daruyanagi:20140308220635p:plain

さっそく ALTER TABLE が失敗したメールが来た /(^o^)\