Upload
narami-kiyokura
View
11.834
Download
7
Embed Size (px)
DESCRIPTION
2013/10/12 .NET 勉強会 / ヒーロー島 『ヒーロー島 秋の収穫祭 2013』の同名のセッションの資料です サンプルソースやフォローアップなどはこちら: http://d.hatena.ne.jp/kiyokura/20131014/p1
Citation preview
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についてのインタビュー記事