前回、ソートフィールド定義を扱うクラスを作って、単体テストでソートできるところまで作ってみました。

今回はwebアプリから使ってみます。

ASP.NET MVCプロジェクトの作成

プロジェクトの追加でC#->Web->ASP.NET Webアプリケーションを選びます。

名前はChimaLibSampleにしました。

プロジェクトの設定はこんな感じ。

MVCかつWebAPIを選びます。

認証は無し、Azureは使いません。

プロジェクト参照

前回作ったクラスライブラリをプロジェクト参照します。

実際にはgitのサブモジュールとしてライブラリを登録してから、既存プロジェクトとして追加。

パッケージ

Entity Frameworkをnugetからインストール。

PM> Install-Package EntityFramework

Modelの定義

こちらの

Chaper2 2-4-3章(49Page)を参考にモデル、コンテキスト、イニシャライザを追加します。

Articles

モデルは参考記事にあるArticleのみ定義しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Article
    {
        public int Id { get; set; }

        [DisplayName("URL")]
        [DataType(DataType.Url)]
        public string Url { get; set; }

        [DisplayName("タイトル")]
        public string Title { get; set; }

        [DisplayName("概要")]
        [DataType(DataType.MultilineText)]
        public string Description { get; set; }

        [DisplayName("ビュー数")]
        public int Viewcount { get; set; }

        [DisplayName("公開日")]
        [DisplayFormat(DataFormatString = "{0:yyyy年MM月dd日}")]
        public DateTime Published { get; set; }

        [DisplayName("公開済")]
        public bool Released { get; set; }

Controller

コントローラーはScaffolding(VSのテンプレート)の「Entity Frameworkを使用した、ビューがあるMVC5コントローラー」を使用します。

設定はこんな感じ。

Indexをソート可能に

Articleの一覧を表示するIndexにソート機能を組み込んでいこうかと思います。

Scaffoldingで作成されたアクションはこちら。

1
2
3
4
5
// GET: Articles
        public ActionResult Index()
        {
            return View(db.Articles.ToList());
        }

これに色々追加していきます。

ソートフィールド定義のインターフェース化

各フィールド毎のソート定義をリストにして、ループさせればOKかと思ったんですが、

ソート定義にフィールドの型を持っているため無理。

1
2
3
4
5
SortFieldDefinition<Article,*****>[] sort_fields = new[] { //フィールドの型が必要
                article.DefineSort(a=>a.Url),
                article.DefineSort(a=>a.Title),
                ....
            };

てことで、interfaceを作ります。

テストはこんな感じ

1
2
3
4
5
6
[TestMethod]
        public void SortDefInterface_New_Test1() {
            Article article = null;
            ISortFieldDefinition<Article> sortdef = article.DefineSort(obj => obj.Title);  //interfaceです
            Assert.AreEqual("Title", sortdef.SortKey);
        }

interfaceはVSのインターフェースの抽出を使って、マウスでちょいっと作れます。

SortFieldDefinitionで右クリック→クイックアクション。

インターフェースの抽出

名前とか確認してOK

できた

1
2
3
4
5
6
7
public interface ISortFieldDefinition<TModel>
    {
        string SortKey { get; }

        IQueryable<TModel> AddOrderBy(IQueryable<TModel> aQuery, string aCurrentSortKey);
        string GetNextSortKey(string aCurrentSortKey);
    }

拡張メソッドもinterfaceを返すように変更します。

1
2
3
4
5
6
public static class SortFieldDefinitionExtention
    {
        public static ISortFieldDefinition<TModel> DefineSort<TModel, TKey>(this TModel aModel, Expression<Func<TModel, TKey>> aKeySelector) {
            return new SortFieldDefinition<TModel, TKey>(aKeySelector);
        }
    }

Indexアクションの変更

てことで、変更後はこちら

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// GET: Articles
        public ActionResult Index(string sort)
        {
            var query = db.Articles.AsQueryable();  //データソース

            Article article = null;
            ISortFieldDefinition<Article>[] sort_fields = new[] {   //ソートフィールド定義
                article.DefineSort(a=>a.Url),
                article.DefineSort(a=>a.Title),
                article.DefineSort(a=>a.Description),
                article.DefineSort(a=>a.Viewcount),
                article.DefineSort(a=>a.Published),
                article.DefineSort(a=>a.Released),
            };

            foreach(var field in sort_fields) { //ソート適用
                query=field.AddOrderBy(query, sort);
            }

            return View(query.ToList());    //ソート済み結果をViewに返す。
        }

データソース

データソースはdb.Articlesですが、IQueryableでほしいので変換します。

ソートフィールド定義

ココが今回のキモなんですが、ソートフィールド定義をモデルの各プロパティごとに生成して配列に収めます。

この配列、staticで持ってもいいかと思います。

ソート適用

フィールド定義でループして、データソースに適切なorderbyを追加します。

ソートしてみる

デバッグ実行で

http://localhost:?????/Articles/?sort=Url

http://localhost:?????/Articles/?sort=Title

とかにアクセスすると、ソートされますね。

短くなった

ここまでで一応のソートができました。modelのプロパティが増えてもソートフィールド定義に一行追加すればOKになってます。

Webプロジェクトのソースはこちら

なんとなくココで満足してしまってるんですが、View側も変更してヘッダクリックでソートできるようにしないと使い物になりませんね。

次回その辺をやるかもしれませんります。