String interpolation を使えるようにしました!
github.com
きっかけは
TLにあったneueさんのツイート
String interpolation/FormattableStringを使ったSQL整形、最高に良いアイディア。うちでも展開していきたい。 / “Announcing EF Core 2.0 Preview 2 | .NE…” https://t.co/FjpytbLQKu
— neuecc (@neuecc) 2017年7月13日
どうやら、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(); }
出力はこうなります。
なんと式を埋め込むことも可能です!
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(); } } }