Soma 0.9.0.0 リリース

F#で作ったO/Rマッパー Soma 0.9.0.0をリリースしました(F#はもちろんC#VB.NETでも使えます)。ぜひお試しください。

Release Notes

  • New Feature - Microsoft SQL Server Compact 4.0 をサポートしました.
  • New Feature - SQLite をサポートしました.
  • New Feature - 動的オブジェクトへのマッピングをサポートしました。 (F# and C#).
  • Change - クエリ結果のカラムをオブジェクトにマッピングする際のルールを緩めました(マッピングするクラスの全プロパティが結果セットに含まれていなくてもOKとした)。
  • Fix - TableAttribute/ColumnAttribute/ProcedureAttribute/ProcedureParamAttribute の IsEnclosed プロパティが MySQL で正しく動作するようになりました.
  • Fix - 列挙型をラップした null許容型 が正しくカラムにマッピングされるようにしました。

Soma 0.9.0.0 の新機能

新機能は大きく言って2つあります。

  1. Microsoft SQL Server Compact 4.0 と SQLite といった軽量RDBMSのサポート
  2. 動的オブジェクトへのマッピングのサポート

Microsoft SQL Server Compact 4.0 と SQLite のサポート

設定方法やデータ型のマッピングなどはドキュメントを読んでもらえればと思います。
ここでは、一番重要なトランザクションを絡めた典型的な使い方だけを紹介します。
C#で書くとこんなコードになります。

var db = new LocalDb(new Config());

using (var tx = new TransactionScope())
using (var con = db.CreateConnection())
{
    var emp = db.Find<Employee>(con, 1);
    emp.EmployeeName = "Hoge";
    db.Update(con, emp);
    db.Delete(con, emp);

    tx.Complete();
}

ポイントは

SQL Server Compact 4.0 と SQLite は、TransactionScopeに複数のコネクションを関連付けられないのでこんな感じになります。コネクションを直接扱うのが煩雑ですが、気になる場合は、実行環境に応じてスレッドやHttpContextに紐付けて管理してクライアントからコネクションを隠蔽するといいんじゃないでしょうか。

動的オブジェクトへのマッピングのサポート

コード書いていておもしろかったのはこっちの対応でした。次の2つの記事に大いに触発されてやってみることにしました。

こんな感じで使えるようにしました。

C#で動的オブジェクトへマッピング

var empList2 = db.Query<dynamic>(@"
select
e.EmployeeId, e.EmployeeName, e.DepartmentId, e.VersionNo
from
Employee e
where
e.DepartmentId = /* emp.DepartmentId */0
"
, new { emp });
Console.WriteLine("QUERY RESULTS AS DYNAMIC OBJECTS :");
foreach (var e in empList2)
{
Console.WriteLine("EmployeeId={0}, EmployeeName={1}", e.EmployeeId, e.EmployeeName);
}
Queryメソッドの型パラメータにdynamicを渡しているのがポイント。そして、最後から2行目の e.EmployeeId とか e.EmployeeName が遅延バインディングです。

F#で動的オブジェクトへマッピング

let empList = 
MyDb.query<dynamic> "
select
e.EmployeeId, e.EmployeeName, e.DepartmentId, e.VersionNo
from
Employee e
where
e.DepartmentId = /* emp.DepartmentId */0
"
<@ let emp = emp in () @>
printfn "QUERY RESULTS AS DYNAMIC OBJECTS :"empList |> List.iter (fun emp -> printfn "EmployeeId=%O, EmployeeName=%O" emp?EmployeeId emp?EmployeeName)
こちらもquery関数の型パラメータにdynamicを渡しているのがポイント。そして最後の行の emp?EmployeeId や emp?EmployeeName が遅延バインディングです。

まとめ

F#版は、F#にはdynamicキーワードがないにもかかわらずC#版と同じように型パラメータでdynamicを指定できるのがかっこいい!! はず。ちなみに、F#では?演算子から推論が働くのでdynamicの型パラメータは省略できることがあります(上の例は省略できます)。

プロトタイピングとかツールとかなら遅延バインディングを使った動的なオブジェクトもいいかもしれないですね。結果セットを受け取る入れ物を用意する必要がないので楽です。ただし、デメリットはもちろんあります(実行時エラーがおきやすくなる、nullが混入する、Enumなどへ型変換できないとか)。