銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

App.config の config変換をしてくれる便利すぎるVisual Studio 拡張「Configuration Transform」

コンソールアプリやWPFアプリを作っていて、アプリのコードは同一だけど配置先に応じて設定を切り替えたい、ということはないでしょうか?Webアプリの場合、 Web.config を変換する仕組みがデフォルトで備わっていて、このブログでも触れたことがあります。

Web発行先に応じたconfig変換をコマンドラインでのビルドで適用する ~既知のバグもあるよ~ - 銀の光と碧い空

ところが、コンソールアプリなどで使われる App.config についてはこのような機能がありません。同様の仕組みを実現するために、ビルドイベントでファイルをいじったり、自前でビルドタスクを書いたり...といろいろな方法がWeb上にはあります。

が、今回紹介する「Config Transform」というVS拡張はほかのものと比べてかなり便利な機能が備わっています。

Configuration Transform extension

個人的に便利だと思っているのは以下の3点です。

  • 拡張機能を使うと、csproj を編集してビルドタスクを作成するので、この拡張機能に依存しない形で展開可能(CIサーバーなどこの拡張機能がない環境でもビルドできる)
  • ビルド構成ごとのconfig 変換ファイルを自動生成
  • config のPreviewもついている

一番大きいのが最初の点ではないでしょうか?自分だけがさわる環境ならともかく、CIサーバーなどを用意して共同開発して自動で展開するような環境だと、VS拡張がないとビルドできない、というものだとなかなか厳しいものがあります。しかし、この Config Transformは App.config の変換を有効にすると、csproj を編集してビルドタスクを新たに作成するため、以降はこのVS拡張がなくてもconfig変換機能が有効になります。

ただ、注意点としては、変更後のcsprojを見ると下記のように、Microsoft.Web.Publishing.Tasks.dll を参照しているのでこのdllが必要そうです。このdllを配置するおそらく一番簡単な方法は、Visual Studio Pro以上もしくはExpress for Web をインストールすることかと思われます。

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

使い方は上のリンクのVS拡張のページに図入りであるので、すぐわかると思います。App.config を右クリックして「App Config Transforms」を選ぶと、ビルド構成に応じたApp.XXX.config が作成されます(XXXはDebugとかReleaseとか)。したがって配置する場所ごとにconfigを差し替えたい時は、配置先に応じたビルド構成を作成する必要があります*1

たとえば、ありがちな AppSettingsを差し替えたい場合はこうなるでしょう。 元のApp.configがこうなっていまして、

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <appSettings>
    <add key="Hoge" value="Fuga"/>
  </appSettings>
</configuration>

変換用のApp.Release.configをこう定義しますと

<?xml version="1.0"?>
<!-- For more information on using app.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings xdt:Transform="Replace">
    <add key="Piyo" value="NewValue!"/>
    <add key="Poyo" value="NewValue!!"/>
  </appSettings>
</configuration>

こういうふうに変換されます。(この結果はPreview機能で元のファイルとの差分としてVS上で確認可能)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <appSettings>
    <add key="Piyo" value="NewValue!" />
    <add key="Poyo" value="NewValue!!" />
  </appSettings>
</configuration>

こんな感じでなかなか便利です。

*1:発行先ごとに設定できるWeb.configと比べてちょっと面倒くさいけど、VSの仕様なのでしょうがない

VisualStudio "14" CTP4 の .NET 関連アップデート

ここにきてアップデート関連の情報が多くなっている気がしますが、Visual Studio "14" CTP4 が出ました。リンク先から飛べるダウンロードページ、もしくは AzureのImageギャラリーから利用可能です。

Announcing October 2014 Updates to .NET Framework vNext, ASP.NET vNext and .NET Native in Visual Studio “14” CTP4

.NET 関連はこんなアップデートになっています。

  • .NET Frameworkのバージョンは 4.5.3 に。RyuJIT がリリースされ、デフォルトで有効な状態*1
  • ASP.NET vNext CTP4 リリース
    • Roslynによるコンパイルの高速化
    • Nuget が ASP.NET vNext にも対応
    • プロジェクトテンプレートで新しいソリューションレイアウトが導入
    • ASP.NET vNext のUnitTestでDebugサポート
  • .NET Native
    • WCF サポート関連のバグFix
    • 特定のXAMLルート要素なしにツールの利用が可能に
    • WinMDに対してカスタムロケーションが利用可能に
    • 出力先の絶対パスのサポート

