Windows Azure と Visual Studio Online で継続的インテグレーション (2)

今回は、データベースと連携する Web アプリケーションを作成して、
Windows Azure と Visual Studio Online で継続的インテグレーションを構成してみたいと思います。

手順は Windows Azure と Visual Studio Online で継続的インテグレーション (1) とほぼ同様になるため、
主に相違点について記述していきます。

まず以下で示すように、
Windows Azure Web サイトを作成する際に、同時に無料の 20 MB SQL データベースを作成します。

Windows Azure の管理ポータルで、新規の Web サイトを [カスタム作成] で作成します。

image

[無料の 20 MB SQL データベースの作成] を選択します。
アプリケーション側で使用する接続文字列の名前 (Web.config で定義されているもの) を指定します。
[ソース管理から発行] をオンにします。

image

今回は、新しい SQL データベース サーバーも作成することにします。
[データベースの詳細設定を構成します] をオンにします。

image

照合順序を選択します。

image

[Visual Studio Online] を選択します。

image

Visual Studio Online の URL を入力して、[今すぐ承認] をクリックします。
Visual Studio Online 側の承認画面が表示されたら [Accept] をクリックします。

image

リポジトリ名 (チーム プロジェクト名) を選択して、右下の ✓ をクリックします。
すると、Web サイトおよび SQL データベースが作成されます。

image

 

ここから先の手順は Windows Azure と Visual Studio Online で継続的インテグレーション (1) で示した通り、

  • ビルド定義を編集する
  • Web アプリケーションをチェックインする

を実施すれば継続的インテグレーションが構成されます。
Visual Studio から Windows Azure にデプロイするで説明した通り、
接続文字列は Windows Azure Web サイトに登録されているものが使用されるため、デプロイ時に改めて設定する必要はありません。

例として、Visual Studio から Windows Azure にデプロイするで利用した Todo List の Web アプリケーションで
動作を確認できました。

image

 

バージョン情報
Visual Studio 2012

参照
Windows Azure
Visual Studio Online

Windows Azure と Visual Studio Online で継続的インテグレーション (1)
Visual Studio から Windows Azure にデプロイする

広告

Visual Studio から Windows Azure にデプロイする

データベースと連携する Web アプリケーションを、
Visual Studio の発行コマンドで Windows Azure Web サイトにデプロイするための手順を示します。

 

■ Windows Azure SQL データベースを作成する

Windows Azure の管理ポータルで、新規の SQL データベースを作成します。
[カスタム作成] を選択します。

image

照合順序およびサーバーを選択します。
SQL データベースは、「SQL データベース サーバー」の中に作成されるという構成になるため、
まだ存在していない場合は作成しなければなりません。

image

SQL データベース サーバーを新しく作成する場合のみ、ログインや地域を設定します。

image

 

作成が完了したら、ダッシュボードで [接続文字列の表示] をクリックして、ADO.NET 用の接続文字列を入手しておきます。

image

次と同等の接続文字列が得られるはずです。

Data Source=(servername).database.windows.net;Initial Catalog=(dbname);User ID=(userid);Password=(password);Connect Timeout=30;Encrypt=True

 

■ Windows Azure Web サイトを作成する

Windows Azure の管理ポータルで、新規の Web サイトを [簡易作成] で作成します。

作成が完了したら、ダッシュボードで [発行プロファイルのダウンロード] をクリックして、
発行プロファイル (.PublishSettings) を入手しておきます。

image

 

■ Web アプリケーションを発行する

Visual Studio で、Web アプリケーション プロジェクトを作成します。
ここでは例として、[ASP.NET MVC 4 Web アプリケーション] の [シングル ページ アプリケーション] を作成します。
すると、データベースを利用する Todo List という Web アプリケーションが作成されます。

プロジェクトを右クリックして [発行] をクリックします。

image

[インポート] をクリックして、入手しておいた発行プロファイルを指定します。

image

入手しておいた接続文字列を指定します。
[発行] をクリックします。

image

 

以上の手順で Windows Azure Web サイトにデプロイされ、
データベースと連携した Web アプリケーションが利用可能になります。

image

 

■ Windows Azure Web サイト側で接続文字列を指定する方法

