ささいなことですが。

Windowsアプリテスト自動化ライブラリFriendly開発者の日記です。

LambdicSql - EntityFrameworkと仲良くなりました。

EntityFrameworkを使っているときでも、たまにSQLを直に書きたくなる場合がありますよね。そんな時はLambdicSqlを使うと便利です。そして、EntityFrameworkを使っている環境からLambdicSqlを使いやすい工夫を入れました。しかもそれでいてLambdicSql自体はDLL的にEntityFrameworkに依存したりはしていません。

DataContextクラスを指定できるようにしました。

EntityFrameworkでDBからモデルクラスを生成すると、こんな感じのものが生成されます。(若干省略してます)

public partial class tbl_remuneration
{
    public int? staff_id { get; set; }
    public string payment_date { get; set; }
    public int id { get; set; }
    public decimal? money { get; set; }
}

public partial class tbl_staff
{
    public int id { get; set; }
    public string name { get; set; }
}

public partial class ModelLambdicSqlTestDB : DbContext
{
    public virtual DbSet<tbl_remuneration> tbl_remuneration { get; set; }
    public virtual DbSet<tbl_staff> tbl_staff { get; set; }
}

これはLambdicSqlの定義に似ています。これをそのまま使えるようにしました。

public void TestEFAndLambdic()
{
    var query = Db<DB>.Sql(db =>
        Select(new
        {
            name = db.tbl_staff.T().name,
            payment_date = db.tbl_remuneration.T().payment_date,
            money = db.tbl_remuneration.T().money,
        }).
        From(db.tbl_remuneration.T()).
            Join(db.tbl_staff, db.tbl_remuneration.T().staff_id == db.tbl_staff.T().id);

    var datas = _connection.Query<SelectData1>(query).ToList();
}

T()がポイントですね。DbSet<>をスルーするものです。次のようなSQLになります。

SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	tbl_remuneration.money AS "money"
FROM  tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)

上記の例では素直にEFで書けばよいのですが、例えばOR結合を条件によって組み立てるときとか、Window関数を使うときとか、EFでは書きづらいもので利用してもらえばよいと思います。

//ORを任意の条件で組み立てるときとか
var exp = Sql<DB>.Create(db =>
    Condition(minCondition, 3000 < db.tbl_remuneration.T().money) ||
    Condition(maxCondition, db.tbl_remuneration.T().money < 4000));
var query1 = Sql<DB>.Create(db => SelectFrom(db.tbl_remuneration.T()).Where(exp));

//Window関数を使いたいときとか
var query2 = Db<DB>.Sql(db =>
    Select(new SelectData()
    {
        Average = Window.Avg(db.tbl_remuneration.T().money).
                Over<decimal>(null,
                    new OrderBy(new Asc(db.tbl_remuneration.T().payment_date)),
                    null)
    }).
    From(db.tbl_remuneration.T()));

名前解決のルールは若干違うので気を付けてください。

LambdicSqlのルールは、こちらです。EntityFrameworkとはテーブル名のルールが違っていますね。EFの場合はクラス名とテーブル名を対応させるルールで、しかも複数形と単数形の解決とか面倒なことをやってますね。とは言え、普通に生成するとEFも変数名とテーブル名は一致します。TableAttributeで指定している場合はLambdicSqlも同じルールで解決するので問題ないですね。問題ある命名している場合は、すみませんがテーブル定義を並べるクラスだけ別途作成お願いします。

是非EntityFrameworkユーザーの方もご利用お願いします!

LambdicSqlはメインでも使えて、脇役に回ってもいい仕事をするライブラリを目指しております。

LambdicSql - TableAttributeとColumnAttributeに対応しました。

LambdicSql_α0.0.53をリリースしました。
www.nuget.org

テーブル名とカラム名のルール

LambdicSqlのテーブル名とカラム名は、通常は変数名で表します。

