nuits.jp blog

C#, Xamarin, WPFを中心に書いています。Microsoft MVP for Development Technologies。

Dapperの拡張ライブラリ 比較検討

皆さん、Dapper使っていますか?

私は比較的最近まで、オレオレMicro ORMを使っていたので、あまり使っていませんでしたが、いろいろと反省してDapperに移行中です。

さてDapper非常に良いと思うのですが、単品だとさすがに実装効率があまりよろしくありません*1。そこで活躍するのがDapperの拡張ライブラリです。

しかし、多くの拡張ライブラリが存在しているため、何を使うか悩みます。というかまだ悩み中です。何かお勧めがあれば教えてもらいたいところです。

とはいえ、自分で調べないという訳にもいきませんので、Dapper拡張ライブラリをいくつか比較してみました。

前提条件

  • 私自身Dapperは「にわか」ユーザーです
  • あくまで機能「メニュー」上での判断で、使い込むのはこれからです
  • 基本的にエンタープライズなSIer視点です

個人的な結論

つぎの二つの条件を満たすのであればDapper.FastCrudを、でなければDapperExtensionsを使いたいと思いました。

  • 対象のデータベースがDB2、Oracle以外
  • カラム名の別名定義が不要

後者は私の勘違いでDapper.FastCrudでも可能かもしれません。また、Entityクラスは基本的にDBから自動生成する想定でいるので、個人的には、あまり気にしていません。

比較一覧

比較的使われていそうなものを、私自身が気になる観点でまとめたのが次の表です。

ライブラリ カスタム
マッピング
複合キーサポート .NET Standard ページング ダウンロード
Dapper.Contrib Attribute × 1.3, 2.0 × 250,050
Dapper.Rainbow Attribute × 1.3 ×
DapperExtensions Fluent or Attribute × 252,670
DapperExtensions
.DotnetCore
Fluent or Attribute 1.6 13,233
Dapper.SimpleCRUD Attribute × × 83,588
Dapper.FastCrud Fluent or Attribute 1.6 42,782
Dommel Fluent × 1.3 × 38,026

各項目の説明は次の通りです。

項目 説明
カスタムマッピング データベースのテーブルや列名と、クラスのマッピングのカスタム方法を表します。
エンティティクラスにAttributeで指定する方法と、エンティティクラスは純粋なPOCOにして、外部からFluent方式でマッピングを指定する方法の2種類に分けて整理しています。
複合キーサポート 複合プライマリーキーをサポートしているかどうかを表します。
未サポートのライブラリの場合は、複合キーが必要な場合は全てサロゲートキーに置き換えてDBを設計する必要があり、DBを自由に設計できる前提条件が必要になります。
.NET Standard 現時点でサポートされている.NET Standardのバージョン。
小さい数字が書かれている方が、使う側からは利用しやすいです。
ページング エンタープライズなので、ページングに便利な機能の有無はすごい気になります!
ダウンロード NuGet上のダウンロード数です。

考察

私の利用シーン的に複合キーNGは厳しいため、選択肢はつぎの何れかに絞られます*2

表上の条件では(ダウンロード数を除き)大きな差がないため、もう少しだけ詳細を見てみます。

対応データベース

DB2 MySQL Oracle PostgreSQL SQL Server SQL Server Compact SQLServer LocalDB SQLite
DapperExtensions
Dapper.FastCrud

処理速度

Dapper.FastCrudのベンチマークによると 全体的にDapper.FastCrudの方が「やや」早いようです。

ただし決定的な程の差はないように見えます。

使い勝手

Insert、Update、Delete、IDによるSelectあたりは殆ど大差ありません。違うのは条件指定でのSELECT分でしょう。

次の例はDapper.FastCrudにコード例です。

foreach (var detail in 
    connection.Find<PurchaseOrderDetail>(statement => statement
        .Where($"{nameof(PurchaseOrderDetail.ProductID):C}=@ProductID AND {nameof(PurchaseOrderDetail.PurchaseOrderID)} >= @PurchaseOrderID")
        .OrderBy($"{nameof(PurchaseOrderDetail.PurchaseOrderID)}, {nameof(PurchaseOrderDetail.PurchaseOrderDetailID)}")
        .WithParameters(new { ProductID = 1, PurchaseOrderID = 1000 })))
{
    Console.WriteLine($"PurchaseOrderID:{detail.PurchaseOrderID} PurchaseOrderDetailID:{detail.PurchaseOrderDetailID}");
}

これと同等のSQLをDapperExtensionsで記載すると次の通りになります。

var predicateGroup = new PredicateGroup {Operator = GroupOperator.And, Predicates = new List<IPredicate>()};
predicateGroup.Predicates.Add(Predicates.Field<PurchaseOrderDetail>(f => f.ProductID, Operator.Eq, 1));
predicateGroup.Predicates.Add(Predicates.Field<PurchaseOrderDetail>(f => f.PurchaseOrderID, Operator.Ge, 1000));
var sort = new List<ISort>
{
    Predicates.Sort<PurchaseOrderDetail>(x => x.PurchaseOrderID),
    Predicates.Sort<PurchaseOrderDetail>(x => x.PurchaseOrderDetailID),
};

foreach (var detail in
    connection.GetList<PurchaseOrderDetail>(predicateGroup, sort))
{
    Console.WriteLine($"PurchaseOrderID:{detail.PurchaseOrderID} PurchaseOrderDetailID:{detail.PurchaseOrderDetailID}");
}

好みの問題ではありますし、正直どっちも微妙な思いがありますが、相対的には個人的にDapper.FastCrudの方が好きかな?

ただしDapper.FastCrudの場合、カラム名の別名がこの方式では定義できない気がしますが大丈夫なのでしょうか?まだ良く分かりません。

さいごに

という訳でした。
「こっちの方が良いよ」というご意見がある方は、ぜひ教えてください。

*1:主キー指定で1レコード取得したり、1レコードのアップデートしたりを都度SQL書くのは非効率ですよね

*2:リレーションテーブルは複合キーにしたかったり、既存DBが複合キー使っていたりなど