上記の手順では発行の際に接続文字列を指定しましたが、
Windows Azure Web サイトに接続文字列を最初から設定しておくこともできます。

このように設定するにはまず、Windows Azure Web サイトを [カスタム作成] で作成し、利用する SQL データベースを選択します。
あとは、Web アプリケーションで利用する接続文字列の名前 (Web.config で定義されているもの) と、
SQL データベースのログイン情報を入力します。

image

 

すると、SQL データベースが Web サイトの [リンク済みリソース] として構成されます。

image

[構成] タブに、接続文字列が登録されます。

image

このように構成されていると、Visual Studio から発行する際に接続文字列を指定する必要がなくなります。
こちらの方法ではデータベースのログイン情報を持ち出さなくて済むため、より安全であると言えるでしょう。

Web サイトと SQL データベースのリンクは、あとからでも [リンク済みリソース] タブで追加できます。
また、SQL データベースがリンク済みリソースとして登録されていなくても、
[構成] タブの接続文字列に直接設定することもできます。
ただし、リンク済みリソースに登録しておくと、Web サイトと同時にデータベースなども監視できるようになるため便利です。

 

データベースと連携する Web アプリケーションで継続的インテグレーションを構成する方法
Windows Azure と Visual Studio Online で継続的インテグレーション (2)

バージョン情報
Visual Studio 2012

カテゴリー: クラウド, データベース. タグ: . 2 Comments »

Windows Azure と Visual Studio Online で継続的インテグレーション (1)

Visual Studio Online (旧 Team Foundation Service) に Web アプリケーションのソース コードをチェックインすると
自動的にビルドが開始し、Windows Azure Web サイトにデプロイされるような
継続的インテグレーション (Continuous Integration, CI) を構成するための手順について記述します。

 

■ ソリューションをチェックインする

Visual Studio Online でチーム プロジェクトを作成しておきます。
Visual Studio でソリューションのみを作成して、Visual Studio Online にチェックインします。

image

 

■ Windows Azure Web サイトを作成する

Windows Azure の管理ポータルで、新規の Web サイトを作成します。
[カスタム作成] を選択します。

image

 

作成する Web サイトの URL を入力し、[ソース管理から発行] をオンにします。

image

ソースコードのリポジトリの種類を指定します。
ここでは [Visual Studio Online] を選択します。

image

image

Visual Studio Online の URL を指定します。
[今すぐ承認] をクリックすると Visual Studio Online 側の承認画面が表示されるので、[Accept] をクリックします。

image

リポジトリ名 (チーム プロジェクト名) を選択して、右下の ✓ をクリックすれば Web サイトが作成されます。
このとき同時に、Visual Studio Online でビルド定義が作成されます。

image

ソース管理システムと連携されていない Web サイトにあとから設定を追加する場合でも、手順は同様です。

 

■ ビルド定義を編集する

Visual Studio で、先ほど作成されたビルド定義を編集します。
ビルド定義は「(サイト名)_CD」という名前で作成されています。
このビルド定義名を変更しても、デプロイ構成には影響ありません。

image

[全般] の [キューの処理] が [有効] に設定されていることを確認します。

image

ビルドの出力場所は既定で [ビルド出力をサーバーにコピーする] となっていますが、
保存しておきたい場合には [ビルド出力を次のソース管理フォルダーにコピーする] を選択します。

image

ビルド対象のソリューションを選択するには、次の図の [ソース設定] と、
その次の図の [プロセス] の [1. Required] – [Solution To Build] で設定します。

image

Release ビルドにするには、[2. Basic] – [Configuration To Build] で、Release 用の構成を追加します。

image

必要に応じて、[3. 基本] – [ビルド番号形式] を変更します。

image

なお、このビルド定義と Windows Azure Web サイトは、
[Deployment Settings Name] に Web サイトの名前を設定することで関連付けられています。

image

 

■ Web アプリケーションをチェックインする

まず Web アプリケーションを作成する前に、
TFS のビルド時に NuGet パッケージを復元するに記述されている手順の通り、
ソリューションを右クリックして [NuGet パッケージの復元の有効化] をクリックします。
これにより、ビルド サーバー側で NuGet パッケージをダウンロードするように構成されます。

