Azure Web App ServiceのSlotを使った場合の接続文字列の設定方法

# はじめに
ここに記載した方法は、ASP.Net初心者が、AzureにWebサービスを実装するために、試行錯誤して調べた備忘録となります。
## 対象とする環境
- Microsoft Azure Web App Service
- Microsoft Azure SQL Server
- ASP.NET C#
- VisualStudio2017

## 改善したかったこと
Azure Web App Serviceというのは、簡単に言えば、クラウド上に用意されたIISです。
VisualStudioから発行という機能で、実行モジュールがサーバーへコピーされWebサービスが公開されます。
ローカルのIISでデバックして、完成したらサーバーへ発行するというのは、非常に分かり易いのですが、常に稼働し続けなければならないプロダクトでは、難しい面があります。
そういった場合は、一般的には、プロダクション(本番)環境とステージング(開発)環境を分けて開発を行います。
Azureには、Slotという考え方がありステージングに割り当てたSlotで開発を行い「完成！」となったところで、プロダクションSlotとスイッチひとつで、入れ替えることが出来ます。
発行機能で、のろのろと実行モジュールをアップロードするのではなくカチッと入れ替えるので、ダウンタイムは非常に短くなります。
では、データベースは、どうでしょうか？
当然ながらデータを壊して良いように、本番用のDBと開発用のDBの2つを用意します。
DBへ接続するパラメーターをひとまとめにしたものを接続文字列と呼びますが、DBごとに接続文字列は異なります。
Slotで簡単に開発環境と本番環境を入れ替えられる便利な機能があるにも関わらず、毎回接続文字列を書き換えて、発行するのは非常に面倒な作業ですし間違えた場合のダメージも大きいです。

# Web.configを使った方法
ADO.NETを使った場合、接続文字列は、web.configに記載されます。
web.configには、ビルド構成によって内容を切り替える機能が用意されておりデバックビルドの時には開発DBへ接続、リリースビルドの時には本番DBへ接続するということができます。
[Web.config の内容を発行先やビルド構成で切り替える](http://program.station.ez-net.jp/special/visual_studio/2012/web.config.switch.asp)
この機能の問題は、作業ステップが非常に多いことです。
本番へ配置する作業は、下記の3ステップを踏む必要があります。
1.本番のビルド構成でコンパイル
2.ステージングへ発行
3.スロットの交換
手作業の部分が残っているため間違って、ステージングのビルド構成でコンパイルしたものを発行してしまったら事故になってしまいます。

# Azure Potalのアプリケーション設定を使った方法
AzureのSlotには、アプリケーション設定(接続文字列)というKey/Valueの設定値が持たせられます。
Web.configの接続文字列のname属性とアプリケーション設定(接続文字列)のkey名を揃えると値が上書きされます。
[Azure Web Appsで、DBへの接続文字列をコードから分離する](http://www.atmarkit.co.jp/ait/articles/1706/05/news021.html)
つまり、動作しているSlotに合わせて自動的に接続文字列が選択されるようになります。
ミスをして、開発DBへ繋がる実行モジュールを本番環境へ発行してしまうことも原理的にありえません。
安心して作業が行なえます。

# ユニットテストにも対応する方法
もうひとつの問題は、ユニットテストで、ユニットテストDBを利用する場合です。
Azureは、DBのインスタンスで課金されるだけでなくデータセンターアウトのトラフィックにも課金されます。
最近のクラウドは安価だとはいえ、ユニットテストのために、DBをクラウドに用意するのは無駄です。
SQL Server Expressを開発PCへインストールして、ユニットテスト時には、そっちらへ接続するようにします。
まず下記のように、ユニットテストで使うSQL Server Expressの接続文字列を追加します。

```xml:web.config
  <connectionStrings>
    <add name="Entities" connectionString="接続文字列" providerName="System.Data.EntityClient" />
    <add name="UnitTest" connectionString="接続文字列" providerName="System.Data.EntityClient" />
  </connectionStrings>
```
次に、DbContext派生クラスのpartial classを作成して、name属性を指定できるようにします。
デフォルトでは、"Entities"が使用されるようになっています。
プログラムからは、EntitiesHelper経由で、Entitiesクラスを利用するようにします。

```csharp:Entities.cs
namespace EFModels
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    public partial class Entities : DbContext
    {
        public Entities(string name) : base("name=" + name)
        {
        }
    }

    public class EntitiesHelper
    {
        #region Singleton
        /// <summary>
        /// Singleton
        /// </summary>
        public static readonly EntitiesHelper Instance = new EntitiesHelper();

        static EntitiesHelper()
        {
        }
        private EntitiesHelper()
        {
        }
        #endregion

        private Entities db = null;

        public Entities GetEntities()
        {
            var value = System.Environment.GetEnvironmentVariable("ConnectionString");

            if (value != null)
            {
                this.db = new Entities(value);
            }
            else
            {
                this.db = new Entities();
            }
            return this.db;
        }
    }
}
```
最後に、開発PCに環境変数"ConnectionString"を登録します。
値は、"UnitTest"です。
Azure環境では、環境変数"ConnectionString"は設定されていませんので、デフォルトの"Entities"が利用されますが、開発PCでは、"UnitTest"が使用されるようになります。

# 心残り
ASP.Net Coreを使いたかったけど、まだ色々情報が集まらなくて断念したこと。
次の機会には、Coreを使いたい。