ささいなことですが。

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

LambdicSql - String interpolation 対応しました。

String interpolation を使えるようにしました!
github.com

きっかけは

TLにあったneueさんのツイート

どうやら、EFで以下のような書き方ができるようになったとのこと。

var city = "London";
var contactTitle = "Sales Representative";

using (var context = CreateContext())
{
    context.Customers
       .FromSql($@"
           SELECT *
           FROM Customers
           WHERE City = {city}
               AND ContactTitle = {contactTitle}")
       .ToArray();
}
@p0='London' (Size = 4000)
@p1='Sales Representative' (Size = 4000)

SELECT *
FROM Customers
WHERE City = @p0
    AND ContactTitle = @p1

えええ!?
なんで、そんなことできるの?

って思ってたら、ブログの下の方に、
FormattableString で受けたらいいんだよ。
って書いてありました。
なるほどねー。
これは、LambdicSqlにも是非取り込まねばってことでやってみました。

InterpolateSql

新たに、InterpolateSqlって関数を追加しました。

static void Sample1(IDbConnection cnn)
{
    var city = "London";
    var contactTitle = "Sales Representative";

    var sql = Db.InterpolateSql<Customers>(() =>
$@"SELECT *
FROM Customers
WHERE City = {city}
AND ContactTitle = {contactTitle}"
       );

    //変換
    var info = sql.Build(cnn.GetType());

    //文字列とパラメータをコンソールに出力
    Console.WriteLine(info.Text);
    Console.WriteLine("\r\n------Params------");
    foreach (var e in info.GetParams()) Console.WriteLine($"{e.Key} = {e.Value.Value}");

    //実行するときはこんな感じ
    var datas = cnn.Query(sql).ToList();
}

出力はこうなります。
f:id:ishikawa-tatsuya:20170715151258p:plain

なんと式を埋め込むことも可能です!

LambdicSqlはもともとExpression解析するライブラリなんで、もっと色々な情報が取れます。例えば上のでも変数名が取れてたり。なので、{}内にはLambdicSqlで使えるものは全部れることができるんです。
式を埋め込むこともOKです。

var sql = Db<DB>.InterpolateSql<Customers>(db =>
$@"SELECT *
FROM Customers
WHERE {(db.Customers.City == city && db.Customers.ContactTitle == contactTitle)}"
);
SELECT *
FROM Customers
WHERE ((Customers.City) = (@city)) AND ((Customers.ContactTitle) = (@contactTitle))

.NetFramework4.6以降で使えます。

FormattableString が4.6以降でないと使えないようですね。PCLとか.NetStandardとか今回対応できませんでした。(使えんことはないよな?、そのうち調べて対応します。)

え?文字列使わずに書けるようにするって・・・

はい。SQL全網羅に向けて頑張ってますよー。でもまだ先は長い(長すぎる)。SQLServerは半分くらいはいったかなー。ほとんどのは書けるんですけど、定義してないの使うことあったら、これ使ってください的な。引き続きメンバー募集中です!

サンプルコード全文

DapperとLambdicSql.SqlServerをNugetから入れて、接続文字書いてもらったら動きます。使ってみてくださいねー。

using System;
using System.Linq;

//LambdicSql
using LambdicSql;

//for SqlServer and Dapper.
//Of course, other connections are OK.
//OracleConnection, SQLiteConnection, NpgsqlConnection, MySqlConnection, DB2Connection
using System.Data;
using System.Data.SqlClient;
using static LambdicSql.SqlServer.Symbol;
using LambdicSql.feat.Dapper;

namespace FormattableStringSample
{
    class Program
    {
        class Customers
        {
            public string City { get; set; }
            public string ContactTitle { get; set; }
        }

        class DB
        {
            public Customers Customers { get; set; }
        }

        static void Main(string[] args)
        {
            //initialize dapper.
            DapperAdapter.Assembly = typeof(Dapper.SqlMapper).Assembly;

            //test.
            using (var cnn = new SqlConnection("your connection string")) Sample1(cnn);
            {
                Sample1(cnn);
                Sample2(cnn);
                Sample3(cnn);
            }
        }

        static void Sample1(IDbConnection cnn)
        {
            var city = "London";
            var contactTitle = "Sales Representative";

            var sql = Db.InterpolateSql<Customers>(() =>
$@"SELECT *
FROM Customers
WHERE City = {city}
    AND ContactTitle = {contactTitle}"
               );

            //to string and params.
            var info = sql.Build(cnn.GetType());

            //print.
            Console.WriteLine(info.Text);
            Console.WriteLine("\r\n------Params------");
            foreach (var e in info.GetParams()) Console.WriteLine($"{e.Key} = {e.Value.Value}");

            //execute query by dapper or sql-net-pcl.
            var datas = cnn.Query(sql).ToList();
        }

        static void Sample2(IDbConnection cnn)
        {
            var city = "London";
            var contactTitle = "Sales Representative";

            var sql = Db<DB>.InterpolateSql<Customers>(db =>
$@"SELECT *
FROM Customers
WHERE {(db.Customers.City == city && db.Customers.ContactTitle == contactTitle)}"
           );

            //to string and params.
            var info = sql.Build(cnn.GetType());

            //print.
            Console.WriteLine(info.Text);
            Console.WriteLine("\r\n------Params------");
            foreach (var e in info.GetParams()) Console.WriteLine($"{e.Key} = {e.Value.Value}");

            //execute query by dapper or sql-net-pcl.
            var datas = cnn.Query(sql).ToList();
        }

        static void Sample3(IDbConnection cnn)
        {
            var city = "London";
            var contactTitle = "Sales Representative";
            
            var sql = Db<DB>.InterpolateSql<Customers>(db =>
$@"SELECT *
FROM Customers
{Where(new Condition(!string.IsNullOrEmpty(city), db.Customers.City == city) && new Condition(!string.IsNullOrEmpty(contactTitle), db.Customers.ContactTitle == contactTitle))}"
           );

            //to string and params.
            var info = sql.Build(cnn.GetType());

            //print.
            Console.WriteLine(info.Text);
            Console.WriteLine("\r\n------Params------");
            foreach (var e in info.GetParams()) Console.WriteLine($"{e.Key} = {e.Value.Value}");

            //execute query by dapper or sql-net-pcl.
            var datas = cnn.Query(sql).ToList();
        }
    }
}