C# 8 switch 式の威力はすごい。EntityFramework Core 用の Upsert が実質 3 行 *1 で記述できてしまった *2 。
public static void Upsert<T>(this DbSet target, IEnumerable<T> source) where T : class, IDbMappable => source .GroupJoin(target , l => l.GetKeyObject(), r => r.GetKeyObject(), (l, r) => r.FirstOrDefault() switch { // data-driven table operation with side-effect expected null => target.Add(l) as T, // Insert var f when ! f.Equals(l) => f.Substitute(l) , // Update _ => l , // Nop }) .ToList();
同一キーレコードであるものないもの、同一値レコードであるものを GroupJoin() と switch 式を用いることでグループ分けし、データドリブンで次のアクションを Insert / Update / Nop (何もしない) と振り分ける。
ラムダ式を要求する LINQ によるデータ処理であるため switch 式を使うのだが、switch 文 ではなくわざわざ switch「式」を使っているにも関わらず、副作用に依拠してテーブル処理しており、逆に出力 ToList() は何も利用していないのがトリッキーである。switch 式がすばらしいのは、変数宣言式が使えない現行 C# にあって、var f として型推論・変数代入で受けられるところ。これにより手続や条件分岐を冗長に記述することなく Equals ()や Substitute() *3 といった式にかけられる。
LINQ を使っていると、データ変換の一連の式の内部で途中結果を複数回参照する場面が多々あり、
var obj = new { Text = "Sample", Value = 1 }; var result = (var x = obj.Value + 1) * x;
のように、式の流れを止めずに変数宣言式を用いて中間変数を定義したいところだが、これができない。今後は、代わりに
var result = (obj.Value + 1) switch { var x => x * x };
と中間変数を受けるだけの目的で switch 式を使うようになるかもしれない。