RyuJITがデフォルト有効になり、ASP.NET vNext も徐々に全貌を見せてきました。また、先月公開されたGitHubの.NET サンプルのリポジトリにも、新しいサンプルが追加されていくと書いてあり楽しみです。

Microsoft/dotnetsamples · GitHub

*1:今まではRyuJITを別にインストールする必要があって、かつ有効にするにはレジストリ設定などが必要だった

ASP.NET MVC 5 アプリにSignalRの機能を足す方法

SignalR についてはいろいろブログや記事も出ていますが、ちょっとはまったので、MVC アプリにSignalRの機能を足して、サーバーからクライアントにメッセージを送るところまでをまとめてみました。

まずは、Nugetでライブラリを追加します。ASP.NET 用にJavaScriptなども入った全部入りのパッケージがあるので、それをインストールします。

Install-Package Microsoft.AspNet.SignalR
Update-Package

インストールすると開くREADMEにも書いていますが、Startup クラスで MapSignalR メソッドを実行します。(partial クラスなのと、ConfigureAuth メソッドを呼んでいるのは、MVCプロジェクトのテンプレートが作成しているためなので、SignalR的には不要)

using Microsoft.Owin;
using Owin;

[assembly: OwinStartupAttribute(typeof(WebApplication13.Startup))]
namespace WebApplication13
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);

            app.MapSignalR();
        }
    }
}

そして、Hubクラスを作ります。が、今回は空です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace WebApplication13.Hubs
{
    public class MyHub : Hub
    {
    }
}

で、クライアント側のページを作ります。今回は /Debug/ で開くページでSignalRを使うようにするので、 Views/Debug/Index.cshtml を作ります。

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="/Scripts/jquery-2.1.1.min.js"></script>
    <script src="/Scripts/jquery.signalR-2.1.2.js"></script>
</head>
<body>
    <script src="/signalr/hubs"></script>
    <script>
        $(function () {
            var myHub= $.connection.myHub,
                $list = $("#messagelist");
            myHub.client.update = function (model) {
                $list.prepend($('<p />').text(model.Timestamp + ' ' + model.Message));
            };

        });
        $.connection.hub.start().done(function () {
            $("#connection-satus").text("SignalR接続済み");
        });
    </script>
    <p id="connection-satus">SignalR接続済み未接続</p>
    <div id="messagelist">
    </div>
</body>
</html>

jQuery のバージョンとかは適宜変えてください*1 を参照したうえで、 connection.hub.start() でSignalRで接続します。 また、 $.connection.myHub でサーバー側で作成した MyHub クラスを参照し、myHub.client.update でクライアント側の update メソッドを定義しています。

あとは、サーバー側からクライアントで定義した update メソッドを呼んでやることにします。MyHub クラスの中からであれば簡単ですが、普通の MVC のロジックから呼びたいことがあると思います。そういうときは次のように書きます。

var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.update(new
{
    Timestamp = DateTime.Now,
    Message = "Hello"
});

また、クラス名が動的に決まるとかいう事情があってクラスを直接指定できない場合は、名前でも参照可能です*2

var hubContext = GlobalHost.ConnectionManager.GetHubContext("MyHub");
hubContext.Clients.All.update(new
{
    Timestamp = DateTime.Now,
    Message = "Hello"
});

これで /Debug を開いてサーバーからメッセージを送信するとこんな風に動いていることが確認できます。

f:id:tanaka733:20141006113031p:plain

というわけで、SignalR の機能が追加できました。なんで今回こんな紹介をしたかというと、次回の記事で SLABのEventSource を組み合わせてSignalRでサーバーのログをクライアントにそのまま送信しよう*3というものを書きたいためでした。

*1:実際はScriptBundleとか使うんですかね?

*2:いずれにせよ戻り値の型は IHubContext

*3:開発時の調査のために