前回、ソートフィールド定義を扱うクラスを作って、単体テストでソートできるところまで作ってみました。
今回は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側も変更してヘッダクリックでソートできるようにしないと使い物になりませんね。
次回その辺をやるかもしれませんります。