次に、例として、[ASP.NET MVC 4 Web アプリケーション] を [空] で作成します。
HomeController を [空の MVC コントローラー] として作成し、Index メソッドに対するビューを作成しておきます。

image

 

これらのファイルを Visual Studio Online にチェックインします。
すると、ビルドが自動的に開始され、Windows Azure Web サイトに初回のビルドがデプロイされます。

Visual Studio での表示:

image

Visual Studio Online での表示:

image

Windows Azure Web サイトでの表示:

image

 

ビルドおよびデプロイに成功すると、Web サイトが表示されるようになります。

image

上述の手順で [ビルド出力を次のソース管理フォルダーにコピーする] を選択しておけば、
ビルド結果は Visual Studio Online に保存されます。

image

その後も、ソース コードをチェックインするたびに自動的にビルドおよびデプロイが実施され、
継続的インテグレーションを実現できます。

過去のバージョンを選択して再デプロイすることもできます。

image

 

なお、ビルドに失敗した場合は自動的にバグとして起票されます。

image

 

■ 番外編: Visual Studio Online "Monaco" で編集する

Windows Azure Web サイトでは、デプロイされているソースをブラウザー上で直接編集できるようになりました。

この機能を有効にするには、[構成] タブで [Visual Studio Online での編集] をオンにします。

image

編集を開始するには、ダッシュボード上の [Visual Studio Online での編集] をクリックします。

image

ブラウザー上で、デスクトップ版の Visual Studio に似た IDE を利用できます。
継続的インテグレーションとは逆に、
Monaco 上での変更結果を Visual Studio Online や Git といったソース管理システムに保存することもできるようです。

image

 

次回は、データベースと連携する Web アプリケーションについて取り扱います。
Windows Azure と Visual Studio Online で継続的インテグレーション (2)

バージョン情報
Visual Studio 2012

参照
Windows Azure
Visual Studio Online
継続的な配置
TFS のビルド時に NuGet パッケージを復元する

センサーのデータを SignalR でホストする (5)

前回のセンサーのデータを SignalR でホストする (4) までで、

  • ASP.NET SignalR サービス (コンソール アプリケーション)
  • ASP.NET SignalR クライアント (HTML)
  • ASP.NET SignalR クライアント (コンソール アプリケーション)

を作成しました。

今回は、コンソール アプリケーションとして作成した ASP.NET SignalR サービスを
タスクバーの通知領域に常駐させるように変更したいと思います。

 

(5) サービスをタスクバーの通知領域に常駐させる

Windows のタスクバーの通知領域を利用するには、アイコン ファイル (.ico) が必須です。
まず、以下の手順でアイコンをリソースに登録します。

(1), (2) で作成したプロジェクトのプロパティを開き、[リソース] タブを選択します。
中央のリンクをクリックして、リソース ファイルを作成します。

リソース 1

通知領域に表示させるアイコン ファイル (.ico) を用意し、
エクスプローラーから下図の中央部分にドラッグ アンド ドロップします。

リソース 2

名前を ServiceIcon に変更しておきます。

リソース 3

 

次に、プロジェクトのプロパティの [アプリケーション] タブを選択し、
[出力の種類] を [Windows アプリケーション] に変更します。
これにより、コンソール画面を出現させずにアプリケーションを実行できます。

出力の種類

 

プロジェクトに System.Windows.Forms.dll への参照を追加して、
次のように Program クラスを変更していきます。


class Program
{
    const string SelfHostUrl = "http://localhost:8080";
    const string AppName = "Sensors Host";
    static IDisposable webApp;
    static NotifyIcon notifyIcon;

    static void Main(string[] args)
    {
        webApp = WebApp.Start(SelfHostUrl);
        ShowNotifyIcon();
        Application.Run();
    }

    static void ShowNotifyIcon()
    {
        var exitMenu = new ToolStripMenuItem("終了(&X)");
        exitMenu.Click += (o, e) => ExitApp();
        var contextMenuStrip = new ContextMenuStrip();
        contextMenuStrip.Items.Add(exitMenu);

        notifyIcon = new NotifyIcon
        {
            ContextMenuStrip = contextMenuStrip,
            Icon = Properties.Resources.ServiceIcon,
            Text = AppName,
            Visible = true,
        };

        notifyIcon.ShowBalloonTip(3000, AppName, "サービスを開始しました。", ToolTipIcon.Info);
    }

