ASP.NET MVC5 で Ajax を使用するための準備

忘れてしまうので、この場にメモ。

  1. NuGet パッケージ管理から「Microsoft.jQuery.Unobtrusive.Ajax」をインストール
  2. BundleConfig クラスの RegisterBundles メソッドに次のコードを追加
    bundles.Add(new ScriptBundle("~/bundles/jqueryajax").Include(
        "~/Scripts/jquery.unobtrusive-ajax*"));
  3. _Layout.cshtmlなどに次のコードを追加
    @Scripts.Render("~/bundles/jqueryajax")

コントローラー側で受け取ったリクエストが Ajax かどうかを判断するには、Request.IsAjaxRequest() から取得できる値を確認する。

XAML でチェックボックスとテキストがズレる

XAML で画面を作成していてチェックボックスやラジオボタンでテキストがズレて表示されたため、以下のように対応しました。

    <RadioButton 
            VerticalAlignment="Center"
            VerticalContentAlignment="Center"
            Content="テキスト"/>    

VerticalAlignment プロパティは自身のコントロールに対して、VerticalContentAlignment プロパティは Content プロパティに格納されているコントロールに対して作用するんですね。
Label コントロールの場合も同様で、Labelコントロールの位置と Content プロパティに格納されているオブジェクトの位置は別になるので注意が必要です。
Border 付きで表示するとわかりやすのですが、いつも軽くハマるのでメモしておきました。

C#6~7.2までの新機能一覧

C#6~7.2までの新機能について一覧表にしたものです。
先日のわんくま勉強会で登壇した時に作成した資料からの抜粋です。
ちょっとした確認の時などに、便利かと思うので転載しておきます。

このようにまとめてみると、最近は「式を使ったプログラミングに関する機能」と「処理の高速化」に関する追加が多くなってきているように感じますね。

C# 6
読み取り専用の自動プロパティ コンストラクターのみ設定可能な、読み取り専用の自動プロパティの作成 Constructor() {
    Hoge = 123;
}
int Hoge { get; }
自動プロパティの初期化子 自動プロパティの初期値が設定可能に int Hoge { get; } = 123;
ラムダ式を使ったメソッドの実装 メソッドやプロパティの実装にラムダ式が使用可能に int Method() => 1 + 2 + 3;
int Property => 5;
using static クラスを名前空間にインポート using static System.Math;
class Hoge {
    void Main() {
        var max = Max(1, 2);
    }
}
null 条件演算子 メンバーにアクセスする時、オブジェクトが null なら null を返す var x = Hoge?.ToString() ?? “”;
文字列補間 インライン式を使用した書式設定文字列式が記述できる var s = $”年齢は{ x.Age }才です”;
例外フィルター catch でキャッチできる例外を式で指定 try {
}
catch(Exception ex)
    when (e.Message.Contains(“Hoge”)) {
}
nameof 式 名前を文字列として取得 string Method() => nameof(Method);
catch finally ブロックで await 演算子 catch finally ブロックで await 演算子が使用可能に try {
throw new Exception();
}
catch(Exception ex) {
    await HogeAsync();
}
インデックス初期化子 Dictionary などのインデックス付きコレクションの初期化子 var d = new Dictionary<int, string> {
    [4] = “よん”,
    [8] = “はち”
};
コレクション初期化子の拡張メソッド Add拡張メソッドを宣言して、コレクション初期化子を使用できる public static class HogeExtensions {
    public static void Add(this FugaList list, Fuga x)
        => list.AddFuga(x);
}
オーバーロード解決の改善 あいまいなメソッド呼び出しと判断されていた処理の改善 static Task Hoge => Task.FromResult(0);
static void Fuga() => Task.Run(Hoge);
// Func<Task> であることを認識

 

C# 7
out 変数 out の値をメソッドの引数としてインラインで宣言 if (int.TryParse(“1”, out var i))
    return i * i;
タプル 手軽に名前の無い型を作成・コンパイラや IDE ツールでも対応 (int hoge, int fuga) GetValues()
    => (1, 2);
破棄  タプルおよびユーザー定義の型を分解する場合、メソッドを out パラメーターを使用して呼び出す場合に不要な値を破棄 var (_, piyo) = GetValue();
// hoge を破棄
パターン一致 型に基づいて、分岐ロジックを作成 if (piyo is int i) return i * i;

switch(piyo){
    case int i: return i * i;
    case string s: return s + s;
}