public class Staff
{
    //変数名がカラム名になる
    public int id { get; set; }
    public string name { get; set; }
}
public class Remuneration
{
    public int id { get; set; }
    public int staff_id { get; set; }
    public DateTime payment_date { get; set; }
    public decimal money { get; set; }
}
public class DB
{
    //テーブル名も変数名で表す
    public Staff tbl_staff { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}

スキーマを使う場合も変数名で表します。(もちろんスキーマを書く必要がない場合はこれは省略できます)

public class DBO
{
    public Staff tbl_staff { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}
public class DB_EX
{
    //スキーマも変数名で表す
    public DBO dbo { get; set;}
}
//スキーマまで書きたい場合
var query = Sql<DB_EX>.Create(db =>
    Select(new
    {
        Name = db.dbo.tbl_staff.name,
        PaymentDate = db.dbo.tbl_remuneration.payment_date,
        Money = db.dbo.tbl_remuneration.money,
    }).
    From(db.dbo.tbl_remuneration).
        Join(db.dbo.tbl_staff, db.dbo.tbl_staff.id == db.dbo.tbl_remuneration.staff_id));

var info = query.ToSqlInfo(_connection.GetType());
Debug.Print(info.SqlText);
SELECT
	dbo.tbl_staff.name AS "Name",
	dbo.tbl_remuneration.payment_date AS "PaymentDate",
	dbo.tbl_remuneration.money AS "Money"
FROM dbo.tbl_remuneration
	JOIN dbo.tbl_staff ON (dbo.tbl_staff.id) = (dbo.tbl_remuneration.staff_id)

TableAttributeとColumnAttributeに対応しました。

それに加えてTableAttribute、ColumnAttributeでも名前を指定できるようにしました。
TableAttribute、ColumnAttributeは System.ComponentModel.DataAnnotations.dll に定義されている標準のものです。
テーブル名とか変数名をラムダ中で短く書きたい場合や、スキーマを一段クラスで表現するのが面倒な場合、それからテーブルやカラムの名前がC#の変数名で使えないものだったりする場合に利用できると思います。

//テーブル名をクラス定義時に指定できる
[Table("tbl_staff")]
public class StaffX
{
    //属性の方が優先される
    [Column("id")]
    public int idx { get; set; }
    [Column("name")]
    public string namex { get; set; }
}
public class DB
{
    //StaffXは属性でテーブル名を持っているのでそちらが優先される
    public StaffX xxx { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}
var query = Db<DB>.Sql(db =>
    Select(new
    {
        name = db.xxx.namex,
        payment_date = db.tbl_remuneration.payment_date,
        money = db.tbl_remuneration.money,
    }).
    From(db.tbl_remuneration).
        Join(db.xxx, db.tbl_remuneration.staff_id == db.xxx.idx);

var info = query.Build(_connection.GetType());
Debug.Print(info.SqlText);
SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	tbl_remuneration.money AS "money"
FROM tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)

LambdicSql - α51 その他

パラメータ名称

@p_0って感じで連番にしていました。折角変数名を使っているのだから、それを変数名に使った方が良いとの意見をいただそのようにしました。

public void ParamName()
{
    var min = 3000;
    var max = 4000;

    var query = Db<DB>.Sql(db =>
        Select(new
        {
            money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < max));

    var info = query.Build(cnn.GetType());
    Debug.Print(info.SqlText);
}
SELECT
	tbl_remuneration.money AS "money"
FROM tbl_remuneration
WHERE ((@min) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@max))

ただ、一つの関数内で完結していれば、問題ないのですが、LambdicSqlは式を組み合わせることができるので、変数名も重複する可能性があります。
その場合は、_がついていくことになります。

public void ParamName2()
{
    var min = 3000;
    var max = 4000;

    var query = Db<DB>.Sql(db =>
        Select(new
        {
            money = MoneyExp(5, 10).Cast<decimal>(),
        }).
        From(db.tbl_remuneration).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < max));