    static void ExitApp()
    {
        webApp.Dispose();
        notifyIcon.Dispose();
        Application.Exit();
    }
}


サービスのセルフホストを開始したら、Application.Run メソッドを呼び出します。
これによりブロックが起き、アプリケーションの実行状態が保持されます。

NotifyIcon オブジェクトを生成して Visible プロパティを true に設定することにより、
タスクバーの通知領域にアイコンが表示されるようになります。
また、[終了] メニューを ContextMenuStrip オブジェクトに追加しておきます。

 

これを実行します。
タスクバーの通知領域にアイコンとメッセージが表示されます。

Sensors Host (開始)

アイコンを右クリックすると、[終了] メニューが表示されます。

Sensors Host (終了)

 

前: センサーのデータを SignalR でホストする (4)

作成したサンプル
WinRT-SignalR-Sample (GitHub)

バージョン情報
.NET Framework 4.5

参照
NotifyIcon クラス

センサーのデータを SignalR でホストする (4)

前回のセンサーのデータを SignalR でホストする (3) では、
ASP.NET SignalR サービスに対するクライアントを HTML で実装しました。
今回は同様のクライアントをコンソール アプリケーションで実装します。

 

(4) コンソール アプリケーションからサービスにアクセスする

まず、[新しいプロジェクトの追加] でコンソール アプリケーション プロジェクトを作成します。

準備として、次のものを参照に追加する必要があります。

  • ASP.NET SignalR .NET Client (Microsoft.AspNet.SignalR.Client)

作成したプロジェクトを右クリックして [Nuget パッケージの管理] を選択します。
上記のパッケージを検索してインストールします。

ASP.NET SignalR .NET Client

 

Program.cs を、次のように実装します。


class Program
{
    static void Main(string[] args)
    {
        Task.Run(() => StartConnection());

        Console.WriteLine("Press [Enter] to exit.");
        Console.ReadLine();
    }

    async static void StartConnection()
    {
        var connection = new HubConnection("http://localhost:8080/");
        connection.ConnectionSlow += () => Console.WriteLine("Connection is slow.");
        connection.Error += ex => Console.WriteLine(ex);

        var lightSensor = connection.CreateHubProxy("LightSensorHub");
        lightSensor.On<float>("NotifyIlluminanceInLux", NotifyIlluminanceInLux);
        var compass = connection.CreateHubProxy("CompassHub");
        compass.On<double>("NotifyHeadingMagneticNorth", NotifyHeadingMagneticNorth);

        await connection.Start();

        var illuminanceInLux = await lightSensor.Invoke<float>("GetIlluminanceInLux");
        NotifyIlluminanceInLux(illuminanceInLux);
        var headingMagneticNorth = await compass.Invoke<double>("GetHeadingMagneticNorth");
        NotifyHeadingMagneticNorth(headingMagneticNorth);
    }

    static void NotifyIlluminanceInLux(float illuminanceInLux)
    {
        Console.WriteLine("Light: {0} lx", illuminanceInLux);
    }

    static void NotifyHeadingMagneticNorth(double headingMagneticNorth)
    {
        Console.WriteLine("Compass: {0} °", headingMagneticNorth);
    }
}


HubConnection.CreateHubProxy メソッドで、Hub のプロキシを作成します。

サービス側から直接呼び出せるメソッドを定義するには、On メソッドを使います。
逆に、サービス側で定義されたメソッドを呼び出すには、Invoke メソッドを使います。

なお、サービスのパスを明示的に指定するには、

var connection = new HubConnection("http://localhost:8080/signalr", false);

のようにします。

 

では、これを実行します。
端末に手をかざしたり端末を回転したりすれば、サービス側からそれぞれのデータがプッシュ送信されます。

SensorsConsole

 

次回は、サービス側のアプリケーションを通知領域に常駐させるように変更します。

つづく

前: センサーのデータを SignalR でホストする (3)
次: センサーのデータを SignalR でホストする (5)

作成したサンプル
WinRT-SignalR-Sample (GitHub) (今回までの分)
WinRT-SignalR-Sample (GitHub)

バージョン情報
.NET Framework 4.5
ASP.NET SignalR .NET Client 2.0