ref ローカル変数と戻り値 メソッド引数、ローカル変数、戻り値を参照 void Fuga() {
    var i = 5;
    ref var s = ref Hoge(ref i);
    s = 3;
}
ref int Hoge(ref int a) => ref a;
ローカル関数 関数の入れ子関数を作成 void Hoge {
    var i = 0;
    var j = fuga();
    int fuga() {
        return ++i;
    }
}
式形式のメンバーの追加 式を使用して作成できるメンバーが増えた class Hoge {
    Hoge(int fuga) => Fuga = _fuga;

    ~Hoge() => _fuga = 0;

    int _fuga;
    int Fuga {
        get => _fuga;
        set => _fuga = value;
    }
}

throw 式 throw ステートメントを、式として使用 return hoge ??
    throw new Exception(“fuga”);
一般化された async の戻り値の型 async 修飾されたメソッドの戻り値の型をTask と Task<T> 以外にできる
(ただし、一定の条件を満たす必要がある)
async ValueTask<int> HogeAsync() {
    if (_list.Count < 1000)
        return Hoge();

    return await Task.Run(() => Hoge());
}

数値リテラルの構文の改善 数値定数の表現方法が増えた const int BIN =  0b0001;  // 二進数
const int BIT =  0b0001_0000; // 長いビットパターン
const int DEC = 100_000_000; // 十進数

 

C#7.1
async Mainメソッド アプリケーションのエントリポイントメソッドをasync修飾 static async Task<int> Main()
static async Task Main(string[] args)
defaultリテラル式 型推論できる場合は、defaultの型名を省略 Hoge hoge = default;
// 今までは Hoge hoge = default(Hoge);
bool f = (hoge == default);
推論されたタプル要素の名前 タプル要素の名前を推論 var id = 3;
var title = “hoge”;
var member = (id, title);
// 今までは、(id: id, title: title) と名前を明記

 

C#7.2
値の型による参照セマンティクス 参照渡しに関する構文の強化 ref readonly int Hoge(in Fuga f) {
    // 読取り専用の引数と戻り値
    f.SetValue(19); // 変更されない
    return ref f;
}
void Piyo() {
    var fuga = new Fuga();
    ref readonly var x = ref Hoge(fuga); // x は 読取り専用
    x.SetValue(20); // 変更されない
}
末尾以外の名前付き引数 名前付き引数の後ろに位置引数を指定 HogeMethod(fuga: 3, “piyo”, 1, 2);
数値定数の先頭のアンダースコア 数値リテラルの前にアンダースコア(_) を含められる const int BIN =  0b_0001;  // 二進数
const int BIT =  0b_0001_0000; // 長いビットパターン
private protected アクセス修飾子 internal protected が 「internal」 or 「protected」なら
private protectedは「internal」and 「protected」
private protected int Hoge { get; set; }

Unit Test 実行時の config ファイルの引継ぎ

単体テスト実施時に、テスト対象メソッド内で設定値を参照している場合に、設定値を取得できずにテストが正常に動作しない場合があります。
これは、たとえばテスト対象のプロジェクトに含まれている Web.config や App.config ファイルは、テストプロジェクト側の設定ファイルではないからです。
したがって、テストプロジェクト内部に App.config ファイルを作成して、必要な設定値をテスト対象プロジェクトの設定ファイルからコピーすることで回避することができます。

一方、正しくコピーしている筈なのにテスト実行時にエラーになる場合もあります。
これは、NuGetパッケージインストール時に自動的に記述される設定値が正しくなかったり、テストを実行する側にもパッケージが必要だったりすることがあるためだと考えられます。
したがって、テストプロジェクトに App.config 作成した後に、テスト対象プロジェクトで使用している NuGet パッケージをテストプロジェクト側でもインストールすることで回避することができます。

以上、よくはまるので、この場にメモしておきます。

誕生日から年齢を計算

必要に応じて毎回書いていたのですが、面倒なのでメモしておくことにします。
まずは、次のようなメソッドにして算出する場合です。

int GetAge(DateTime birthday, DateTime? baseday = null) {
    var b = baseday ?? DateTime.Today; // 第2引数省略時はシステム日付時点の年齢を返す
    return (getNum(b) - getNum(birthday)) / 10000;
    int getNum(DateTime d) => d.Year * 10000 + d.Month * 100 + d.Day;
}

処理内容は単純で、二つの年月日を数値化して差を求めて 10000 で割った商を年齢として返しているだけです。
なぜ 10000 で割るのかというと、たとえば「1987年1月23日」なら 19870123 という数値になっているため、10000 で割ることで「年」の部分が取り出せるからです。
なお、getNum はローカル関数で、この機能は C#7 以降のみ対応です。

たとえば、以下のように使います。

