【ASP.NET MVC】 Azureで公開してるサイトのレスポンスがだいぶ良くなったのでやったことのメモ

# ASP.NET MVCをAzure環境で使ってます
[こんなサイト](https://shiropro-logger.azurewebsites.net/)を作成しています。
しかし機能拡張をしていく中でどんどんサイトが重くなってしまいました。
このままでは遅いからだれにも必要とされないサイトになってしまいそうだったので低レイテンシをめざして改修しました。
目標はgoogleの推奨する200ms以下のディレイ。
(2018/05/02追記)PageSpeed Insightsで~~99/97達成~~したので追記します。~~adsense~~/analytics付きなのでここらが限界かなと思います。
(2018/05/18)[Azure http2対応](https://blogs.msdn.microsoft.com/appserviceteam/2018/04/13/announcing-http2-support-in-azure-app-service/)したのでサイトに反映。
[TestMySite](https://testmysite.withgoogle.com/intl/ja-jp)で2秒がでるようになりました。
しかしPageSpeed Insightsで99/97達成出来なくなりましたorz

[参考サイト](https://www.danylkoweb.com/Blog/10-ways-to-speed-up-your-aspnet-mvc-application-EL)を見ながら少しずつ進めました。
## 1. Linq to SQLでIncludeを使う
基本的なことですができてませんでした。。。
HogeのIndexページでHugaクラスの属性も表示する場合
以下のようにIncludeするとDBから一度にHogeとHugaを読み込むため
レスポンスが良くなります。

```C#:HogeRepository.cs
    public List<Hoge> ToList() {
        var q = from s in db.Hoges
                select s;
        return q.Include(m => m.Huga).ToList();
    }
```

## 2.string/intに対してHtml.DisplayForの使用をやめる
一覧画面で合計4000回ぐらいHtml.DisplayForを読んでいたところ
HTML出力が遅くなっていました。VS2015のプロファイラーで
CPU時間を確認するとHtml.DisplayForが原因の一部と判明。
モデルからはstringとintの出力しかしていなかったため
直接出力することにしました。
これは大量にHtml.DisplayForを実行している場合にだけ有効ですね。

転送量を削減すればディレイも短くなります。
## 3. 出力されるHTMLからスペースと改行を削除する
[参考サイト](https://www.danylkoweb.com/Blog/my-top-5-aspnet-mvc-actionfilters-AD)よりWhitespaceFilterAttribute
を拝借してプロジェクトに追加します。
WhitespaceFilterAttributeをControllerにつけると出力されるHTMLのスペースと改行が削除されます。
同じサイトにCompressFilterAttributeも載ってましたが
1つのcontrollerに両方使用するとうまく動かなかったため
私は効果の高かったWhitespaceFilterだけ使用することにしました。
ある一覧画面では500kb出力していたページが200kbまで落とせました。

```C#:HogesController.cs
    [WhitespaceFilter]
    public class HogesController : Controller {
        // Index 等の実装・・・
    }
```

## 4. JS/CSSを圧縮する
これも基本ですができてませんでした。
Web.Release.configに以下を追記

```xml:Web.Release.config
<compilation debug="false" targetFramework="4.5.2" />
```

## 5. CSSスプライトを利用する
私は↓のサイトを利用して各ページのリンク画像をCSSスプライトにしました。
[参考サイト2](https://spritegen.website-performance.org/)

## 6. レポジトリレベルでのキャッシュを実装する
これはアクセスが集中した場合に有効でした。
HogeクラスにToListという全レコードをリスト形式で取り出すメソッドがあったとして以下のような継承クラスを作成しControllerは
CachedHogeRepository を利用するように変えました。

```c#:CachedHogeRepository.cs
    public class CachedHogeRepository : HogeRepository
    {
        private static readonly object CacheLockObject = new object();
        private const string cacheKey = "CachedHogeRepository:ToList";
        public override IList<Hoge> ToList()
        {
            IList<Hoge> result = HttpRuntime.Cache[cacheKey] as List<Hoge>;
            // キャッシュにないの場合
            if (result == null)
            {
                lock (CacheLockObject)
                {
                    result = HttpRuntime.Cache[cacheKey] as List<Hoge>;
                    if (result == null)
                    {
                        result = base.ToList();
                        HttpRuntime.Cache.Insert(cacheKey, result,
                            null, DateTime.Now.AddHours(12), TimeSpan.Zero);
                    }
                }
            }
            return result;
        }
        // 作成更新削除があったらこのメソッドでキャッシュをクリアします。
        public override int SaveChanges()
        {
            var ret = base.SaveChanges();
            HttpRuntime.Cache.Remove(cacheKey);
            return ret;
        }
    }
```

(ここから追記)
## 7 link rel preloadを利用する
```html
<link rel="preload" as="script" href='@Scripts.Url("~/bundles/jquery")' />
```

cssのダウンロード/読み込み中にscriptをダウンロードさせることで時間を節約します。

## 8 CSSや小さなJavaScriptをインラインにする

```c#
public ActionResult InlineCss(string Path)
{
    BundleContext context = new BundleContext(
        new HttpContextWrapper(System.Web.HttpContext.Current),
        BundleTable.Bundles,
        Path);

    Bundle cssBundle = BundleTable.Bundles.GetBundleFor(Path);
    BundleResponse response = cssBundle.GenerateBundleResponse(context);

    return Content(response.Content);
}
```

私はHomeControllerに上のメソッドを追加し_Layout.cshtml内で下のように読み込むことで
CSSをインラインにしました。

```html
<style type="text/css">
    @Html.Action("InlineCss", "Home", new { Path = "~/Content/css" })
</style>
```

(ここまで追記)

現在はAzureで以下のようなレスポンスを達成できました。
![1.png](https://qiita-image-store.s3.amazonaws.com/0/169961/067dae90-1685-3ee5-3233-2d6936f61282.png)