参照
Hubs API Guide – .NET Client (C#)

センサーのデータを SignalR でホストする (3)

前回のセンサーのデータを SignalR でホストする (2) では、サービス側を実装しました。
今回はクライアント側となる Web アプリケーションを実装します。

 

(3) ブラウザーからサービスにアクセスする

まず、[新しいプロジェクトの追加] で [ASP.NET 空の Web アプリケーション] を選択し、
Web アプリケーション プロジェクトを作成します。

準備として、次のものを参照に追加する必要があります。

  • jQuery
  • ASP.NET SignalR JavaScript Client (Microsoft.AspNet.SignalR.JS)

通常、Web アプリケーション プロジェクトで ASP.NET SignalR のクライアント側もサービス側も実装する場合は
ASP.NET SignalR (Microsoft.AspNet.SignalR) をインストールしますが、
今回はクライアント側のみを利用するため、ASP.NET SignalR JavaScript Client をインストールします。

また、現在のところ、ASP.NET SignalR JavaScript Client をインストールすると、
古いバージョンである jQuery 1.6.4 が同時に入ってしまうため、先に jQuery を個別にインストールしておきます。

作成したプロジェクトを右クリックして [Nuget パッケージの管理] を選択します。
上記のパッケージを検索してインストールします。

jQuery

ASP.NET SignalR JavaScript Client

 

プロジェクトに [HTML ページ] を追加し、名前を index.html とします。
index.html を次のように実装して、サービスからセンサーのデータを取得します。


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Sensors Client</title>
    <script src="Scripts/jquery-2.0.3.min.js"></script>
    <script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
    <script src="http://localhost:8080/signalr/hubs"></script>
</head>
<body>
    <script type="text/javascript">
        $(function () {
            $.connection.hub.url = "http://localhost:8080/signalr";
            var appModel = { light: 0, compass: 0 };

            var lightSensor = $.connection.lightSensorHub;
            lightSensor.client.notifyIlluminanceInLux = function (illuminanceInLux) {
                appModel.light = illuminanceInLux;
                console.log("Light: %f lx", appModel.light);
            };
            var compass = $.connection.compassHub;
            compass.client.notifyHeadingMagneticNorth = function (headingMagneticNorth) {
                appModel.compass = headingMagneticNorth;
                console.log("Compass: %f °", appModel.compass);
            };

            $.connection.hub.start()
                .done(function () {
                    lightSensor.server.getIlluminanceInLux()
                        .done(lightSensor.client.notifyIlluminanceInLux);
                    compass.server.getHeadingMagneticNorth()
                        .done(compass.client.notifyHeadingMagneticNorth);
                });
        });
    </script>
</body>
</html>


サービス側から直接呼び出せるメソッドを定義するには、lightSensor.client.notifyIlluminanceInLux のように、
hub の client プロパティに関数を設定します。

逆に、サービス側で定義されたメソッドを呼び出すには、lightSensor.server.getIlluminanceInLux のように、
hub の server プロパティを経由して呼び出します。

これを実行します。
端末に手をかざしたり端末を回転したりすれば、サービス側からそれぞれのデータがプッシュ送信され、
ブラウザーのコンソールに値がレポートされます。

Sensors Client (コンソール)

 

あとは、GUI を追加すれば変化が視覚的にわかりやすくなります。
この先のコードは index.html (GitHub) をご覧ください。

Sensors Client 1

Sensors Client 2

 

次回は、コンソール アプリケーションでクライアントを作成します。

つづく

前: センサーのデータを SignalR でホストする (2)
次: センサーのデータを SignalR でホストする (4)

作成したサンプル
WinRT-SignalR-Sample (GitHub) (今回までの分)
WinRT-SignalR-Sample (GitHub)

バージョン情報
.NET Framework 4.5
jQuery 2.0
ASP.NET SignalR JavaScript Client 2.0

参照
Tutorial: SignalR Self-Host

センサーのデータを SignalR でホストする (2)

前回のセンサーのデータを SignalR でホストする (1) では、
センサーのデータをトラッキングするコンソール アプリケーションを作成しました。

今回は、そのデータをリアルタイムで配信するサービスを ASP.NET SignalR で作成してみたいと思います。
ただし IIS 上の Web サービスとしてホストするわけではなく、コンソール アプリケーションでホストします。
そのためには、ASP.NET SignalR Self Host を利用します。

 

(2) ASP.NET SignalR でサービスをセルフホストする

準備として、次のものを参照に追加する必要があります。

  • ASP.NET SignalR Self Host (Microsoft.AspNet.SignalR.SelfHost)
  • Microsoft.Owin.Cors

Visual Studio で、前回作成したプロジェクトを右クリックして [Nuget パッケージの管理] を選択します。
上記のパッケージを検索してインストールします。

ASP.NET SignalR Self Host

Microsoft.Owin.Cors

 

では、実装です。
IIS 上の ASP.NET SignalR と同様に、サービスを表すクラスを Hub クラスの具象クラスとして実装します。

まず、照度センサーに対する LightSensorHub クラスを実装します。


public class LightSensorHub : Hub
{
    static readonly IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<LightSensorHub>();
    static readonly LightSensor lightSensor = LightSensor.GetDefault();

    public static void StartBroadcast()
    {
        lightSensor.ReportInterval = 500;
        lightSensor.ReadingChanged += (o, e) =>
        {
            Console.WriteLine("Light: {0} lx", e.Reading.IlluminanceInLux);
            hubContext.Clients.All.NotifyIlluminanceInLux(e.Reading.IlluminanceInLux);
        };
    }

    public float GetIlluminanceInLux()
    {
        return lightSensor.GetCurrentReading().IlluminanceInLux;
    }
}


クライアント側から直接呼び出すためのメソッドとして、GetIlluminanceInLux メソッドを定義しています。
逆に、クライアント側にプッシュ送信するためには、LightSensorHub クラスに対する IHubContext オブジェクトを取得します。

クライアント側から呼び出されたときであれば Hub.Clients プロパティからプッシュ送信できるのですが、
任意のタイミングでクライアント側にデータをプッシュするにはこのようにします。

電子コンパスに対しても同様に、CompassHub クラスを実装します。


public class CompassHub : Hub
{
    static readonly IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<CompassHub>();
    static readonly Compass compass = Compass.GetDefault();

    public static void StartBroadcast()
    {
        compass.ReadingChanged += (o, e) =>
        {
            var rounded = Math.Round(e.Reading.HeadingMagneticNorth, 3);
            Console.WriteLine("Compass: {0} °", rounded);
            hubContext.Clients.All.NotifyHeadingMagneticNorth(rounded);
        };
    }

    public double GetHeadingMagneticNorth()
    {
        return Math.Round(compass.GetCurrentReading().HeadingMagneticNorth, 3);
    }
}


 

Web サーバーを起動するためのクラスとして、Startup クラスを実装します。
Configuration メソッドに、起動時の構成を記述します。


class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);
        app.MapSignalR();

        LightSensorHub.StartBroadcast();
        CompassHub.StartBroadcast();
    }
}


