くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

2017年07月

JQuery Autocompleteのあれこれ

jQuery UIのオートコンプリートを使ってて何個かメモ。

候補の数がそんなに無い時に、入力してなくても候補のリストを表示したい場合。

$("#hogefuga").autocomplete({ source: ["test","tamesi"], minLength: 0 });

minLengthを0にしておくと、入力しなくても↓キー押せば候補が出てくる。

途中で候補の中身を変えたい場合。

$("#hogefuga").autocomplete("option", "source", ["kawari","henkou"]);

候補を選択された時のイベントで処理したい場合

$("#targetdom").autocomplete({
    source: [],
    minLength: 0,
    //”値が変更されて”フォーカス外れたときに発火する
    change: function (e, u) {
        var cval = $(this).val();
        console.log(cval)
    },
});

↑はテキストボックス内の値が変更されてない場合は発火しない。

C# ラムダ式を動的に作る

DBとのやり取りをEF使って、Linqとかラムダ式でやってる場合、 固定的な奴はいいんだけど、やっぱ動的な条件付けが必要になることがある。

SQLを直で書いてもいいと思うんだけど、せっかくなのでラムダ式も 汎用的にやりたい。いきなり書くとわけわかんないコードになるので、 汎用的にやるために必要な部分をテスト的に書いたものが↓。

いちをこんな感じのある程度複雑なクエリを投げる想定

select * from 台帳
where 
(
	(
	顧客名 = 'お客さん1'
	OR
	顧客名 = 'お客さん2'
	)
	AND 
	(日付 >= '2016-10-01')
)
OR
(
	(
		業務 in ('業務1','業務2','業務3')
		AND
		媒体 = 'メール'
	)
	AND 
	(日付 >= '2016-10-01')
)

で、ソース。

using System.Linq.Expressions;
using System.Reflection;

public List<HogeTable> wkTest()
{
    var predList1 = new List<Expression>();
    var predList2 = new List<Expression>();
    var predList3 = new List<Expression>();
    var param = Expression.Parameter(typeof(HogeTable), "p");

    var left1 = Expression.PropertyOrField(param, "顧客名");
    var left2 = Expression.PropertyOrField(param, "業務");
    var left3 = Expression.PropertyOrField(param, "媒体");
    var left4 = Expression.PropertyOrField(param, "日付");

    //in句用のList
    List wkConstList = new List<string>();
    wkConstList.Add("業務1");
    wkConstList.Add("業務2");
    wkConstList.Add("業務3");

    //条件用の値準備
    var right1 = Expression.Constant("お客さん1");
    var right2 = Expression.Constant("お客さん2");
    var right3 = Expression.Constant("メール");
    var right4 = Expression.Constant("2016-10-01");
    var right5 = Expression.Constant(wkConstList, typeof(List<string>));

    predList1.Add(Expression.Equal(left1, right1));
    predList1.Add(Expression.Equal(left1, right2));
    
    //IN句の代用
    MethodInfo Contains = typeof(List<string>).GetMethod("Contains");
    predList2.Add(Expression.Call(right5, Contains, left2));
    predList2.Add(Expression.Equal(left3, right3));
    //文字列の大小比較
    predList3.Add(Expression.GreaterThanOrEqual(Expression.Call(typeof(string), "Compare", null,left4, right4 ), Expression.Constant(0)));

    var tmpbody1 = predList1.Aggregate((l, r) => Expression.MakeBinary(ExpressionType.OrElse, l, r));
    var tmpbody2 = predList2.Aggregate((l, r) => Expression.MakeBinary(ExpressionType.AndAlso, l, r));
    var tmpbody3 = predList3.Aggregate((l, r) => Expression.MakeBinary(ExpressionType.AndAlso, l, r));

    var body1 = Expression.MakeBinary(ExpressionType.AndAlso, tmpbody2, tmpbody3);
    var body2 = Expression.MakeBinary(ExpressionType.AndAlso, tmpbody1, tmpbody3);
    var body3 = Expression.MakeBinary(ExpressionType.OrElse, body2, body1);

    IEnumerable<HogeTable> wkResult;
    wkResult = HogeTables.AsQueryable().Where(Expression.Lambda<Func<HogeTable, bool>>(body3, param));

    return wkResult.ToList();
}

いちを、全部、Expressionで動的に定義してるので、あとは一工夫してあげれば出来上がりという寸法です。

個人的に条件文のネストといいますか、()の深い構造をどう作るのかな~っていうのを試したかった。 あと、Expression使う場合に文字列の大小比較と、IN句に対応する「List.Contains(x.列名)」的な書き方がいけるのか確認しておきたかったのでそれも。。。

上記のソースで発行されるクエリは↓の条件だった。

WHERE 
(
  ([Extent1].[顧客名] IN (N'お客さん1',N'お客さん2')) 
  AND 
  ([Extent1].[日付] >= N'2016-10-01')
) 
OR 
(
  ([Extent1].[業務] IN (N'業務1', N'業務2', N'業務3')) 
  AND
  ([Extent1].[業務] IS NOT NULL) 
  AND
  (N'メール' = [Extent1].[媒体]) 
  AND
  ([Extent1].[日付] >= N'2016-10-01')
)

おおむね、想定通りのクエリ。文字列の大小比較とかいけるか怪しい気がしたけどちゃんと行けてるの。あとOrで繋げた部分も勝手に判断してin句に変えてくれてる。賢い。ただ、「is not null」がじゃっかん気になるところ。。。

で、最初はBlock使わないと条件文のネストしてくれないかと思ってたんだけど、順番つけて普通にAndAlsoとかOrElseで繋げれば勝手にやってくれるっぽ。てか、DB宛てじゃないモデルの場合はBlock使えるんだけど、テーブルが裏にいるEFのモデルだとBlock使わせてくれないのかしら。何かエラーになる。あまし調べてない。。。

あー、Linqの書き方の場合でも上記のExpressionそのままWhereに突っ込めば動く。

問合せ