Hokuriku.NET × JAZUG信州 -北陸・信州合同勉強会-
DeclarativeSql
- 属性ベーステーブルマッピングとSQLの自動生成 -
Name
鈴木 孝明
Twitter Account
@xin9le
Award
Microsoft MVP for .NET
Web Site
http://xin9le.net
About Me
ライブラリ作成のモチベーション
Why declarative ?
Dapper
軽量で超高速なO/R Mapper
POCOへのマッピングのみを請け負うので、SQLは完全直書き
Entity Framework
LINQによるDBアクセスからO/Rマッピングまでフルサポート
SQLの直書きはないが、パッと使うには仰々しい感
Dapper vs Entity Framework
昨今のチーム事情
けど
定型SQLは
楽したい
チーム全員
SQL星人
でも
LINQもEFも
できない
ってことは
Dapper一択
そうだ、型情報からSQL作ろう
update Person
set
Age = @Age
where Name = @Name
and Sex = @Sex
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Sex { get; set; }
}
こんな感じになったら嬉しい
簡易な定型文のみをサポート
「それって何てEntity Framework?」にならないように
複雑なクエリが必要なときはSQL星人たちの神業に頼ればよい
自由度とメンテナンス性の両立
SQLの自動生成 = いくらかバグの混入を防ぐことができる
機能を求め過ぎて中途半端になるくらいならSQLの直書きも許容
求め過ぎないという割り切り
https://github.com/xin9le/DeclarativeSql
It’s Free & Open Source
PM> Install-Package DeclarativeSql.Core
PM> Install-Package DeclarativeSql.Dapper
「ちょっとの便利」を後押しするコア機能
DeclarativeSql.Core
using (var connection = DbProvider.CreateConnection(DbKind.SqlServer, "接続文字列"))
{
connection.Open();
//--- 何かデータベース操作
}
EnumベースでのDB接続生成
対象DBを
引数で変更
可能
Oracle
MySQL
などもOK
using (var connection = DbProvider.CreateConnection(DbKind.Oracle, "接続文字列"))
using (var transaction = connection.StartTransaction())
{
//--- レコードの挿入/更新/削除など
transaction.Complete();
}
usingによるトランザクション
Transaction
Scopeと
同じ使い方
[Table("Person", Schema = "dbo")] //--- テーブル名の指定
public class Person
{
[Key] //--- 主キー
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] //--- 自動採番
public int Id { get; set; }
[Required] //--- NotNull
[Column("名前")] //--- 列名の指定
public string Name { get; set; }
[Sequence("AgeSeq", Schema = "dbo")] //--- シーケンスの利用 (Oracleなどで)
public int? Age { get; set; }
[NotMapped] //--- マッピングしない
public int Sex { get; set; }
}
属性によるマッピング型定義
Code Firstと
ほぼ同様
public sealed class TableMappingInfo
{
public Type Type { get; } //--- マッピングするクラスの型
public string Schema { get; } //--- テーブルのスキーマ名
public string Name { get; } //--- テーブル名
public IReadOnlyList<ColumnMappingInfo> Columns { get; } //--- 列マッピング情報
public static TableMappingInfo Create<T>(){ … }; //--- メタデータの取得
}
マッピングメタデータの提供 #1
キャッシュ
が効くので
高速
public sealed class ColumnMappingInfo
{
public string PropertyName { get; } //--- プロパティ名
public Type PropertyType { get; } //--- プロパティのデータ型
public string ColumnName { get; } //--- 列名
public DbType ColumnType { get; } //--- 列のデータ型
public bool IsPrimaryKey { get; } //--- 主キーかどうか
public bool IsNullable { get; } //--- NULL許可かどうか
public bool IsIdentity { get; } //--- 自動採番をするどうか
public SequenceMappingInfo Sequence { get; } //--- 設定されているシーケンス情報
}
マッピングメタデータの提供 #2
public sealed class SequenceMappingInfo
{
public string Schema { get; } //--- シーケンスのスキーマ名
public string Name { get; } //--- シーケンス名
}
マッピングメタデータの提供 #3
//--- 指定の列のみを対象として全レコード取得
var sql = PrimitiveSql.CreateSelect<Person>(x => x.Id, x => x.Name);
/*
select
Id as Id,
名前 as Name
from dbo.Person
*/
プリミティブなSQLの生成 #1
指定がない
場合は
全列が対象
//--- 指定の列のみを対象として全レコードを更新
var sql = PrimitiveSql.CreateUpdate<Person>(DbKind.SqlServer, x => x.Name);
/*
update dbo.Person
set
名前 = @Name
*/
プリミティブなSQLの生成 #2
Bind変数の
接頭辞の
決定に必要
指定がない
場合は
全列が対象
Dapperを基にした超簡単で直観的なCRUD操作
DeclarativeSql.Dapper
//--- 全件取得
var p1 = connection.Select<Person>();
//--- Id, Name 列に絞って全件取得
var p2 = connection.Select<Person>(x => x.Id, x => x.Name);
//--- Id = 3 のレコードのみ取得
var p3 = connection.Select<Person>(x => x.Id == 3);
//--- Id > 3 のレコードを Id, Name 列に絞って取得
var p4 = connection.Select<Person>(x => x.Id > 3, x => x.Id, x => x.Name);
レコードの取得
.Select<T>
//--- 指定されたデータを挿入
var p5 = connection.Insert(new Person { Name = "xin9le", Age = 30 });
//--- 複数のレコードでもOK
var p6 = connection.Insert(new []
{
new Person { Name = "yoshiki", Age= 49, },
new Person { Name = "suzuki", Age= 30, },
new Person { Name = "anders", Age= 54, },
});
レコード挿入
.Insert<T>
//--- Age = 30 のレコードの Name 列を更新
var p7 = connection.Update
(
new Person { Name = “xin9le" },
x => x.Age == 30, //--- 指定しなければ全レコード更新
x => x.Name //--- 指定がなければ全列更新
);
レコード更新
.Update<T>
//--- 全レコード削除
var p8 = connection.Delete<Person>();
//--- 年齢が30歳でないレコードを削除
var p9 = connection.Delete<Person>(x => x.Age != 30);
//--- テーブルの切り捨て
var p10 = connection.Truncate<Person>();
レコード削除
.Delete<T>
.Truncate<T>
//--- メソッド名に ‘Async’ を付けるだけ
var p1 = await connection.SelectAsync<Person>();
var p2 = await connection.InsertAsync(new Person { Name = "xin9le" });
var p3 = await connection.UpdateAsync(new Person { Name = "xin9le" });
var p4 = await connection.DeleteAsync<Person>();
もちろん非同期版も
実際に非同期処理になるかは各DBプロバイダーに依存する
Simple = Speed + Power
Dapperの強みをそのままに、select * などの決まった手間を軽減
たった1文で書けるDBアクセス
サポートしていない構文
inner join / left join / order by / group by / サブクエリー (etc.)
やり過ぎは複雑さの増大と自由度の低下を招く
シンプルに、そしてカジュアルに
Have a nice database operation!!
Thank you

DeclarativeSql