    var info = query.Build(cnn.GetType());
    Debug.Print(info.SqlText);
}
public ISqlExpression<decimal> MoneyExp(int min, int max)
    => Sql<Data>.Create(db => db.tbl_remuneration.money + min + max);
SELECT
	((tbl_remuneration.money) + (@min)) + (@max) AS "money"
FROM tbl_remuneration
WHERE ((@min_) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@max_))

Window関数対応

Window関数に対応しました。こんな感じで使えます。

public void TestWindow()
{
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Avg = Window.Avg(db.tbl_remuneration.money).
                    Over<decimal>(null,
                        new OrderBy(new Asc(db.tbl_remuneration.payment_date)),
                        null),
            PaymentDate = db.tbl_remuneration.payment_date
        }).
        From(db.tbl_remuneration));

    //to string and params.
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //dapper
    var datas = _connection.Query<SelectData>(info.SqlText, info.Parameters).ToList();
}
SELECT
	AVG(tbl_remuneration.money)OVER(
	ORDER BY
		tbl_remuneration.payment_date ASC) AS "Avg",
	tbl_remuneration.payment_date AS "PaymentDate"
FROM tbl_remuneration

一気に書いても良いのですが、以下のように分けて書くことも可能です。

var avg = Db<DB>.Sql(db => Window.Avg(db.tbl_remuneration.money).
                Over<decimal>(null,
                    new OrderBy(new Asc(db.tbl_remuneration.payment_date)),
                    null));
//make sql.
var query = Db<DB>.Sql(db =>
    Select(new SelectData()
    {
        Avg = avg,
        PaymentDate = db.tbl_remuneration.payment_date
    }).
    From(db.tbl_remuneration));

それでWindow関数の書式なのですが、以下のようなものになっています。Window関数はいくつかは普通の集約関数と同名ですので、using staticはしない方が良いですね。

var avg = Db<DB>.Sql(db =>
        //関数
        Window.Avg(db.tbl_remuneration.money).
        //不要な部分はnullを入れる
        Over<decimal>(
            //Partitionを指定 複数指定可能
            new PartitionBy(db.tbl_staff.name, db.tbl_remuneration.payment_date),
            //OrderByを指定 複数指定可能
            new OrderBy(new Asc(db.tbl_remuneration.money), new Desc(db.tbl_remuneration.payment_date)),
            //Rowsを指定 引数は一つの場合はPRECEDINGのみの指定となる
            new Rows(1, 5)));

LambdicSql - 文字列連携、そして2WaySQL -

LambdicSqlはLambdaでSQLを表現することを目的にしています。でも、やっぱり文字列も使えた方が安心感がありますよね。てわけで文字列を埋め込める機能を追加しました。

文字列を式に変換

こんな感じで文字列を途中に埋めれます。ただ文字列を入れるだけではなく、そこにラムダで表現した情報を入れれるのです。

public void TestFormatText()
{
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            name = db.tbl_staff.name,
            payment_date = db.tbl_remuneration.payment_date,
            //好きな文字列を入れることができる
            money = Text<decimal>("{0} + {1}", db.tbl_remuneration.money, 1000),
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id).
        Where(3000 < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    //to string and params.
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //dapper
    var datas = _connection.Query<SelectData1>(query).ToList();
}
SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	/*↓こんな感じで合成される*/
	tbl_remuneration.money + @p_0 AS "money"
FROM tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)
WHERE ((@p_1) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@p_2))

2WaySQL

これは総監督から強い要望があった機能です。2WaySQLという考え方があるようです。Javaの界隈ではDomaというライブラリが有名なようです。

  1. テキストでベースのSQLを書く
  2. 動的に変えたいところにコメントで印をつける
  3. プログラムで動的に置換

ポイントは2.の時点でコメントで印をつけただけなので、そのままDBMSで実行することができるという点です。基本のSQLだけでも確認取れてた方が安心感が増しますよね。LambdicSqlではシンプルに/*no*/~/**/の間を置き換えるというルールにしました。

public void TestFormat2WaySql(bool minCondition, bool maxCondition, decimal bonus)
{
    var sql = @"
SELECT
tbl_staff.name AS name,
tbl_remuneration.payment_date AS payment_date,
tbl_remuneration.money + /*0*/1000/**/ AS money
FROM tbl_remuneration 
JOIN tbl_staff ON tbl_staff.id = tbl_remuneration.staff_id
/*1*/WHERE tbl_remuneration.money = 100/**/";

    var query = Db<DB>.Sql(db => TwoWaySql(sql,
        bonus,
        Where(
            Condition(minCondition, 3000 < db.tbl_remuneration.money) &&
            Condition(maxCondition, db.tbl_remuneration.money < 4000))
        ));
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //Dapperを使っているなら以下で実行できます。
    //型はこの時点で指定してください
    var datas = _connection.Query<SelectedData>(query).ToList();
}
SELECT
    tbl_staff.name AS name,
    tbl_remuneration.payment_date AS payment_date,
    /*ここは置き換えられた*/
    tbl_remuneration.money + @bonus AS money
