ADO.NETとORMとMicro-ORM
-dapper dot netを使ってみた-
at
ヒーロー島 秋の収穫祭 2013
2013/10/12
きよくらならみ
自己紹介
• きよくら ならみ
– @kiyokura / id:kiyokura
• 岡山生まれ岡山育ちのプログラマー
– 現在は県内の某製造業で社内SE
• NET系の開発やWebアプリ開発
– Microsoft MVP for ASP.NET/IIS
本セッションの目的
• Micro-ORMを知ってもらう
• dapper dot netを知ってもらう
本セッションのゴール
• .NET向けのDBアクセス手法の概要を知る
• ORMの概要と"不得手"な部分を知る
• dapper dot netの概要を知る
本日のアジェンダ
• .NETのDBアクセステクノロジの基本
• ORM 超・概要と.NETのORM
• ORM≠銀の弾丸
• Micro-ORM そしてdapper dot net
• まとめ
.NETのDBアクセステクノロジの基本
基本はADO.NET
• .NETにおけるDBアクセスの基本
• 様々なデータソースに対して
『一貫性を持ったアクセス機能を実現』
ADO.NET
ADO.NETのスタック
データソース(DBほか、xmlやExcel等)
データプロバイダ
Connection Command
DataReader
Transaction
Transaction DataAdapter
プログラム(C# or VB.NET or etc)
DataSet
DataTable
一貫性のある操作
• 各ソースに対応した”プロバイダ”
– 製品専用のプロバイダ
• SQL Server
• SQL Server CE
• Oracle
• MySQL
• PostgreSQL
…等々。
– 汎用ドライバ用
• OLE DB
• ODBC
プロバイダはインタフェースを実装
• 共通のインタフェースを実装することで
操作の一貫性を保証
– IDataReader
– IDbConnection
– IDbCommand
– IDataParameter
– IDbTransaction
– IDbDataAdapter
接続の基本:Connection
• Connectionオブジェクトに
接続文字列をセット
• 接続を開始
クエリ実行の基本:Command
• Commandオブジェクトによりクエリ実行
– DML・DDL・DCL
• CommandにSQLをセットして実行
• 結果としてレコードが返るときは?
→ DataReaderを使う
データ読込の基本:DataReader
• Commandの結果DataReaderが返る
• レコードに対して順番にアクセス
– DBのカーソルやADOのRecordSetに近い
最も基本的なデータアクセス流れ
• DBに接続
– Connectionオブジェクト
• コマンドにSQLをセット
– Commandオブジェクト
• コマンドを実行してリーダーを作成
– DataReaderオブジェクト
リーダーをループしながらデータを読む
最も基本的なデータアクセス流れ
DB
Connection
接続文字列
(1)
Command
SQL: SELECT * FROM HOGEHOGE
(2)
DataReader(3)
コード例
ここまでのまとめ
.NETのDBアクセスの基本はADO.NET
ソース毎に異なるプロバイダを利用
共通のインタフェースにより
ソースが変わっても一貫した操作を提供
データ読み込みの基本手順
Connectionで接続
Commandで実行
DataReaderで読込
ORM 超・概要と.NETのORM
ORMって?
• Object-Relational mapper / mapping
– O/RM 、 O/R-M、OR-M
– 「おーあーるまっぱー」
• 永続化データに対するギャップを埋める
– メモリ上の変数とDB上のカラムのマッピング
– "オブジェクト操作"と"DBの操作"
• その他、DB操作をラップ
代表的な機能
• クエリの自動生成
• データ(値)のマッピング
• DB操作のラッピング
• ソースコード自動生成
ライブラリや実装よって有無はあります
例:Entity Framework
• .NET用のORMであるEntity Framework
を例としてデモします
– ASP.NET MVC 4 + Entity Framework
Entity Frameworkの例
• メソッドを呼ぶだけでデータを取得
– 列挙型のオブジェクトとしてデータが
「マネージドのオブジェクト」として取得できる
• オブジェクトに値とセットしてメソッドを呼
べば更新完了
SQLを全く意識しない
DBの接続等の制御用もまったく意識しない
.NETで利用できるORMの例
• マイクロソフト製
– ADO.NET DataSet
– LINQ to SQL
– Entity Framework
• サードパーティー製
– NHibernate
– S2Dao.NET
– LightSpeed(商用)
…等々
ここまでのまとめ
ORMはDB操作をラップ
SQL作成や実行、データのオブジェクトへの
マップ等
.NET向けにも多数の実装あり
ORM≠銀の弾丸
ORMを使うべきか否か
• "便利なケース"では使うべき
– どんどん使いましょう
• "美味しくない場合"は使わなくてよい
– 無理して使うようなものでもない
ただし当然ですが
• 銀の弾丸は無い
• あらゆるケースで全く問題が無いわけで
は無い
"美味しくない"例をいくつか考えてみます
DB設計がORM適さなくなりがちなケース
• データがアプリケーション内に閉じない
– データが多数のアプリで横断的に利用される
– アプリから見えない業務ロジックが存在
• 『データ > アプリ』
– 利用範囲の他、権限や寿命
• データ中心アプローチ(DOA)
基幹業務システムの例
販売データ
労務データ
製造データ
研究開発
データ
顧客データ
販売管理システム 生産管理システム
賞与計算システム 製造品質管理
システム
パフォーマンスで問題が出るケース
• ORMでパフォーマンスが出ない場合が
– 仮に設計に問題がなくても
• パフォーマンス比較の例
手法 実行時間(短=速)
Hand coded(DataReaderでループ) 47ms
NHibernate SQL 104ms
Linq 2 SQL ExecuteQuery 181ms
Entity framework ExecuteStoreQuery 631ms
dapper dot net の公式サイト上のベンチマーク結果より
( https://code.google.com/p/dapper-dot-net/ )
その他のアリがちな話
• そもそもDB設計がイケてない
– 自動生成のクエリではパフォーマンスが(
– 適切にリレーションが貼られていない
• プロバイダが(使いたい)ORMに未対応
– たまにある
• DBサーバ・ネイティブクライアント・プロバイダのそれぞ
れのバージョンの対応とかも関連
• 自動生成されたSQLがDBAの怒りに触れる(
– DBA:「そんなトン○キなクエリ投げるな!」
ここまでのまとめ
ORMのメリットが生きる場合は使うべき
ORMのメリットが生きない・使いにくい
場面は存在する
そんなときは無理して使う必要はない
Micro-ORM
そしてdapper dot net
ORMが使えなくても
• なるべく楽はしたい
• 生ADO.NETはいろいろ面倒
– Connectionはまだしも
– SQL組み立てて
– Commandを作って
– Parameterを作って
– 実行して
– 値をオブジェクトにマッピング
そこでMicro-ORM
• 隙間を埋めてくれるソリューション
Micro-ORMとは
• 特徴
– フルスタックのORMほどの機能は無いが
– そこそこ便利な機能があって
– 軽量で
– 早い
• そういうカテゴリ(?)のライブラリの総称
機能のあるやなしや
クエリの
自動生成
データ
(値)の
マッピング
DB操作の
ラッピング
ソース
コード自
動生成
☆物によってはある程度するものも
つまり
• SQLは自分で書く必要がある
• 実行時の面倒なことのうち、
ある程度のことをやってくれる
代表例
• dappar dot net
– この後、少し詳しく話します
• Massive
– https://github.com/robconery/massive
• PetaPoco
– http://www.toptensoftware.com/petapoco
dapper dot net
• Dapper.NETとも書かれる
– .NET向けのMicro-ORM
• オープンソース
– ApachとMITのデュアルライセンス
• サイトはこの辺
– https://code.google.com/p/dapper-dot-net
– https://github.com/SamSaffron/dapper-dot-
net
dapper dot net
• 作った人: Sam Saffron
– Stack Overflowの中の人
• 早い
– 生SQL実行(DataReader)とほぼ互角
– Stack Overflowのパフォーマンス改善のため
に作られた(らしい)
dapper dot net
• どのDBで使える?
– ADO.NET準拠のプロバイダが使えるなら大抵
いけるんでは?
• IDbConnectionの実装なら、多分
• 確認した範囲
– SQL Server / SQL Server CE
– Oracle
– MySQL
– postgreSQL
使い方
• 2STEPで簡単!
STEP1
• NuGetで一発導入!
STEP2
• 使いたい場面でDapper名前空間をイン
ポート
– 拡張メソッドなのでインポート必須!
これだけです
実際の例をご紹介します
• ADO.NETで直接実行する場合との
対比で紹介します
例1:とりあえずループして読みだす
ADO.NETの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var sql = "select ID, Name , Age , Email From Employee;";
var cmd = new SqlCeCommand(sql, cn);
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
dr["ID"],
dr["Name"],
dr["Age"],
dr["Email"]);
}
}
}
dapperの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var sql = "select ID, Name , Age , Email From Employee;";
var result = cn.Query(sql);
foreach (var d in result)
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
d.ID,
d.Name,
d.Age,
d.Email);
}
}
ポイント1
• Query拡張メソッド
– Connectionオブジェクトを拡張
ポイント2
• 戻り値はIEnumerable<dynamic>
つまり
• Query拡張メソッドを実行すると
• 戻り値はIEnumerable<dynamic>が返っ
てくる
• dynamicの中身はカラム名から自動生成さ
れた匿名型にマップされている
例2:用意した型にマップする
ADO.NETの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var cmd = new SqlCeCommand("select ID, Name , Age , Email From Employee;", cn);
var result = new List<EmployeeEntity>();
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
result.Add(new EmployeeEntity()
{
ID = (int)dr["ID"],
Name = (string)dr["Name"],
Age = (int)dr["Age"],
Email = (string)dr["Email"]
});
}
}
foreach (var d in result)
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
d.ID,
d.Name,
d.Age,
d.Email);
}
}
dapperの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var sql = "select ID, Name , Age , Email From Employee;";
var result = cn.Query<EmployeeEntity>(sql);
foreach (var d in result)
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
d.ID,
d.Name,
d.Age,
d.Email);
}
}
ポイント1
• Query<T>拡張メソッド
– マップしたい型を型パラメータに指定
ポイント2
• 戻り値はIEnumerable<dynamic>
つまり
• 型パラメータTをとる
Query拡張メソッドを実行すると
• 戻り値はIEnumerable<T>が返ってくる
– クエリの結果が型Tにマップされている
• カラム名に対応したメンバにマップ
• 適切に型の変換も行われている
例3:パラメータをバインドする
ADO.NETの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var cmd = new SqlCeCommand("select ID, Name , Age , Email From Employee where Age = @age;", cn);
var param = cmd.CreateParameter();
param.SqlDbType = System.Data.SqlDbType.Int;
param.Direction = System.Data.ParameterDirection.Input;
param.ParameterName = "age";
param.Value = 25;
cmd.Parameters.Add(param);
var result = new List<EmployeeEntity>();
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
result.Add(new EmployeeEntity()
{
ID = (int)dr["ID"],
Name = (string)dr["Name"],
Age = (int)dr["Age"],
Email = (string)dr["Email"]
});
}
}
foreach (var d in result)
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
d.ID,
d.Name,
d.Age,
d.Email);
}
dapperの場合
using (var cn = new SqlCeConnection(connectionString))
{
cn.Open();
var sql = "select ID, Name , Age , Email From Employee where Age = @Age;";
var result = cn.Query<EmployeeEntity>(sql, new { Age = 25 });
foreach (var d in result)
{
Console.WriteLine("ID:{0} , Name:{1} , Age:{2} , Email:{3}",
d.ID,
d.Name,
d.Age,
d.Email);
}
}
ポイント
• Query<T>拡張メソッドの第二引数に
パラメータを指定
– SQL文のパラメータと同じメンバがあればOK
• 匿名型でもOK
つまり
• Query拡張メソッドの第二引数に
• SQL文中のパラメータと
同名のメンバを持つオブジェクト
を渡すと
• パラメータとしてバインドして実行する
その他
• 複数の型(ネストされた型)にバインド
することも可能
– 但し、基本1:1
• もちろん、INSERT / UPDATE / DELETE
等も発行できる
FAQ
Q1:速度は?
• A1:早いです
– 生ADO.NETとほぼ誤差の範囲くらい
– 公式サイトのベンチマークも参照
Q2: DB側の命名がsnake_case…
• A2-1:SQLで別名を付ける
– first_name as FirstName
• A2-2:SetTypeMapという仕組みを使う
– 詳細は公式サンプルや、neue ccさんのblog
エントリを参照
• http://neue.cc/2012/12/11_390.html
Q3:SQL書けません!
• A3:頑張って書いてください
– どうしても解らない人は、クエリビルダ的な
ツール使うといいのでは
• SQL Server Management Studio
• A5:SQL Mk-2
• SI Object Browser
– もしくは、ヘルパライブラリを使う手も
• Dapper.Rainbow
• Dapper.Contrib
Q4:マップするクラスは自分書く?
• A4:自分で書きましょう
– 別途、自動化・半自動化はできるのでは
• DDLやDB定義書から
• マクロやT4で自動生成等
補足情報
• ソースは1ファイルなのでGithubからとっ
てきて入れてもいいです
– ちょっと改変して使いたいならそれが楽
• MySQLで高負荷かけると不具合が出るら
しい
– 詳細と対策などはneue ccさんのblogエント
リ参照:
• http://neue.cc/2013/08/06_423.html
ここまでのまとめ
生ADO.NETとORMの間を埋める、
Micro-ORM
dapper dot netは早くて手軽で便利
まとめ
まとめ(1)
• .NETのデータアクセスはADO.NETが基本
• ORMは便利だが使いにくいケースもある
• 間を埋める選択してのMicro-ORM
まとめ(2)
• Micro-ORMは大体こんなもの
– SQLは自力で書く
– 実行結果やパラメータをマップしてくれる
• dapper dot net
– 多分もっともメジャーなMicro-ORM
– 手軽で便利、(割と)かゆいところに手が届く
最後に
• 「ORMが適用しにくい」と思ったら無理
に適用しなくていいです
– 無理に使っても余計な苦労を背負い込む事が
多い気がします
最後に
• 生ADO.NETよりも
「もう少し文化的にやりたい」
とき、dapperはお勧めのソリューション
だと思います
• Web Pages(WeMatrix)にも良いかも
– WebMatrix.Dataに物足りなくなったときの
次の一手として
参考
dapper公式関連
• 公式サイト
– https://code.google.com/p/dapper-dot-net/
• ベンチマーク結果あり
• ソースコード
– https://github.com/SamSaffron/dapper-dot-
net
• テストコード(サンプルとしても有用)あり
– https://github.com/SamSaffron/dapper-dot-
net/blob/master/Tests/Tests.cs
• Test内に、ベンチマークのプログラムもあり
その他
• Micro ORMs with Sam Saffron and Rob
Conery
– http://www.infoq.com/articles/ORM-
Saffron-Conery
– MassiveのRob ConeryとdapperのSam
SaffronへのMicro-ORMについてのインタ
ビュー記事

ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた