かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Azure Mobile AppsでテーブルのクエリにIN句を使いたい

ODataはin句をサポートしてないらしい?ので出来ません。(完) こんな定義らしい。

filter = '$filter' EQ boolCommonExpr

boolCommonExpr = ( isofExpr 
                 / boolMethodCallExpr 
                 / notExpr  
                 / commonExpr
                   [ eqExpr 
                   / neExpr 
                   / ltExpr  
                   / leExpr  
                   / gtExpr 
                   / geExpr 
                   / hasExpr 
                   ]
                 / boolParenExpr
                 ) [ andExpr / orExpr ]

というのではあんまりなので代替案を。 渡された配列に対して1要素ずつ比較するのをORでつないでやればOKという方法で代替できます。 ただLINQでORをやるのは苦行で式木に踏み込まないといけません。

ということで、以下のようなTodoItemクラスに対して

using System;

namespace PrismMobileApp.Models
{
    public class TodoItem
    {
        public string Id { get; set; }

        public string Text { get; set; }

        public bool Complete { get; set; }

        public byte[] Version { get; set; }

        public DateTimeOffset? CreatedAt { get; set; }

        public DateTimeOffset? UpdatedAt { get; set; }
    }
}

IMobileServiceTable<TodoItem>型のTodoItemTableがある前提で引数で渡された文字列配列に対して一致するIDを持つTodoItemを抜き出す処理を書いてみましょう。

public Task<IEnumerable<TodoItem>> GetIds(params string[] ids)
{
    // x => false or id[0] == x.Id or id[1] == x.Id ...
    var x = Expression.Parameter(typeof(TodoItem), "x");
    Expression expression = Expression.Constant(false);
    foreach (var id in ids)
    {
        expression = Expression.OrElse(
            expression,
            Expression.Equal(Expression.Property(x, nameof(TodoItem.Id)), Expression.Constant(id)));
    }

    var lambda = Expression.Lambda<Func<TodoItem, bool>>(expression, x);
    return this.TodoItemTable.CreateQuery()
        .Where(lambda)
        .OrderBy(y => y.Text)
        .ToEnumerableAsync();
}

ということでこういう感じになります。式木の初歩的な感じですね。コメントに書いてあるような式を組み立ててます。式の中身を組み立てて最後にラムダ式にしてWhereに渡すという流れです。

では、よいLINQライフを。