FROM tbl_remuneration 
    JOIN tbl_staff ON tbl_staff.id = tbl_remuneration.staff_id
/*ここも置き換えられた*/
/*最小の条件はfalseだったので消えた*/
/*条件がなくなった場合はWhereも消えます*/
WHERE (tbl_remuneration.money) < (@p_0)

Where句とかを動的に作って差し込めるのは便利ですね。まあ、好みで使い分けていただけたらと思います。それに、場合によってはラムダで書けないみたいなケースも出てきそうですし、そんな時にテキストも使えると心強いですよね!

履歴

2016/09/02 β版対応

LambdicSql - 断捨離 O/Rマッパー機能 捨てました。-

LambdicSqlもSQLを実行して、その結果をオブジェクトにマッピングする機能がありました。Dapperよりも便利なところもあって、匿名クラスにマッピングすることもできてました。ではなぜやめたか。
※Dapperでも匿名クラスにマッピングでる場合もあります。でも、失敗するケースもあり。DateTimeが入っていると大抵失敗する。詳細は追っていません。

//便利なとこもあるけど、この機能は捨てた
public void Test()
{
    //以前は匿名クラスにマッピングすることも可能だった。
    var query = Db<DB>.Sql<DB>().
    Select(db => new
    {
        name = db.tbl_staff.name,
        payment_date = db.tbl_remuneration.payment_date,
        money = db.tbl_remuneration.money,
    }).
    From(db => db.tbl_remuneration).
        Join(db => db.tbl_staff, db => db.tbl_remuneration.staff_id == db.tbl_staff.id);

    //読み取り専用なはずの匿名クラスにマッピング
    var data = query.ToExecutor(new SqlConnection(sqlConnectionString)).Read();
}

特徴を際立たせる

LambdicSqlはラムダからSQLを作成するためのライブラリです。DBにはアクセスしたり、マッピングする機能ははオマケだったのです。でも両方あると「新種のO/Rマッパーね」くらいに捉えられて特徴が伝わりづらいのではないかなーと。

Dapper or LambdicSqlだとDapper使うよね。

折角つくったのだから、広く多くの人に使ってもらいたいですよね。でもこの分野はEntityFrameworkやDapperがすでにあり、そんな信頼性の高いライブラリと張り合てもなかなかねー。総監督からも、「Dapperの利用者が二択せまられたら、Dapper使うよねー」とのご意見をいただきました。それはそうですね。

LambdicSql feat.Dapper

Hey yo yo yeah!オレたちDapperマジリスペクト!
ってわけでフィーチャリングします。実際DapperとLambdicSqlでは注力している部分が違うのです。LambdicSqlでSQLを組み立てて、実行はDapperでやってもらえばよいかなーって思います。ということでサンプルコードです。

public void TestStandard()
{
    var min = 3000;

    //LambdicSqlのお仕事
    //SQLを組み立てる
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Name = db.tbl_staff.name,
            PaymentDate = db.tbl_remuneration.payment_date,
            Money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_staff.id == db.tbl_remuneration.staff_id).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    //テキストとパラメータに落とす
    var info = Db<DB>.Sql(_connection.GetType());
    Debug.Print(info.SqlText);

    //ここから先はDapperにお任せ
    //パラメータはDictionary<string, object>で渡す
    var datas = _connection.Query<SelectData1>(query).ToList();
}

というわけでDapper使いの皆さま、合わせてLambdicSqlのご利用もお願いします!

LambdicSql - 生まれ変わりました -

※注) 以前のブログもβ版に合わせてコードを修正しているので、前のブログ記事と比べても変化は感じられません。