var birthday = new DateTime(1989, 12, 1); // 誕生日
var age = GetAge(birthday); // 今日時点の年齢

必要に応じて計算したい場合は、以下のようにワンライナーで書くこともできます。

var age = ((t.Year * 10000 + t.Month * 100 + t.Day) - (b.Year * 10000 + b.Month * 100 + b.Day)) / 10000;

この方が高速ですし、わかりやすいかもしれません。

文字列を DateTime 型に変換

DateTime 型のプロパティ Date を実装した例です。
C#7 からは、以下のようなパターンで記述することが多くなりそうです。

public DateTime Date => 
    DateTime.TryParseExact(
	"20170123", 
	"yyyyMMdd", 
	CultureInfo.CurrentCulture, 
	DateTimeStyles.None, 
	out DateTime result) ? 
	    result : 
	    DateTime.Today;

文字列で表現された日付(時刻)が正しく DateTime 型に変換できた場合は、変換した結果の result の値を返します。
そうでなければ今日の日付を返します。

参考までに、以下のような書き方もできます。

public DateTime Date { 
    get {
        DateTime result;
        if (DateTime.TryParseExact(
            "20170123", 
	    "yyyyMMdd", 
	    CultureInfo.CurrentCulture, 
	    DateTimeStyles.None, 
	    out result)
        {
            return result;
        } 
	return DateTime.Today;
    }
}

なるべく式で書いた方が良いので、私は前者の書き方ですね。

ローカル関数を使用する

C#7 からローカル関数が使えるようになりました。
今までは、ひとつのメソッドが長くなりすぎた場合に、可読性を向上させる目的で下請けメソッドを宣言していました。

int Method() {
    :
    Shitauke();
    :
}

int Shitauke() {
    :
    :
}

ローカル関数を使うと、以下のように書くことができます。

int Method() {
    int Shitauke() {
        :
        :
    }
    :
    Shitauke();
    :
}

これによって、気軽に自身のメソッドブロック内に独立性の高い処理をメソッドアウトできるので、他のメソッドに干渉されることなく関連性の高いメソッドをまとめて読めるので積極的に使っています。
ただし、メソッドブロック内のコードの可読性が落ちます。
特に、ローカル関数の宣言部分と、メソッドのメインコードの区別がつかないところが悩ましいです。
いろいろ試してみた結果、次のように落ち着きました。

  1. ローカル関数名はキャメル表記にする
  2. ローカル関数の宣言はメソッドブロックの最後にまとめる
  3. そのメソッドの主処理の末尾に return ステートメントを記述する

ローカル関数名をキャメル表記にするのは、主処理が関数を呼び出すとき、他で宣言されているものかローカル関数かを明確にできるためです。
ローカル関数の宣言を最後にまとめる理由ですが、先頭にまとめると主処理がどこから開始されるのかを探さなければならないからです。
主処理の最後に return を記述する必要は本来はないのですが、これもどこまでが主処理の最後なのかを明確にするためです。

int Method() {
    :
    shitauke();
    :
    return;

    int shitauke() { // ローカル関数名はキャメル表記にする。
        :
        :
    }
}

ただし、これが正解かというと、いろいろな意見もありそうです。
とりあえず、しばらくは上記の規則いこうかと思います。

IEnumerable の遅延実行ではまる例

先ほど軽くはまったのでメモです。

次のコードは、正しく動作しません。

IEnumerable GetItems(){
    using (var db = CreateDataContext()) {
        return db.Table;
    }
}

void Main(){
    var items = GetItems();
    foreach(var x in items) {
        Console.WriteLine(x.Title);
    }
}

理由です。
db.Table に実際にアクセスされるのは、Main メソッドの foreach で items を使用した時点ですが、この時すでに DataContext インスタンスが破棄されているためデータベースにアクセスできずにエラーが発生します。

もう少し詳しく説明すると、GetItems メソッドが return している db.Table は IQueryable型です。
これは、データベースへのアクセスに関する定義を返すだけで、実際に取得したコレクションインスタンスを格納している訳ではありません。
一方 IQueryable 型は IEnumerable 型を継承しているため、GetItems メソッドのように暗黙の型変換が実行されています。
ここに気づかないとはまる訳です。
GetItems メソッドを以下のようにすると、正しく動作するようになります。

using (var db = CreateDataContext()) {
    return db.Table.ToList(); // ←変更
}

理由は、ToList メソッドで取得したデータからコレクションインスタンスを生成したものを返すためです。
簡単な理由ですが、実際の開発コードでは、複雑にメソッドが絡み合っていたりするので、軽くはまるのでメモしておきます。