IEnumerableからExcelへ出力のエクステンション

#はじめに
[C#でOpenXMLを使ったExcelの読み込み](http://qiita.com/st450/items/219ba02be4726d773a65)を使い読み込んでいたが、OpenXMLを使いデータを書き込みたく調べていたところ「ClosedXML」を使うとかなり簡単に読み書き込みできることがわかった。
データを分析するときは、Power Query、Power Pivodeでデータを抽出、加工集計をおこなっていたが、大量にデータ処理していると、Powerシリーズでは加工に時間がかかりすぎるため、C#で加工して直接Excelに書き込み加工したくIEnumerableからExcelへ出力するエクステンションを作成した。
以前は、複雑な処理は一度ReportにしてExcelに出力した結果を集計していたので、いきなりExcelに出力でき簡単に集計できるため作業時間の短縮できた。

#使ったライブラリ
##ClosedXML
[NuGet](https://www.nuget.org/packages/ClosedXML/)
ClosedXMLで検索するとかなりサンプルがある。
##Dapper
[NuGet](https://www.nuget.org/packages/Dapper)
とても使いやすく、Stored Procedureも簡単に扱える。

#サンプル
リハで初期加算を算定している最初の日だけを取得したく作成した簡単なサンプル。

```csharp:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;

using Dapper;
using ClassLibrary.Extensions;

namespace Sample
{
    class Program
    {
        private static string Sql =
@"
	SELECT WK.患者番号, convert(date, WK.年月日, 112) as 日付, WK.名称
		FROM WK診療日別データ AS WK 
		WHERE 対象年月 BETWEEN @ymFrom AND @ymTo
			AND WK.診療区分 = '80'
			AND WK.名称 IN ('初期加算（運動器）', '初期加算（脳血管疾患等）')
";

        static IEnumerable<Reha> GetData()
        {
            var constr = Properties.Settings.Default.ConnectionString;
            using (var conn = new SqlConnection(constr))
            {
                var q = conn.Query<Reha>(Sql,
                        new { ymFrom = "201403", ymTo = "201503" },
                        commandTimeout: 500000);
                foreach (var grp in q.GroupBy(r => r.患者番号))
                {
                    var isFirst = true;
                    var dtFrom = DateTime.MinValue;
                    var dtTo = DateTime.MinValue;
                    foreach (var obj in grp.OrderBy(r => r.日付))
                    {
                        if (!isFirst && obj.日付.InRange(dtFrom, dtTo))
                            continue;
                        isFirst = false;
                        dtFrom = obj.日付;
                        dtTo = dtFrom.AddDays(13);
                        yield return obj;
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            GetData().ToExcel("output.xlsx");
        }
    }
}
```

```csharp:IEnumerableExtention.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using ClosedXML.Excel;

namespace ClassLibrary.Extensions
{
    public static class IEnumerableExtention
    {
        public static void ToExcel<T>(this IEnumerable<T> enumerable, 
                string path, string sheetName = "Sheet1")
        {
            // Excelのブックを作成
            var book = new XLWorkbook();

            // シートを作成
            var sheet = book.Worksheets.Add(sheetName);

            // Tタイプのプロパティを記録
            var props = typeof(T).GetProperties().ToList();

            // 行件数
            var rowCount = 1;

            // ヘッダーにあたる行を作成
            {
                // 行を取得
                var row = sheet.Row(rowCount++);

                // ヘッダー列の値を設定
                for (var i = 1; i <= props.Count; i++)
                    row.Cell(i).Value = props[i - 1].Name;
            }

            // データ出力
            foreach (var obj in enumerable)
            {
                // データセットする行を取得
                var row = sheet.Row(rowCount++);

                // 各プロパティの値をセット
                for (var i = 1; i <= props.Count; i++)
                {
                    var prop = props[i - 1];
                    var val = prop.GetValue(obj);
                    if (val is string)
                        row.Cell(i).Value = "'" + val;
                    else
                        row.Cell(i).Value = val;
                }
            }

            // テーブルを作成
            sheet.Range(1, 1, rowCount - 1, props.Count).CreateTable();

            // セルの幅をコンテンツの内容にあわせてリサイズ
            sheet.Columns(1, props.Count).AdjustToContents();

            // 保存
            book.SaveAs(path);

        }
    }
}
```