IAppBuilder.UseCors メソッドにより、CORS (Cross-Origin Resource Sharing) の設定をします。
要するにクロスドメインのアクセス許可ですが、AllowAll を指定することですべてのドメインからのアクセスを許可します。

ホストする URL のパスは既定で /signalr ですが、これを明示的に指定するには、

app.MapSignalR("/signalr", new HubConfiguration());

のようにします。

 

最後に、Main メソッドを書き換えます。
ポートを指定して、Web サーバーを起動します。


class Program
{
    const string SelfHostUrl = "http://localhost:8080";

    static void Main(string[] args)
    {
        using (WebApp.Start(SelfHostUrl))
        {
            Console.WriteLine("Press [Enter] to exit.");
            Console.ReadLine();
        }
    }
}


 

このようにして、リアルタイム双方向サービスをセルフホストできるようになります。
次回はクライアントとなる HTML アプリを作成します。

つづく

前: センサーのデータを SignalR でホストする (1)
次: センサーのデータを SignalR でホストする (3)

作成したサンプル
WinRT-SignalR-Sample (GitHub) (今回までの分)
WinRT-SignalR-Sample (GitHub)

バージョン情報
.NET Framework 4.5
ASP.NET SignalR Self Host 2.0
Microsoft.Owin.Cors 2.0

参照
Tutorial: SignalR Self-Host