LambdicSql_α0.0.51 をリリースしました。コードのほとんどを書き直すほどの破壊的大変更を入れました。まさにRebornなのです!
www.nuget.org
変更内容は盛りだくさんです。
先日、総監督と打ち合わせして、その内容をかなり取り込みました。ボリューミーなので何回かに分けて書きます。手っ取り早く全貌を見たい方は、こちらのページを参照お願いします。
github.com

変更一覧

  1. 書き心地100%UP
  2. 断捨離
  3. 文字列との組み合わせ
  4. 2WaySQL
  5. パラメータ名称
  6. Window関数対応

今回は1.を書きます。

書き心地 100%Up(当社比)

大きくはこれです。以前比べると、かなり書きやすく、そして読みやすくなりました。

using Dapper;
using LambdicSql;
//★重要 using static.
using static LambdicSql.Keywords;
using static LambdicSql.Funcs;
using static LambdicSql.Utils;

public void TestStandard()
{
    //ラムダでSQLを作成
    var query = Db<DB>.Sql(db =>
    Select(new SelectData
    {
        Name = db.tbl_staff.name,
        Count = Count(db.tbl_remuneration.money),
        Total = Sum(db.tbl_remuneration.money),
        Average = Avg(db.tbl_remuneration.money),
        Minimum = Min(db.tbl_remuneration.money),
        Maximum = Max(db.tbl_remuneration.money),
    }).
    From(db.tbl_remuneration).
        Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id).
    GroupBy(db.tbl_staff.id, db.tbl_staff.name));

    //文字列とパラメータに変更
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);
}
public void TestCase()
{
   //case文がこんなにすっきり書ける
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Type = Case().
                      When(db.tbl_staff.id == 3).Then("x").
                      When(db.tbl_staff.id == 4).Then("y").
                      Else("z").
                   End()
        }).
        From(db.tbl_staff));
}

Sql.Create()でまとめて書けるようになった

以前は句ごとに分かれていました。そのため、毎回 db=> て書く必要がありました。それが今回の変更で一つのExpressionとして表現するようになったので、db=>は最初の一回でよくなりました。

using staticを使いやすく変更

それから、これはVisualStudio2015を使ってないと無理なのですが(そろそろ、みんな使ってますよね?)メソッドをstaticにすることによって、using staticを使って唐突にメソッドを使えるようにしました。SelectとかSumとか唐突に呼べるようになって、さらにSQLっぽくなったのではないでしょうか。残念ながら、2013までの人はメソッドの前にクラス名を書く必要があります。

組み合わせ自由自在

以前までもできましたが、さらに自由になりました。暗黙の変換の導入でキャストの手間が減りました。まあ、キャストはコンパイルを通すためだけなので。

