2015年3月25日

IQueryable はタイプセーフではない

Entity Framework (EF) 使いにとっては常識かもしれないが、IQueryable は残念ながらタイプセーフではない。
より正確に言うと、式木の構築はタイプセーフだが、IQueryable.Provider.Execute による式木の実行はタイプセーフでなくなる。

なぜなら、IQueryProvider.Execute の実装が、すべてのメソッドに対応しているわけではないため。

例えば EF の場合、IQueryProvider.Execute は式木から SQL を構築して DB に投げるが、式木内に SQL 変換が考慮されてないメソッドがあった場合、エラーを発生させるしかない。
特に自作メソッドについては対応しようがないため、すべてエラーとなる。

[EF のエラー発生サンプル]

// テーブル定義
public partial class Customer
{
    public string Id { get; set; }
    public string Name { get; set; }
}

// 実行処理 (クラス内)
public static void Sample()
{
    using( var context = new SampleEntities() ){
        var query =
            from item in context.Customer
            select item.Name.ToUpperInvariant();
        // ToUpperInvariant() は LINQ to Entities 未対応
        // ToUpper() は対応してる (EF 5.0 で確認)

        foreach( var item in query ) Console.WriteLine(item);
        // System.NotSupportedException 発生
    }
}

例外的に、Queryable.AsQueryable から生成できる EnumerableQuery は、ほぼタイプセーフになっている。
これの IQueryProvider.Execute の実装は、式木内の Queryable クラスのメソッドを Enumerable クラスの同名のメソッドに置換した後、コンパイルしてラムダ式を作成し、そのまま実行している。
つまり、LINQ to Objects に変換して実行するだけなので、自作メソッドでも何でも使用可能。

参考情報

LINQ to Entities で使用可能なメソッドについては、下記参照。
上記にないものについては、EntityFunctions や SqlFunctions (SQL Server 専用) を使用する。

LINQ to Entities で自作メソッドを SQL にマップする方法も一応ある。
LINQ to Entities が実際に実行できるかを確認したい場合は、LINQPad がオススメ。
というか競合してるアプリが他にない・・・

0 件のコメント:

コメントを投稿