public void TestSqlExpression()
{
    //式単位で扱うときも統一的に扱える
    var expMoneyAdd = Db<DB>.Sql(db => db.tbl_remuneration.money + 100);
    var expWhereMin = Db<DB>.Sql(db => 3000 < db.tbl_remuneration.money);
    var expWhereMax = Db<DB>.Sql(db => db.tbl_remuneration.money < 4000);

    //式の型を覚えていて、暗黙に変換される
    //上手く合わないときだけCast<>()メソッドを使う    
    var query = Db<DB>.Sql(db =>
        Select(new SelectData1()
        {
            Name = db.tbl_staff.name,
            PaymentDate = db.tbl_remuneration.payment_date,
            //decimalに変換される
            Money = expMoneyAdd,
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id).
        //boolに変換され、それらを&&や||で演算できる
        Where(expWhereMin && expWhereMax).
        OrderBy(new Asc(db.tbl_staff.name)));
}

条件式作成も書きやすく、直感的に

public void TestWhereEx(bool minCondition, bool maxCondition)
{
    //Conditionメソッドの第一引数は、その条件が有効か否かを設定    
    //無効なら消える
    //||演算も当然できるし、()を使った複雑な式にも対応
    var exp = Sql<DB>.Create(db =>
        Condition(minCondition, 3000 < db.tbl_remuneration.money) &&
        Condition(maxCondition, db.tbl_remuneration.money < 4000));

    //条件がなくなったらWhereは消えます
    var query = Sql<DB>.Create(db => SelectFrom(db.tbl_remuneration).Where(exp));
}

lambdicSql - Queryの自由な組み立て - ③サブクエリ -

サブクエリは前からある機能ですが、おさらいで。
これも、クエリを先に作っておいて、Castで文中に入れ込みます。入れたところで展開されます。

Select句

public void SelectSubQuery()
{
    //moneyの全合計
    var sub = Db<DB>.Sql(db=>
        Select(new
        {
            total = Sum(db.tbl_remuneration.money)
        }).
        From(db.tbl_remuneration));

    //スタッフごとにグルーピングされたmoneyと全体の合計を同時に表示する
    var query = Db<DB>.Sql(db =>
        Select(new
        {
            name = db.tbl_staff.name,
            personalTotal = Sum(db.tbl_remuneration.money),
            total = sub.Cast<decimal>()//★サブクエリ 代入したかのようなコードが書ける
        }).
        From(db.tbl_staff).
        Join(db.tbl_remuneration, db.tbl_remuneration.staff_id == db.tbl_staff.id).
        GroupBy(db.tbl_remuneration.staff_id, db.tbl_staff.name));

    //文字列化
    Debug.Print(query.Build(typeof(SqlConnection)).Text);

    //Dapperを使っているなら、以下のように実行できます
    var datas = _connection.Query(query).ToList();
}
SELECT
tbl_staff.name AS name,
	SUM(tbl_remuneration.money) AS personalTotal,
	(SELECT
	SUM(tbl_remuneration.money) AS total
	FROM tbl_remuneration) AS total
FROM tbl_staff
	JOIN tbl_remuneration ON (tbl_remuneration.staff_id) = (tbl_staff.id)
GROUP BY tbl_remuneration.staff_id, tbl_staff.name

Where句

public void WhereInSubQuery()
{
    //サブクエリ作成
    var sub = Db<DB>.Sql(db =>
        Select(new
        {
            money = db.tbl_remuneration.money
        }).
        From(db.tbl_remuneration).
        Join(db.tbl_staff, db => db.tbl_remuneration.staff_id == db.tbl_staff.id).
        Where(3000 < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    //Inにサブクエリを入れる
    var query = Sql<Data>.Create(db =>
    Select(new Asterisk()).
    From(db.tbl_remuneration).

    //条件指定
    Where(In(db.tbl_remuneration.money, sub.Cast<int>()));

    //文字列化
    Debug.Print(query.Build(typeof(SqlConnection)).Text);

    //Dapperを使っているなら、以下のように実行できます
    var datas = _connection.Query(query).ToList();
}
SELECT *
FROM tbl_remuneration
WHERE tbl_remuneration.money IN(
	(SELECT
	tbl_remuneration.money AS money
	FROM tbl_remuneration
		JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)
	WHERE ((@p_0) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@p_1))))

From句

From句で使うときは少しコツがあって、DB定義の部分で使っておきます。ここに入ってないと、続くクエリ構築で使えないのです。その後、From句で(JoinでもOK)で使います。

public void FromSubQuery()
{
    //サブクエリ作成
    var sub = Db<DB>.Sql(db =>
        Select(new
        {
            name = db.tbl_staff.name,
            payment_date = db.tbl_remuneration.payment_date,
            money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id).
        Where(3000 < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    var query = Db<DB>.Sql(db =>
        Select(new
        {
            name = sub.Body.name
        }).
        //From句でサブクエリのテーブル(的なもの)を指定
        //View的な使い方
        From(sub));

    //文字列化
    Debug.Print(query.Build(typeof(SqlConnection)).Text);

    //Dapperを使っているなら、以下のように実行できます
    var datas = _connection.Query(query).ToList();
}
SELECT
sub.name AS name
FROM 
	(SELECT
	tbl_staff.name AS name,
		tbl_remuneration.payment_date AS payment_date,
		tbl_remuneration.money AS money
	FROM tbl_remuneration
		JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)
	WHERE ((@p_2) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@p_3))) sub

履歴

2016/09/02 β版対応